How OpenSEO Handles Keyword Locations for Rank Tracking: A Technical Deep Dive
OpenSEO uses DataForSEO location codes with fallback logic for languages and API provider selection to ensure every rank-tracking query targets a valid geographic market.
The open-source OpenSEO project (available at every-app/open-seo) implements a robust location-handling system for SERP rank tracking. Its architecture centers on a curated catalog of country codes, intelligent language resolution, and automatic provider routing to DataForSEO's APIs. This ensures that keywords are evaluated in the correct market with appropriate localization settings.
Location Catalog and Data Structure
All supported markets are defined in src/shared/keyword-locations.ts. The static array LOCATION_OPTIONS contains every country available for tracking.
Each location entry includes:
- location_code – DataForSEO's numeric identifier (e.g.,
2840for United States) - label – Human-readable country name
- short_label – ISO-3166-1 alpha-2 code for display
- default_language – The market's primary language for fallback purposes
- googleAdsOnly – Flag indicating Google-Ads-only availability
The United States (2840) serves as the product-wide default, and the list is maintained in alphabetical order for consistent UI rendering.
ISO Code Conversion for API Requests
The getIsoCountryCode() function (lines 0048–0053 in keyword-locations.ts) converts location codes to lowercase ISO-3166-1 alpha-2 identifiers required by external APIs.
import { getIsoCountryCode } from '@/shared/keyword-locations';
// United States
console.log(getIsoCountryCode(2840)); // 'us'
// United Kingdom (special case handling)
console.log(getIsoCountryCode(2826)); // 'gb' (not 'uk')
The function applies a hardcoded override for the United Kingdom to ensure compliance with ISO standards (UK → GB).
Market Resolution with Language Fallback
The resolveMarket() function (lines 0170–0276) merges user-provided location and language preferences with project defaults, applying intelligent fallbacks when inputs are incomplete.
import { resolveMarket } from '@/shared/keyword-locations';
const projectDefaults = { locationCode: 2840, languageCode: 'en' };
// User specifies Canada but omits language
const result = resolveMarket({ locationCode: 2124 }, projectDefaults);
console.log(result);
// { locationCode: 2124, languageCode: 'en' } – inherits Canada's default
This prevents API failures from unsupported language-location combinations while respecting explicit user intent.
Language Validation for Keyword Data Endpoints
DataForSEO's SERP API accepts any language for any location, but its keyword-data endpoints (Labs and Google Ads) enforce stricter validation. The resolveKeywordDataLanguage() function (lines 00831–00840) ensures compatibility:
- Validates whether the requested language is supported for the given location
- Falls back to the location's default language when unsupported
- Returns a guaranteed-valid code for Labs or Google Ads calls
import { resolveKeywordDataLanguage } from '@/shared/keyword-locations';
// Requesting Spanish data for Japan (unsupported combination)
const safeLang = resolveKeywordDataLanguage(2392, 'es');
console.log(safeLang); // 'ja' – falls back to Japan's default
Automatic Provider Selection
The getKeywordDataProvider() function (lines 0055–0062) determines which DataForSEO backend to use based on location availability:
- "labs" – Used for locations in the Labs catalog (broader metrics support)
- "google_ads" – Used for Google-Ads-only locations
import { getKeywordDataProvider } from '@/shared/keyword-locations';
console.log(getKeywordDataProvider(2840)); // 'labs' (United States)
console.log(getKeywordDataProvider(2344)); // 'google_ads' (Hong Kong)
This routing happens automatically without user intervention.
SERP Request Construction in Rank Tracking
When src/shared/rank-tracking.ts schedules a rank check, it packages resolved location and language values into the DataForSEO payload. The implementation separates concerns between SERP and keyword-data requirements:
import {
getKeywordDataProvider,
resolveKeywordDataLanguage
} from '@/shared/keyword-locations';
function buildSerpPayload(locationCode: number, languageCode: string) {
return {
location_code: locationCode,
language_code: languageCode, // SERP: any language accepted
keyword_language: resolveKeywordDataLanguage(locationCode, languageCode),
provider: getKeywordDataProvider(locationCode),
};
}
// Hong Kong with English preference
const payload = buildSerpPayload(2344, 'en');
/*
{
location_code: 2344,
language_code: 'en',
keyword_language: 'en', // validated as supported
provider: 'google_ads'
}
*/
Input Validation Guards
Multiple guard functions prevent invalid API calls:
| Function | Purpose | Line Range |
|---|---|---|
isSupportedLocationCode() |
Verifies location exists in catalog | — |
isLabsLocationCode() |
Checks Labs availability | — |
isLanguageServedForLocation() |
Validates language-location compatibility | — |
These run before any external request, catching errors early in the pipeline.
Cost Estimation Integration
The location system integrates with cost calculation in src/shared/rank-tracking.ts. The estimateScheduledRankCheckCredits() function (lines 0212–0219) projects usage based on keyword volume, device targeting, result depth, and schedule frequency:
import { estimateScheduledRankCheckCredits } from '@/shared/rank-tracking';
const estimate = estimateScheduledRankCheckCredits(
20, // keywords
'both', // desktop + mobile
30, // results depth
'weekly', // schedule
);
console.log(estimate.monthlyCostUsd); // 0.048
console.log(estimate.monthlyCostCredits); // 48
Server-Side Execution Flow
The server endpoint at src/serverFunctions/rank-tracking.ts receives client requests and orchestrates the full pipeline:
- Receives location and language parameters
- Calls
resolveMarket()to normalize inputs - Validates with guard functions
- Constructs DataForSEO payload with resolved values
- Dispatches to appropriate API endpoint
This ensures consistent behavior across all rank-tracking operations.
Summary
- Static catalog in
keyword-locations.tsdefines all supported markets with metadata - ISO conversion handles edge cases like United Kingdom (
UK → GB) - Language resolution provides sensible fallbacks for incomplete or invalid combinations
- Provider routing automatically selects Labs or Google Ads based on location type
- Validation guards prevent API errors before requests are sent
- Server integration combines all components for reliable rank-tracking execution
Frequently Asked Questions
What happens if I specify a language that isn't supported for my chosen location?
The system falls back to the location's default language. For example, requesting Spanish (es) for Japan (2392) automatically switches to Japanese (ja). This fallback is handled by resolveKeywordDataLanguage() in keyword-locations.ts, ensuring API requests always succeed.
Can I track rankings for Google-Ads-only locations like Hong Kong?
Yes. Locations flagged with googleAdsOnly: true route automatically to the Google Ads API via getKeywordDataProvider(). You don't need to configure this manually—the system detects the location code and selects the appropriate backend.
Why does the United Kingdom convert to 'gb' instead of 'uk'?
The getIsoCountryCode() function applies a hardcoded override (UK → GB) to comply with ISO-3166-1 alpha-2 standards. While DataForSEO accepts various identifiers, external APIs and data processors typically require strict ISO compliance, making this conversion necessary for reliable integrations.
How does OpenSEO estimate costs for scheduled rank checks?
The estimateScheduledRankCheckCredits() function in rank-tracking.ts calculates credits based on four factors: number of keywords, device targeting (desktop/mobile/both), SERP result depth, and schedule frequency. It returns per-run costs, monthly projections, and credit consumption for budget planning.
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 →