How Keyword Location Resolution Handles Different Markets in Open‑SEO
Open‑SEO maps markets (location + language pairs) to the correct DataForSEO API endpoints through a centralized resolution system that automatically selects providers, validates languages, and gracefully falls back when Labs‑only tools encounter unsupported regions.
The every-app/open-seo repository implements market-aware keyword data fetching in src/shared/keyword-locations.ts. This module ensures that every request reaches the right API with a compatible location and language combination, preventing costly failed calls and mismatched search data.
Core Architecture of Market Resolution
The resolution system rests on three pillars: a canonical location registry, provider-aware routing, and hierarchical override logic.
The LOCATION_OPTIONS Registry
All supported countries are enumerated in LOCATION_OPTIONS (lines 66‑73). Each entry includes:
code: Numeric DataForSEO location identifierlabel: Human‑readable country namedisplay_iso: Short ISO code for UI presentationdefaultLanguageCode: Primary search market languagegoogleAdsOnly: Optional flag restricting the location to Google Ads API only
This registry is the single source of truth for what markets exist and how they behave.
Provider Routing with getKeywordDataProvider
Not all locations work with every DataForSEO product. The getKeywordDataProvider function (lines 48‑55) inspects the googleAdsOnly flag:
// Returns "google_ads" for restricted locations, "labs" for standard coverage
const provider = getKeywordDataProvider(locationCode);
Labs‑only tools must avoid googleAdsOnly markets entirely, which triggers the fallback system described below.
How resolveMarket Processes Overrides
The resolveMarket function (lines 101‑119) implements the core override hierarchy:
- Caller‑supplied location takes precedence over project defaults
- Language snaps to location default when location changes, unless explicitly overridden
- Unchanged project defaults pass through when no overrides provided
import { resolveMarket } from "./src/shared/keyword-locations";
const projectDefault = { locationCode: 2840, languageCode: "en" }; // US/English
// Location change triggers language auto‑selection
const germany = resolveMarket({ locationCode: 2276 }, projectDefault);
// → { locationCode: 2276, languageCode: "de" }
// Explicit language override preserved
const vietnam = resolveMarket({ locationCode: 2704, languageCode: "vi" }, projectDefault);
// → { locationCode: 2704, languageCode: "vi" }
This prevents the common error of requesting German keywords with English language codes.
Labs‑Only Tool Handling with resolveLabsMarket
Tools restricted to the DataForSEO Labs API use resolveLabsMarket (lines 145‑159), which adds a compatibility check:
import { resolveLabsMarket } from "./src/shared/keyword-locations";
// Project based in UAE (googleAdsOnly: true)
const uaeProject = { locationCode: 2784, languageCode: "en" };
const labsCompatible = resolveLabsMarket({}, uaeProject);
// → { locationCode: 2840, languageCode: "en" } // Falls back to US/EN
The function isLanguageServedForLocation validates Labs compatibility. When the default market fails this check, DEFAULT_LOCATION_CODE (United States, line 22) substitutes with English before delegating to resolveMarket.
Language Validation and Restriction
Two mechanisms prevent invalid language codes from reaching DataForSEO:
Global validation via isSupportedLanguageCode (lines 68‑70):
if (!isSupportedLanguageCode("zz")) {
throw new Error("Invalid language code: zz");
}
Per‑location restriction via getLanguageOptions (lines 108‑114), which filters the global language list to only those supported for a specific country, including multi‑language overrides defined in MULTI_LANGUAGE_LOCATIONS.
Keyword Data Language Fallback
When SERP and keyword data APIs support different language sets, resolveKeywordDataLanguage (lines 124‑132) bridges the gap:
// If SERP language "sv" is unsupported for keyword data in target location,
// falls back to location's default language
const dataLanguage = resolveKeywordDataLanguage(requestedLanguage, locationCode);
This ensures volume and competition metrics return in a compatible language rather than failing silently.
Complete Working Example
import {
resolveMarket,
resolveLabsMarket,
getKeywordDataProvider,
getLanguageOptions,
isSupportedLanguageCode,
} from "./src/shared/keyword-locations";
// Scenario: Multi‑market SEO tool with Labs‑only keyword difficulty feature
const userProject = {
locationCode: 2840, // United States
languageCode: "en"
};
// Standard search request accepts overrides
const searchRequest = resolveMarket(
{ locationCode: 2276 }, // User switches to Germany
userProject
);
console.log(searchRequest); // { locationCode: 2276, languageCode: "de" }
// Keyword difficulty tool (Labs‑only) handles unsupported markets
const difficultyRequest = resolveLabsMarket(
{ locationCode: 2784 }, // User requests UAE (googleAdsOnly)
userProject
);
console.log(difficultyRequest); // { locationCode: 2840, languageCode: "en" }
// Validate UI input before API call
const userLanguage = "fr";
if (!isSupportedLanguageCode(userLanguage)) {
console.error("Please select a supported language");
}
// Populate language dropdown for selected country
const availableLanguages = getLanguageOptions(2276); // Germany
// → [{ code: "de", label: "German" }, { code: "en", label: "English" }, ...]
Key Source Files
| File | Purpose |
|---|---|
[src/shared/keyword-locations.ts](https://github.com/every-app/open-seo/blob/main/src/shared/keyword-locations.ts) |
Core implementation including resolveMarket, resolveLabsMarket, location tables, and provider routing |
[src/shared/keyword-locations.test.ts](https://github.com/every-app/open-seo/blob/main/src/shared/keyword-locations.test.ts) |
Unit tests for override behavior, fallback logic, and edge cases |
[src/shared/keyword-locations.vendor-defaults.test.ts](https://github.com/every-app/open-seo/blob/main/src/shared/keyword-locations.vendor-defaults.test.ts) |
Validation of DataForSEO Labs default language alignment |
Summary
- Market resolution combines location and language into validated pairs for DataForSEO requests
- Location overrides automatically select appropriate default languages unless explicitly specified
- Labs‑only tools fall back to US/English when project defaults are Google‑Ads‑only regions
- Early validation of language codes prevents charged API failures
- Per‑location language lists restrict selections to actually supported combinations
Frequently Asked Questions
What happens when a user requests a location that only supports Google Ads?
The getKeywordDataProvider function detects the googleAdsOnly flag and returns "google_ads". For Labs‑only tools, resolveLabsMarket substitutes the fallback location (DEFAULT_LOCATION_CODE 2840, United States) with English to ensure the request can proceed.
How does Open‑SEO prevent language mismatches when switching countries?
The resolveMarket function automatically calls getLanguageCode to snap the language to the new location's default whenever the location changes. Users must explicitly provide a languageCode override to preserve their original selection.
Why does keyword data sometimes use a different language than SERP data?
DataForSEO's keyword data APIs support fewer languages than their SERP endpoints. The resolveKeywordDataLanguage function detects unsupported combinations and falls back to the location's primary language, ensuring volume and competition metrics remain available.
Where is the master list of supported locations defined?
The LOCATION_OPTIONS array in src/shared/keyword-locations.ts (lines 66‑73) contains all supported countries with their codes, labels, default languages, and Google Ads restrictions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →