Yandex Search API: How to Track Rankings in Russia and CIS Markets
Learn how to use a Yandex Search API to track keyword rankings in Russia and CIS-focused markets. Use Python to collect localized SERP data by city, country, language, and device, then export ranking results to CSV.
Quick answer: A Yandex Search API helps SEO teams collect Yandex search results programmatically, track keyword rankings by city, country, language, and device, and export the results for reporting. This is useful when your SEO work targets Russia or CIS-focused markets where Yandex visibility matters.
If your SEO reporting only tracks Google, you may miss an important part of search visibility in Russia and nearby Russian-speaking or CIS-focused markets.
Yandex rankings can differ from Google rankings. A page that performs well on Google may not have the same visibility on Yandex. For SEO teams, agencies, marketplaces, and international brands, that means Yandex should be tracked as a separate search channel.
A SERP API makes this easier. Instead of manually checking Yandex results from different locations, you can send structured requests and receive search results in a format your system can store and analyze.
What We Will Build
We will create a Python workflow:
Keyword list
→ Market and language list
→ Yandex SERP API request
→ Extract organic results
→ Find target domain ranking
→ Export ranking data to CSV
The final CSV can include:
|
Field |
Meaning |
|
|
Search query |
|
|
Target market label |
|
|
City or region |
|
|
Target country or market |
|
|
Search language |
|
|
Desktop or mobile |
|
|
Ranking position of the target domain |
|
|
Ranking URL |
|
|
Ranking page title |
|
|
Top-ranking domains |
|
|
Collection timestamp |
This setup is useful for Yandex SEO reporting, competitor monitoring, Russian-language SEO, cross-market visibility checks, and recurring rank tracking.
Why Track Yandex Rankings Separately?
Yandex should not be treated as a copy of Google.
When tracking Russia and CIS-focused SEO campaigns, you may need to compare:
|
Dimension |
Why it matters |
|
City |
Rankings may vary by local market |
|
Language |
Russian and local-language queries can return different results |
|
Device |
Mobile and desktop layouts may differ |
|
Keyword wording |
Translated keywords may not match real user phrasing |
|
Competitors |
Local competitors may be more visible on Yandex than on Google |
For example, these two queries may need separate tracking:
купить ноутбук
buy laptop
They may describe the same product category, but they represent different user behavior, language intent, and SERP competition.
Step 1: Set Environment Variables
Do not hardcode your API key.
export TALORDATA_API_KEY="your_api_key_here"
export TALORDATA_SERP_ENDPOINT="your_talordata_serp_endpoint_here"
On Windows PowerShell:
$env:TALORDATA_API_KEY="your_api_key_here"
$env:TALORDATA_SERP_ENDPOINT="your_talordata_serp_endpoint_here"
Install the Python package:
pip install requests
View detailed API documentation>>
Step 2: Define Keywords and Target Markets
Start with a small test set.
KEYWORDS = [
"купить ноутбук",
"доставка цветов",
"онлайн курсы английского",
]
MARKETS = [
{
"market": "Russia - Moscow",
"location": "Moscow, Russia",
"country": "ru",
"language": "ru",
},
{
"market": "Kazakhstan - Almaty",
"location": "Almaty, Kazakhstan",
"country": "kz",
"language": "ru",
},
{
"market": "Belarus - Minsk",
"location": "Minsk, Belarus",
"country": "by",
"language": "ru",
},
]
TARGET_DOMAIN = "example.com"
DEVICE = "desktop"
For production tracking, use real keyword groups, target locations, and language settings based on your SEO strategy.
Step 3: Create a SERP API Request Helper
Keep the request layer separate from parsing logic.
import os
import requests
def require_env(name):
value = os.getenv(name)
if not value:
raise RuntimeError(f"Missing required environment variable: {name}")
return value
TALORDATA_API_KEY = require_env("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = require_env("TALORDATA_SERP_ENDPOINT")
def call_serp_api(payload):
headers = {
"Authorization": f"Bearer {TALORDATA_API_KEY}",
"Content-Type": "application/json",
}
response = requests.post(
TALORDATA_SERP_ENDPOINT,
json=payload,
headers=headers,
timeout=30,
)
response.raise_for_status()
return response.json()
Some API setups use GET instead of POST, or pass the API key as a query parameter. If your account works differently, update only this helper.
Step 4: Fetch Yandex SERP Results
Now create a function for localized Yandex search requests.
def fetch_yandex_serp(keyword, location, country, language, device="desktop"):
payload = {
"engine": "yandex",
"q": keyword,
"location": location,
"gl": country,
"hl": language,
"device": device,
"num": 10,
"output": "json",
}
return call_serp_api(payload)
The exact parameter names may depend on your SERP API setup. The important idea is to keep keyword, location, country, language, and device explicit in each request.
Step 5: Extract Organic Results
SERP API responses may use different field names, so keep the parser flexible.
from urllib.parse import urlparse
def clean_text(value):
if not value:
return ""
return " ".join(str(value).split())
def get_domain(url):
if not url:
return ""
parsed = urlparse(url)
return parsed.netloc.replace("www.", "")
def get_organic_results(serp_json):
return (
serp_json.get("organic_results")
or serp_json.get("organic")
or serp_json.get("results")
or []
)
def normalize_organic_results(serp_json):
results = get_organic_results(serp_json)
rows = []
for index, item in enumerate(results, start=1):
url = item.get("link") or item.get("url") or ""
rows.append({
"position": item.get("position") or item.get("rank") or index,
"title": clean_text(item.get("title")),
"url": url,
"domain": get_domain(url),
"snippet": clean_text(item.get("snippet") or item.get("description")),
})
return rows
This gives you a clean ranking list for each keyword and market.
Step 6: Find the Target Domain Ranking
Check whether your target domain appears in the top results.
def find_domain_ranking(results, target_domain):
target = target_domain.replace("www.", "").lower()
for item in results:
domain = item["domain"].lower()
if domain == target or domain.endswith("." + target):
return {
"position": item["position"],
"matched_url": item["url"],
"matched_title": item["title"],
}
return {
"position": None,
"matched_url": "",
"matched_title": "",
}
def summarize_top_domains(results, limit=10):
domains = []
for item in results[:limit]:
if item["domain"]:
domains.append(item["domain"])
return " | ".join(domains)
If the target domain is not found, keep the position as None. That makes reports more honest than forcing a fake ranking value.
Step 7: Export Yandex Ranking Data to CSV
import csv
from datetime import datetime, timezone
CSV_COLUMNS = [
"keyword",
"market",
"location",
"country",
"language",
"device",
"target_domain",
"position",
"matched_url",
"matched_title",
"top_domains",
"collected_at",
]
def export_to_csv(rows, filename="yandex_rankings.csv"):
if not rows:
print("No ranking rows found.")
return
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=CSV_COLUMNS)
writer.writeheader()
writer.writerows(rows)
print(f"Exported {len(rows)} ranking rows to {filename}")
Complete Python Example
import os
import csv
import requests
from datetime import datetime, timezone
from urllib.parse import urlparse
KEYWORDS = [
"купить ноутбук",
"доставка цветов",
"онлайн курсы английского",
]
MARKETS = [
{
"market": "Russia - Moscow",
"location": "Moscow, Russia",
"country": "ru",
"language": "ru",
},
{
"market": "Kazakhstan - Almaty",
"location": "Almaty, Kazakhstan",
"country": "kz",
"language": "ru",
},
{
"market": "Belarus - Minsk",
"location": "Minsk, Belarus",
"country": "by",
"language": "ru",
},
]
TARGET_DOMAIN = "example.com"
DEVICE = "desktop"
CSV_COLUMNS = [
"keyword",
"market",
"location",
"country",
"language",
"device",
"target_domain",
"position",
"matched_url",
"matched_title",
"top_domains",
"collected_at",
]
def require_env(name):
value = os.getenv(name)
if not value:
raise RuntimeError(f"Missing required environment variable: {name}")
return value
TALORDATA_API_KEY = require_env("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = require_env("TALORDATA_SERP_ENDPOINT")
def call_serp_api(payload):
headers = {
"Authorization": f"Bearer {TALORDATA_API_KEY}",
"Content-Type": "application/json",
}
response = requests.post(
TALORDATA_SERP_ENDPOINT,
json=payload,
headers=headers,
timeout=30,
)
response.raise_for_status()
return response.json()
def fetch_yandex_serp(keyword, location, country, language, device="desktop"):
payload = {
"engine": "yandex",
"q": keyword,
"location": location,
"gl": country,
"hl": language,
"device": device,
"num": 10,
"output": "json",
}
return call_serp_api(payload)
def clean_text(value):
if not value:
return ""
return " ".join(str(value).split())
def get_domain(url):
if not url:
return ""
parsed = urlparse(url)
return parsed.netloc.replace("www.", "")
def get_organic_results(serp_json):
return (
serp_json.get("organic_results")
or serp_json.get("organic")
or serp_json.get("results")
or []
)
def normalize_organic_results(serp_json):
results = get_organic_results(serp_json)
rows = []
for index, item in enumerate(results, start=1):
url = item.get("link") or item.get("url") or ""
rows.append({
"position": item.get("position") or item.get("rank") or index,
"title": clean_text(item.get("title")),
"url": url,
"domain": get_domain(url),
"snippet": clean_text(item.get("snippet") or item.get("description")),
})
return rows
def find_domain_ranking(results, target_domain):
target = target_domain.replace("www.", "").lower()
for item in results:
domain = item["domain"].lower()
if domain == target or domain.endswith("." + target):
return {
"position": item["position"],
"matched_url": item["url"],
"matched_title": item["title"],
}
return {
"position": None,
"matched_url": "",
"matched_title": "",
}
def summarize_top_domains(results, limit=10):
domains = []
for item in results[:limit]:
if item["domain"]:
domains.append(item["domain"])
return " | ".join(domains)
def export_to_csv(rows, filename="yandex_rankings.csv"):
if not rows:
print("No ranking rows found.")
return
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=CSV_COLUMNS)
writer.writeheader()
writer.writerows(rows)
print(f"Exported {len(rows)} ranking rows to {filename}")
if __name__ == "__main__":
collected_at = datetime.now(timezone.utc).isoformat()
output_rows = []
for keyword in KEYWORDS:
for market in MARKETS:
serp_json = fetch_yandex_serp(
keyword=keyword,
location=market["location"],
country=market["country"],
language=market["language"],
device=DEVICE,
)
organic_results = normalize_organic_results(serp_json)
ranking = find_domain_ranking(organic_results, TARGET_DOMAIN)
output_rows.append({
"keyword": keyword,
"market": market["market"],
"location": market["location"],
"country": market["country"],
"language": market["language"],
"device": DEVICE,
"target_domain": TARGET_DOMAIN,
"position": ranking["position"],
"matched_url": ranking["matched_url"],
"matched_title": ranking["matched_title"],
"top_domains": summarize_top_domains(organic_results),
"collected_at": collected_at,
})
export_to_csv(output_rows)
After running the script, you should get:
yandex_rankings.csv
Example Output
keyword,market,location,country,language,device,target_domain,position,matched_url,matched_title,top_domains,collected_at
купить ноутбук,Russia - Moscow,"Moscow, Russia",ru,ru,desktop,example.com,4,https://example.com/laptops,Example Laptop Store,domain1.ru | domain2.ru | example.com,2026-06-22T00:00:00Z
доставка цветов,Kazakhstan - Almaty,"Almaty, Kazakhstan",kz,ru,desktop,example.com,,,,domain3.kz | domain4.kz | domain5.kz,2026-06-22T00:00:00Z
How to Use the Output
For Yandex SEO reporting, group the CSV by:
|
Grouping |
What it shows |
|
Keyword |
Which queries have visibility |
|
Market |
Where rankings are strong or weak |
|
Language |
Whether Russian or local-language pages are visible |
|
Device |
Whether desktop and mobile differ |
|
Top domains |
Which competitors appear most often |
For example:
Keyword: доставка цветов
Moscow: position 5
Almaty: not found
Minsk: position 9
This tells you where to improve localized landing pages, Russian-language content, internal links, product pages, or regional relevance.
Best Practices
Track Yandex separately from Google. Do not assume one search engine’s ranking equals another’s.
Use real local keyword phrasing. Direct translation may not match how users search.
Store market, location, language, country, and device in every row. Ranking data without context is hard to analyze.
Keep raw API responses during development. They help you debug missing fields and SERP layout changes.
Separate organic results, ads, local results, and special SERP features. Each result type should be labeled clearly.
Move to a database when tracking becomes recurring. CSV is good for testing, but historical dashboards need structured storage.
Test Yandex SERP API for free>>
FAQ
What is a Yandex Search API?
A Yandex Search API lets you collect Yandex search results programmatically. It is useful for SEO rank tracking, competitor monitoring, market research, and localized search analysis.
Why should I track Yandex rankings?
If your audience searches on Yandex, Google-only rank tracking may miss important visibility changes. Yandex rankings should be monitored as a separate channel.
Can I track rankings by city and language?
Yes. A SERP API can send localized requests using city, country, language, and device parameters, depending on the provider’s supported options.
Can I compare Yandex and Google rankings?
Yes. You can run the same keyword list through both search engines and compare ranking positions, top domains, snippets, and landing pages.
Should I store Yandex ranking data in CSV or a database?
CSV works for small tests and reports. For recurring rank tracking, alerts, dashboards, and historical comparison, use a database.