International keyword research: markets and languages
International keyword research is the same research repeated per market, where a market is a country and a language together, and the differences between markets are the finding. The SERP for “accounting software” in the US, “Buchhaltungssoftware” in Germany and “logiciel comptable” in France, Canada and Switzerland each has its own questions, features, AI Overview behavior and competitors. The steps below define markets, collect SERPs per market with this API’s country parameter and native-language queries, and compare them. This API returns no search volume, so pair it with a volume source (why).
Definitions
- Market: a country plus a language, such as
CA/frorUS/es. Two languages in one country are two markets. - Seed keyword: a starting keyword for one market, written natively or translated and then validated.
- Transcreation: adapting a keyword to how people in a market express the same need, rather than translating the words.
- SERP overlap: the share of ranking domains two markets have in common for equivalent keywords.
The parameters
From the OpenAPI reference, POST /v1/monitor/google takes:
| Parameter | Format | Role |
|---|---|---|
country |
ISO 3166-1 alpha-2, such as DE |
Result geography. Required in the standard request shape. |
gl |
Same codes, either case | Google’s own name for the same setting. Send country or gl; different values in both is a 400. |
url |
Full Google search URL | Alternative shape to query and country. |
The language of a Google Search market comes from the country (or hl, when set) and from the language the query is written in. location or uule targets a city.
GET /v1/countries returns the supported country codes; pass ?model=google (or aioverview, aimode, chatgpt and so on) to filter by engine. Check it once when you define markets rather than discovering an unsupported code as a validation error mid-run.
AI engines take market parameters too. AI Mode accepts country, gl, hl, location and uule, and Copilot, Perplexity and Grok take any country. ChatGPT and Gemini take country, and both answer from 213 countries; Gemini also takes a US state. Multilingual AI answers covers monitoring those per language.
Canonical location names
AI Mode’s location expects names from Google’s geo targets list. The file published on 2026-08-12 (Google Ads API geotargets, checked 2026-09-17) has columns Criteria ID, Name, Canonical Name, Parent ID, Country Code, Target Type and Status. Canonical names do not always follow English spelling or an obvious region: in that file, Munich is Munich,Bavaria,Germany and Lyon is Lyon,Auvergne-Rhone-Alpes,France. Look names up in the file rather than typing them.
Step 1: define the market matrix
List markets explicitly, with the reason each exists:
MARKETS = [
{"id": "us-en", "country": "US", "lang": "en"},
{"id": "us-es", "country": "US", "lang": "es"}, # Spanish-language searchers in the US
{"id": "gb-en", "country": "GB", "lang": "en"},
{"id": "de-de", "country": "DE", "lang": "de"},
{"id": "ch-de", "country": "CH", "lang": "de"}, # same language as DE, different market
{"id": "ch-fr", "country": "CH", "lang": "fr"},
{"id": "ca-en", "country": "CA", "lang": "en"},
{"id": "ca-fr", "country": "CA", "lang": "fr"},
{"id": "br-pt", "country": "BR", "lang": "pt"},
]
Three rules:
- Separate languages within a country. Canada in English and Canada in French are different research projects, each with keywords written in its own language.
- Separate countries within a language. German in Germany, Austria and Switzerland shares words but not competitors, currencies or regulations.
- Add a market only if you would act on it. Every market multiplies cost.
Step 2: build native seeds
Translation gets you a first guess. The local SERP tells you whether locals search that way.
- Write or translate 10 to 30 seed keywords per market, ideally reviewed by a native speaker.
- Run each seed in its market.
- Read
relatedSearches[].queryandpeopleAlsoAsk[].questionfrom the response. These are phrased by the market. - Replace seeds that return thin or off-topic related searches with the phrasing the SERP suggests.
A seed whose related searches are about something else entirely is a mistranslation; drop it early before it spawns a hundred wrong expansions.
Step 3: collect SERPs as batches
One task per keyword per market. Submit them with POST /v1/async/task/batch (1 to 500 tasks per request), keyed by meaning so re-runs are safe:
import os
from datetime import date
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['ANSWERLINE_API_KEY']}"}
WEBHOOK = "https://research.example.com/hooks/serp"
def tasks_for(keywords: dict[str, list[str]], run: str) -> list[dict]:
"""keywords maps market id to that market's keyword list."""
by_id = {m["id"]: m for m in MARKETS}
return [
{
"taskType": "GOOGLE",
"payload": {
"query": kw,
"country": by_id[market]["country"],
"include": {"aioverview": {}},
},
"idempotencyKey": f"intl:{run}:{market}:{kw}",
"webhook": {"url": WEBHOOK},
}
for market, kws in keywords.items()
for kw in kws
]
def submit(tasks: list[dict]) -> list[dict]:
created = []
for i in range(0, len(tasks), 500):
chunk = tasks[i:i + 500]
body = requests.post(f"{API}/v1/async/task/batch", headers=HEADERS, json=chunk, timeout=60).json()
for item in body["results"]:
if item["success"]:
created.append({"key": chunk[item["index"]]["idempotencyKey"], "task_id": item["task"]["id"]})
elif item["error"]["code"] != "RESOURCE_ALREADY_EXISTS":
print(chunk[item["index"]]["idempotencyKey"], item["error"]["code"])
return created
run = date.today().isoformat()
created = submit(tasks_for({"de-de": ["buchhaltungssoftware", "rechnungsprogramm"], "ca-fr": ["logiciel comptable"]}, run))
Idempotency keys are unique across your account, and long keyword text makes long keys; if that becomes unwieldy, use a keyword id from your own table instead of the text. Store task_id next to the key so a reconciliation pass can fetch anything whose webhook never arrived with GET /v1/async/task/{taskId}. Receiving and verifying webhooks is covered in webhook receiver design.
Step 4: reduce each SERP to a comparable record
Store one row per keyword, market and run:
from urllib.parse import urlparse
def host(url: str) -> str:
h = urlparse(url).netloc.lower()
return h[4:] if h.startswith("www.") else h
def record(market: str, keyword: str, result: dict) -> dict:
organic = [r for r in result.get("organicResults", []) if r.get("position", 99) <= 10]
aio = result.get("aioverview")
return {
"market": market,
"keyword": keyword,
"top10": [host(r["link"]) for r in organic],
"top10_urls": [r["link"] for r in organic],
"has_aio": aio is not None,
"aio_sources": [host(s["url"]) for s in (aio or {}).get("sources", [])],
"paa": [q["question"] for q in result.get("peopleAlsoAsk", [])],
"related": [r["query"] for r in result.get("relatedSearches", [])],
"ads": len(result.get("ads", [])),
"local": bool(result.get("localResults")),
}
Step 5: compare markets
Map equivalent keywords across markets with a concept id (for example accounting-software maps to buchhaltungssoftware in de-de, logiciel comptable in ca-fr). Then compare per concept.
Competitor overlap
def overlap(a: list[str], b: list[str]) -> float:
sa, sb = set(a), set(b)
return len(sa & sb) / len(sa | sb) if sa | sb else 0.0
Low overlap between two markets for the same concept means different competitors: global category leaders in one, local specialists in the other. That changes both content strategy and who you benchmark against.
Feature differences
For each concept, tabulate per market: AI Overview present, ads count, local results present, PAA count. Typical findings worth acting on:
- AI Overview in one market but not another. Plan for citation in the first; AI Overview tracking tools compares ways to monitor it.
- Ads only in some markets. Commercial competition differs; the SERP may be easier organically where advertisers are absent, or the market may be commercially unattractive.
- Local results appear. The concept is local there; plan research by city (local rank tracking by city).
Question sets
Compare paa lists per concept. Questions that appear only in one market (regulation, tax, a local payment method, a local competitor) are the content that localization by translation misses.
Step 6: check which of your pages rank where
International sites often rank the wrong locale: the US English page in Germany, the French-France page in Quebec. From the stored top10_urls:
EXPECTED = {"us-en": "/en-us/", "de-de": "/de-de/", "ca-fr": "/fr-ca/", "ch-fr": "/fr-ch/"}
def locale_mismatches(rows: list[dict], my_host: str) -> list[tuple]:
out = []
for row in rows:
expected = EXPECTED.get(row["market"])
for url in row["top10_urls"]:
if host(url) == my_host and expected and expected not in url:
out.append((row["market"], row["keyword"], url))
return out
Adapt the path check to your URL scheme (subdirectories, subdomains or country domains). Each mismatch is a candidate hreflang, canonical or content issue to investigate.
City-level research inside a market
Some markets are not uniform. Google Search and AI Mode take location with a canonical name, so for local-intent concepts you can see how Google’s AI answer changes by city with POST /v1/monitor/aimode:
{ "prompt": "steuerberater", "country": "DE", "hl": "de", "location": "Munich,Bavaria,Germany" }
location cannot be combined with uule. Every city is another request at the same price.
What it costs
Costs at the Starter tier: a Google Search request costs $0.00103 for one page, include.aioverview adds $0.00071, and each extra results page adds $0.00071. An AI Mode request costs $0.00206.
| Run | Tasks | Credits |
|---|---|---|
| 200 keywords × 4 markets, one page | 800 | $0.824 |
| 500 keywords × 6 markets, with AI Overview | 3,000 | $5.22 |
| Seed validation: 20 seeds × 9 markets, with AI Overview | 180 | $0.3132 |
| 50 local concepts × 10 cities in one market, AI Mode | 500 | $1.03 |
Markets multiply everything. Validate seeds on a small set first; the 100 free requests a new account gets work on any engine, enough to spot-check a few seeds before the full validation run. Tier prices are on /pricing.
Pitfalls
- One market per country. Multilingual countries need a market per language.
- Mismatched
countryandgl. Send one; different values in both is rejected. - Queries in the wrong language. The query carries the market’s language on Google Search, so Spanish in the US needs Spanish keywords, not the English list.
- Guessed location names. Use canonical names from the geo targets file; the English exonym is often not the canonical name.
- Translated keyword lists taken as final. Validate every seed against its local SERP.
- Comparing runs from different days across markets. Collect all markets in the same run window so differences are geographic, not temporal.
- No volume source. SERP data tells you what a market looks like, not how big it is.
See the keyword research use case and the Google Search engine page to start.
Questions
Which parameters control country and language in this API?
On Google Search, country (or gl) sets the result geography as an ISO 3166-1 alpha-2 code, and the language you write the query in carries the market's language; hl sets the interface language, and location or uule a city or region. AI Mode takes the same fields. GET /v1/countries lists the supported country codes, filterable by engine.
Can the interface language differ from the country?
Yes. On Google Search, write the query in the market's language, so Spanish in the US or French in Canada are separate markets. On AI Mode, hl also overrides the interface language derived from the country.
Should I translate my keyword list for each market?
Use translation only to create seeds, then expand from the local results page: related searches and People Also Ask in the market's language show how people there phrase the need. Translated keywords often are not what locals search.
How do I check that the right language version of my site ranks in each market?
Collect organic results per market, find the URLs on your domain, and compare each URL's locale path or subdomain with the market's expected locale. A mismatch, such as the US page ranking in Germany, points to hreflang or localization problems.
What does a multi-market keyword run cost?
Each keyword in each market is one request. A Google Search request is $0.00103 on every tier, plus $0.00071 with the AI Overview. 500 keywords across 6 markets with AI Overview detection is 3,000 tasks and $5.22.