How to Get Website Traffic by Country with Domain Rank Overview
The Domain Rank Overview endpoint of DataForSEO Labs API returns ranking and traffic data from organic and paid search for any domain. The data is updated weekly. You can check the latest update date via the DataForSEO Labs Status endpoint.
Here is the key trick. Location and language are optional parameters. If you omit both, the API returns one data row per country-language combination, covering every locale where the domain has rankings. Each row gives you:
- Organic ETV: estimated monthly organic traffic.
- Paid ETV: estimated monthly paid traffic.
- Keyword count: how many keywords the domain ranks for in that location.
- Estimated paid traffic cost: the USD you would spend on PPC to buy the same organic volume.
- Ranking distribution: keyword counts in position bands from #1 to #91-100.
Pricing is per request and scales with the number of rows returned. See the Pricing page for details.
The Trick: Set the Limit to 1000 and Leave Out Location and Language
The only required field is target (the domain, specified without https:// and without www.). Leave out location_name/location_code and language_name/language_code to get all locales at once.
Note: always set “limit” to 1000, which is the maximum. The default limit is 100, and the response’s total_count field only reports how many rows were returned, not how many actually exist. With the default, countries are silently dropped. For example, dataforseo.com has 102 locale rows. The default limit returns 100, hiding two of them.
Minimal Request
curl -X POST \
"https://api.dataforseo.com/v3/dataforseo_labs/google/domain_rank_overview/live" \
-u "login:password" \
-H "Content-Type: application/json" \
--data '[{ "target": "dataforseo.com", "limit": 1000 }]'
Replace “login” and “password” with your API credentials.
Understanding the Response
The API returns a JSON object. Drill down through tasks, then result, then items to reach the data rows. Each row is one location plus language combination, carrying a metrics object split into organic and paid.
Here is a trimmed example (two rows shown):
{
"tasks": [{
"result": [{
"target": "dataforseo.com",
"total_count": 102,
"items_count": 102,
"items": [
{
"location_code": 2012,
"language_code": "ar",
"metrics": {
"organic": {
"etv": 26.0,
"count": 2,
"estimated_paid_traffic_cost": 5.0,
"pos_1": 0, "pos_2_3": 1, "pos_4_10": 0, "pos_11_20": 1,
"is_new": 1, "is_up": 0, "is_down": 0, "is_lost": 0
},
"paid": {
"etv": 0, "count": 0, "estimated_paid_traffic_cost": 0,
"pos_1": 0, "pos_2_3": 0, "pos_4_10": 0
}
}
},
{
"location_code": 2840,
"language_code": "en",
"metrics": {
"organic": {
"etv": 9079.4,
"count": 3193,
"estimated_paid_traffic_cost": 15078.99,
"pos_1": 11, "pos_2_3": 28, "pos_4_10": 100, "pos_11_20": 135
},
"paid": {
"etv": 32.17, "count": 11, "estimated_paid_traffic_cost": 275.83
}
}
}
]
}]
}]
}
Key fields per row:
location_code/language_code– the location this row coversorganic.etv– estimated monthly organic traffic for this locationorganic.count– the number of keywords the domain ranks for hereorganic.estimated_paid_traffic_cost– USD to replicate this organic volume via PPCorganic.pos_1 ... pos_91_100– keyword counts in each ranking bandpaid.etv– estimated monthly paid trafficorganic.is_new/is_up/is_down/is_lost– ranking movements since last check
Note: the same country can appear more than once. The United States, for instance, shows up as location_code: 2840 with language_code: "en" and again with "es" (Spanish). To get a single traffic figure per country, sum the rows that share the same location_code.
Example: Ranked Country Traffic (Python)
This script does everything in one go. It fetches all locales, aggregates to per-country totals, resolves codes to country names, and prints a ranked table.
import requests
import collections
AUTH = ("login", "password") # from app.dataforseo.com/api-access
# 1. Fetch traffic for ALL countries (omit location and language; limit = max)
url = "https://api.dataforseo.com/v3/dataforseo_labs/google/domain_rank_overview/live"
resp = requests.post(url, auth=AUTH,
json=[{"target": "dataforseo.com", "limit": 1000}]).json()
rows = resp["tasks"][0]["result"][0]["items"]
# 2. Aggregate ETV by country (sum across languages within a country)
by_country = collections.defaultdict(
lambda: {"organic": 0.0, "paid": 0.0, "keywords": 0}
)
for r in rows:
c = by_country[r["location_code"]]
c["organic"] += r["metrics"]["organic"]["etv"]
c["paid"] += r["metrics"]["paid"]["etv"]
c["keywords"] += r["metrics"]["organic"]["count"]
# 3. Resolve location codes to country names (free endpoint, cost = 0)
names_resp = requests.get(
"https://api.dataforseo.com/v3/dataforseo_labs/locations_and_languages",
auth=AUTH
).json()
code_to_name = {
x["location_code"]: x["location_name"]
for x in names_resp["tasks"][0]["result"]
}
# 4. Print ranked table
print(f"{'Country':<25}{'Organic ETV':>14}{'Paid ETV':>12}{'Keywords':>10}")
print("-" * 61)
for code, m in sorted(by_country.items(), key=lambda x: -x[1]["organic"])[:15]:
print(f"{code_to_name.get(code, code):<25}"
f"{m['organic']:>14,.0f}"
f"{m['paid']:>12,.0f}"
f"{m['keywords']:>10,}")
Running this script will produce a similar table:
Country Organic ETV Paid ETV Keywords ------------------------------------------------------------- United States 10,638 32 3,246 India 6,955 0 1,383 United Kingdom 1,493 19 564 Germany 1,048 0 244 Canada 1,022 0 411 Pakistan 824 0 168 France 539 0 53 Indonesia 493 0 131 Spain 487 0 28 Netherlands 371 0 108
Recommendations
1 Always set the limit to 1000.
The default is 100, and the total_count field in the response only reflects rows actually returned, not the true total. With the default, countries are silently dropped. For dataforseo.com (102 locale rows), the default hides two. For a large site like wikipedia.org, over a hundred locales exist and the default would miss most of them.
2 Per-country means aggregating languages.
A single country can appear multiple times, once per supported language. The United States returns separate rows for English and Spanish. Belgium returns French, Dutch, and German. Do not report a country twice. Sum the etv and count values across all rows sharing the same location_code, as the script above does.
3 Resolving codes to country names.
location_code values are numeric and not self-explanatory. The locations_and_languages endpoint (a free GET request, cost 0) returns the full mapping of code to country name, ISO code, and supported languages. Call it once and cache the result, as it rarely changes.
4 Know what ETV actually is.
ETV (Estimated Traffic Volume) is calculated as click-through-rate multiplied by search volume, summed across every keyword the domain ranks for in that locale. It is an estimate of monthly visits from search, not a measurement. See our help center article “How is ETV calculated” for the full methodology.
5 Estimated paid traffic cost.
This is the USD you would need to spend on Google Ads to buy the same traffic volume the domain gets organically. It is calculated as organic ETV multiplied by paid CPC. It is useful for sizing the monetary value of a domain’s organic presence in each market.
6 Data freshness.
The underlying database updates weekly. Use the DataForSEO Labs Status endpoint to see when Google data was last refreshed before quoting figures.
7 Rate limits.
You can send up to 2000 API calls per minute, with a maximum of 30 concurrent requests. Each Live call accepts only one domain, so to compare competitors, run sequential or parallel calls (within the concurrency limit), one per domain.