Tracking Google rankings sounds simple at first.
You have a keyword.
You search it on Google.
You check where your website appears.
That works if you only have one keyword.
But once you need to track 50, 500, or 5,000 keywords across different countries, cities, languages, or devices, manual checking stops making sense.
You need a repeatable workflow.
A simple ranking tracker usually looks like this:
Keyword list → SERP API → organic results → ranking check → CSV/report
In this tutorial, we’ll build a basic Google ranking tracker with Python and a SERP API.
We will:
- Load a list of keywords
- Send each keyword to a SERP API
- Extract Google organic results
- Check whether a target domain appears
- Save ranking data to a CSV file
This is not a full SEO platform, but it gives you the core logic behind many rank tracking tools.
Why use a SERP API?
You can try to scrape Google directly.
For a small experiment, it may work. But production scraping gets messy quickly.
Google result pages can change by:
- country
- city
- language
- device
- search intent
- query type
- result features
A single result page may include:
- organic results
- ads
- local packs
- maps
- images
- videos
- shopping results
- news results
- People Also Ask
- related searches
Then there are the operational issues:
- blocked requests
- CAPTCHA
- proxy management
- unstable HTML
- parser maintenance
- retry logic
- localization problems
If your real goal is to track rankings, you probably do not want to maintain all of that infrastructure.
A SERP API helps by returning structured search results in JSON.
Instead of parsing raw HTML, you get data like:
{
"query": "best project management software",
"organic_results": [
{
"position": 1,
"title": "Best Project Management Software Tools",
"link": "https://example.com",
"snippet": "Compare project management tools and pricing..."
}
]
}
That is much easier to work with in Python.
What we are building
Let’s say we want to track whether our target domain appears for several keywords.
Example target domain:
example.com
Example keywords:
best project management software
project management tools
task management app
team collaboration software
For each keyword, we want to collect:
- keyword
- target domain
- ranking position
- result title
- result URL
- snippet
- whether the domain was found
Example CSV output:
keyword,target_domain,found,position,title,url
best project management software,example.com,yes,3,Example Project Tool,https://example.com
task management app,example.com,no,,,
Install dependencies
We only need two Python packages:
pip install requests python-dotenv
requests is used to call the SERP API.
python-dotenv is used to load your API key from a .env file.
Create a .env file
Create a file named .env:
SERP_API_KEY=your_api_key_here
SERP_API_URL=https://your-serp-api-endpoint.example.com/search
Do not hardcode API keys directly in your script.
The exact endpoint depends on your SERP API provider. You can use providers such as SerpApi, Serper, SearchAPI, Bright Data, or Talordata. In this tutorial, we’ll use a generic request format so you can adapt it to your provider.
Create a keyword list
Create a file named keywords.txt:
best project management software
project management tools
task management app
team collaboration software
Each line is one keyword.
Basic SERP API request
Create a file named rank_tracker.py.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
SERP_API_KEY = os.getenv("SERP_API_KEY")
SERP_API_URL = os.getenv("SERP_API_URL")
def fetch_google_results(query, location="United States", language="en"):
if not SERP_API_KEY:
raise ValueError("Missing SERP_API_KEY environment variable")
if not SERP_API_URL:
raise ValueError("Missing SERP_API_URL environment variable")
params = {
"api_key": SERP_API_KEY,
"engine": "google",
"q": query,
"location": location,
"language": language,
"output": "json",
}
response = requests.get(SERP_API_URL, params=params, timeout=30)
response.raise_for_status()
return response.json()
Different providers may use different parameter names.
For example:
-
qorquery -
location,gl, orcountry -
language,hl, orlocale -
engine,search_type, ortype
Check your provider’s documentation and adjust the parameters.
Extract organic results
Most SERP APIs return organic results in a list.
Common response keys include:
organic_resultsorganicresults
Let’s write a helper function that supports several possible keys.
def get_organic_results(data):
possible_keys = [
"organic_results",
"organic",
"results",
]
for key in possible_keys:
value = data.get(key)
if isinstance(value, list):
return value
return []
Now normalize each result.
def normalize_result(item):
return {
"position": item.get("position") or item.get("rank"),
"title": item.get("title") or "",
"url": item.get("link") or item.get("url") or "",
"snippet": item.get("snippet") or item.get("description") or "",
}
This gives us a clean format no matter which provider we use.
Check whether a domain appears
Now we need to check whether the target domain appears in the organic results.
We can use Python’s urllib.parse to extract the domain from each URL.
from urllib.parse import urlparse
def extract_domain(url):
if not url:
return ""
parsed = urlparse(url)
domain = parsed.netloc.lower()
if domain.startswith("www."):
domain = domain[4:]
return domain
Now write the ranking check function:
def find_domain_ranking(results, target_domain):
target_domain = target_domain.lower().replace("www.", "")
for result in results:
result_domain = extract_domain(result.get("url", ""))
if result_domain == target_domain or result_domain.endswith("." + target_domain):
return {
"found": "yes",
"position": result.get("position"),
"title": result.get("title"),
"url": result.get("url"),
"snippet": result.get("snippet"),
}
return {
"found": "no",
"position": "",
"title": "",
"url": "",
"snippet": "",
}
This checks exact domain matches and subdomains.
For example, it can match:
example.com
blog.example.com
www.example.com
Load keywords from a file
Now let’s read keywords from keywords.txt.
def load_keywords(filename="keywords.txt"):
with open(filename, "r", encoding="utf-8") as file:
return [
line.strip()
for line in file
if line.strip()
]
Save ranking results to CSV
We can save the final ranking data to a CSV file.
import csv
def save_rankings_to_csv(rows, filename="rankings.csv"):
fieldnames = [
"keyword",
"target_domain",
"found",
"position",
"title",
"url",
"snippet",
]
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for row in rows:
writer.writerow(row)
Full script
Here is the complete version:
import os
import csv
import requests
from urllib.parse import urlparse
from dotenv import load_dotenv
load_dotenv()
SERP_API_KEY = os.getenv("SERP_API_KEY")
SERP_API_URL = os.getenv("SERP_API_URL")
def fetch_google_results(query, location="United States", language="en"):
if not SERP_API_KEY:
raise ValueError("Missing SERP_API_KEY environment variable")
if not SERP_API_URL:
raise ValueError("Missing SERP_API_URL environment variable")
params = {
"api_key": SERP_API_KEY,
"engine": "google",
"q": query,
"location": location,
"language": language,
"output": "json",
}
response = requests.get(SERP_API_URL, params=params, timeout=30)
response.raise_for_status()
return response.json()
def get_organic_results(data):
possible_keys = [
"organic_results",
"organic",
"results",
]
for key in possible_keys:
value = data.get(key)
if isinstance(value, list):
return value
return []
def normalize_result(item):
return {
"position": item.get("position") or item.get("rank"),
"title": item.get("title") or "",
"url": item.get("link") or item.get("url") or "",
"snippet": item.get("snippet") or item.get("description") or "",
}
def extract_domain(url):
if not url:
return ""
parsed = urlparse(url)
domain = parsed.netloc.lower()
if domain.startswith("www."):
domain = domain[4:]
return domain
def find_domain_ranking(results, target_domain):
target_domain = target_domain.lower().replace("www.", "")
for result in results:
result_domain = extract_domain(result.get("url", ""))
if result_domain == target_domain or result_domain.endswith("." + target_domain):
return {
"found": "yes",
"position": result.get("position"),
"title": result.get("title"),
"url": result.get("url"),
"snippet": result.get("snippet"),
}
return {
"found": "no",
"position": "",
"title": "",
"url": "",
"snippet": "",
}
def load_keywords(filename="keywords.txt"):
with open(filename, "r", encoding="utf-8") as file:
return [
line.strip()
for line in file
if line.strip()
]
def save_rankings_to_csv(rows, filename="rankings.csv"):
fieldnames = [
"keyword",
"target_domain",
"found",
"position",
"title",
"url",
"snippet",
]
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for row in rows:
writer.writerow(row)
def track_rankings(target_domain, keywords, location="United States", language="en"):
rows = []
for keyword in keywords:
print(f"Checking keyword: {keyword}")
try:
data = fetch_google_results(
query=keyword,
location=location,
language=language,
)
organic_items = get_organic_results(data)
normalized_results = [
normalize_result(item)
for item in organic_items
]
ranking = find_domain_ranking(
results=normalized_results,
target_domain=target_domain,
)
rows.append({
"keyword": keyword,
"target_domain": target_domain,
"found": ranking["found"],
"position": ranking["position"],
"title": ranking["title"],
"url": ranking["url"],
"snippet": ranking["snippet"],
})
except requests.RequestException as error:
print(f"Request failed for keyword: {keyword}")
print(error)
rows.append({
"keyword": keyword,
"target_domain": target_domain,
"found": "error",
"position": "",
"title": "",
"url": "",
"snippet": "",
})
return rows
if __name__ == "__main__":
target_domain = "example.com"
keywords = load_keywords("keywords.txt")
rankings = track_rankings(
target_domain=target_domain,
keywords=keywords,
location="United States",
language="en",
)
save_rankings_to_csv(rankings, "rankings.csv")
print("Saved ranking results to rankings.csv")
Run the script:
python rank_tracker.py
You should get a file named:
rankings.csv
Example CSV output
Your CSV may look like this:
keyword,target_domain,found,position,title,url,snippet
best project management software,example.com,yes,4,Example Project Tool,https://example.com,Manage projects and tasks...
project management tools,example.com,no,,,,
task management app,example.com,yes,7,Example Task App,https://example.com/tasks,Task management for teams...
This is the basic ranking tracker.
Track rankings by location
Google results are not the same everywhere.
A keyword may rank differently in:
United States
United Kingdom
Canada
Australia
Singapore
A local keyword may change even more by city.
For example:
best digital marketing agency
could return very different results in New York, London, and Sydney.
You can track multiple locations like this:
locations = [
"United States",
"United Kingdom",
"Singapore",
]
all_rows = []
for location in locations:
rankings = track_rankings(
target_domain="example.com",
keywords=keywords,
location=location,
language="en",
)
for row in rankings:
row["location"] = location
all_rows.append(row)
If you add location, update the CSV fieldnames:
fieldnames = [
"keyword",
"location",
"target_domain",
"found",
"position",
"title",
"url",
"snippet",
]
Location is important for:
- local SEO
- international SEO
- agency reporting
- market research
- competitor monitoring
- multi-country search analysis
Track multiple domains
You may also want to track competitors.
Example domains:
example.com
competitor-one.com
competitor-two.com
You can loop through multiple domains:
target_domains = [
"example.com",
"competitor-one.com",
"competitor-two.com",
]
all_rows = []
for domain in target_domains:
rankings = track_rankings(
target_domain=domain,
keywords=keywords,
location="United States",
language="en",
)
all_rows.extend(rankings)
save_rankings_to_csv(all_rows, "competitor_rankings.csv")
This gives you a basic competitor visibility report.
How to improve this script
This is a simple version, but you can extend it in many ways.
You could add:
- scheduled daily runs with cron
- SQLite or Postgres storage
- ranking history
- position change detection
- email alerts
- Slack notifications
- competitor domain tracking
- local city-level tracking
- dashboard visualization
- AI-generated SEO summaries
For example, once you store rankings over time, you can detect changes like:
example.com moved from position 8 to position 3 for "project management tools"
That can become the basis for a simple SEO monitoring tool.
Using ranking data with an LLM
SERP data is also useful for AI workflows.
Once you have ranking data, you can ask an LLM to summarize it.
Example prompt:
You are an SEO analyst.
Here is Google ranking data for our domain and competitors.
Summarize:
- which keywords we rank for
- which keywords we are missing
- which competitors appear most often
- what actions we should consider next
You can feed the CSV data or a cleaned summary into the prompt.
This is useful for:
- SEO copilots
- weekly ranking summaries
- competitor analysis
- content planning
- market research
- automated reporting
The key is that the LLM should reason over structured ranking data, not raw search pages.
What to check before choosing a SERP API
Before choosing a SERP API provider, test it with your actual keywords.
Do not only test easy queries.
Try:
brand keywords
commercial keywords
local keywords
long-tail keywords
competitor keywords
high-volume keywords
Then check:
- Does the API return clean JSON?
- Are organic results complete?
- Are positions stable?
- Are titles, links, and snippets available?
- Does geo-targeting work correctly?
- Can you request HTML if needed?
- Are failed requests billed?
- How much cleanup does your code need?
Providers such as SerpApi, Serper, SearchAPI, Bright Data, DataForSEO, and Talordata can all be tested for this kind of workflow.
The best choice depends on your use case.
If you only need a few simple Google searches, a lightweight API may be enough.
If you need SEO monitoring, competitor tracking, AI search workflows, or multi-engine SERP data, test the response structure more carefully.
Final thoughts
Tracking Google rankings is not only about checking where a page appears.
It is about building a repeatable search data workflow.
With Python and a SERP API, you can:
- load keywords
- collect Google results
- extract organic rankings
- check target domains
- save results to CSV
- compare rankings across locations
- monitor competitors
- feed ranking data into AI workflows
You do not need to build and maintain your own Google scraper to get started.
A SERP API gives you structured search data so you can focus on analysis, reporting, and product logic.
If you want to test this workflow, Talordata is one SERP API worth comparing. It supports structured SERP data, JSON / HTML response formats, geo-targeted results, and search workflows for SEO monitoring, AI agents, competitor tracking, and market research.
Talordata also offers 1,000 free API requests after signup, which is enough to test real keywords before choosing a provider.
Top comments (0)