Semrush Gateway API
One unified API for Semrush data — backlinks, referring domains, ranked keywords, keyword research, and bulk analysis — behind a single key. Requests are load-balanced across a pool of independent data sources with automatic failover, so you get one stable endpoint instead of juggling many.
Overview
Every endpoint lives under https://api.seo1.us/v1/ and
speaks JSON (a few also return CSV). Send your API key in the
X-API-Key header on every request. That's the whole
contract — pick an endpoint, POST a JSON body, read the result.
- Automatic failover. If one data source is busy or down, the gateway transparently retries the next one. You never see the plumbing.
- One key, all endpoints. Backlinks, keywords, and rankings all share the same key and base URL.
- CORS-enabled.
/v1/*accepts cross-origin browser requests (no credentials), so you can call it directly from a frontend. - No Semrush login. You never touch Semrush accounts or sessions — just the gateway.
Authentication
Pass your key in the X-API-Key header on every
/v1/* request. Keys are prefixed with
smg_. A missing or invalid key returns
401.
curl https://api.seo1.us/v1/me/usage \ -H "X-API-Key: smg_your_api_key"
The public /health
endpoint is the only one that needs no key.
Quick start
Pull the organic keywords a domain ranks for, in three languages:
curl -X POST https://api.seo1.us/v1/ranked-keywords \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com", "database": "us", "page_size": 50}'
import requests
BASE = "https://api.seo1.us"
KEY = "smg_your_api_key"
r = requests.post(
f"{BASE}/v1/ranked-keywords",
headers={"X-API-Key": KEY},
json={"domain": "example.com", "database": "us", "page_size": 50},
timeout=60,
)
r.raise_for_status()
print(r.json())
const res = await fetch("https://api.seo1.us/v1/ranked-keywords", {
method: "POST",
headers: {
"X-API-Key": "smg_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({ domain: "example.com", database: "us", page_size: 50 }),
});
const data = await res.json();
console.log(data);
Rate limits & quotas
Each key has a per-minute rate limit (sliding 60-second window) and a daily quota (resets at 00:00 UTC). Both are set when the key is issued.
429Over the per-minute limit → {"error": "Rate limit exceeded"} with a Retry-After header (seconds to wait).429Over the daily quota → {"error": "Daily quota exceeded"}. Resets at UTC midnight.
Every /v1/* call counts toward your quota whether it
succeeds or fails — except
/v1/me/usage,
which is free. Check your current usage any time:
curl https://api.seo1.us/v1/me/usage \ -H "X-API-Key: smg_your_api_key"
Response headers
Every /v1/* response carries gateway metadata headers:
X-Request-IdUnique ID for this request. Include it when contacting support about a specific call.X-ProviderOpaque identifier of the data source that served the request. Useful for support; not something you need to act on.X-Failover-CountHow many data sources were tried before one succeeded. 0 = served on the first try.X-Total-AvailableOn large/CSV responses: total rows available upstream for the query.X-Rows-ReturnedOn large/CSV responses: rows actually returned in this response.Retry-AfterOn 429: seconds to wait before retrying.Errors
Errors return a JSON body of the shape {"error": "..."} with a matching HTTP status.
| Status | Meaning | What to do |
|---|---|---|
| 401 | Missing or invalid API key | Check the X-API-Key header. |
| 422 | Validation error in the request body (missing field, >200 targets, bad value) | Fix the body. The response names the offending field. |
| 429 | Rate limit or daily quota exceeded | Back off; honor Retry-After. |
| 503 | Every capable data source failed or was unavailable | Retry shortly. Body includes attempts[] and request_id. |
A 503 means the gateway already tried every data source that supports the endpoint and none answered — it is rare and usually transient.
{
"error": "All providers failed or unavailable",
"attempts": [
{"provider": "a", "status": 503, "error": "upstream busy"},
{"provider": "b", "status": 502, "error": "bad gateway"}
],
"request_id": "1717df10717f43f2b37703b16e8cb6b7"
}
Bulk analysis
Backlink-overview counters for up to 200 domains or URLs in a single call — authority score, total backlinks, referring domains, follow/nofollow split, and outbound counts. This is the most efficient endpoint for profiling many targets at once; a 200-target batch returns in roughly 10–20 seconds.
Returns one result row per requested target, in request order.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetsrequired | array | — | 1–200 items, each { target, target_type }. |
| targets[].targetrequired | string | — | Domain or full URL. |
| targets[].target_type | string | root_domain | root_domain | domain | url |
| concurrency | int | 8 | 1–20 (compatibility; the batch is one upstream call). |
curl -X POST https://api.seo1.us/v1/bulk-analysis \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"targets": [
{"target": "amazon.com", "target_type": "root_domain"},
{"target": "apple.com", "target_type": "root_domain"},
{"target": "shopify.com", "target_type": "root_domain"}
]
}'
{
"success": true,
"count": 1,
"results": [
{
"target": "apple.com",
"target_type": "root_domain",
"authority_score": 100,
"total_backlinks": 6162246181,
"referring_domains": 5761755,
"follow": 4879470067,
"nofollow": 1262596675,
"text": 3206326108,
"image": 2136212652,
"ips": 1318698,
"outbound_links": 16861408,
"outbound_domains": 905859,
"error": null
}
],
"request_id": "5846269872b6438284f292770c458d35"
}
error string — the call as a whole still returns
200. For more than 200 targets, split into batches of 200.
The same bulk-analysis data, returned as a CSV file in one request.
Query parameters
| Param | Default | Notes |
|---|---|---|
| targetsrequired | — | Comma- or newline-separated, max 200. |
| target_type | root_domain | Applied to all targets. |
| concurrency | 8 | 1–20. |
curl -o bulk.csv \ "https://api.seo1.us/v1/bulk-analysis/export?targets=amazon.com,apple.com,shopify.com" \ -H "X-API-Key: smg_your_api_key"
Columns: target, target_type, authority_score, total_backlinks,
referring_domains, follow, nofollow, text, image, ips, outbound_links,
outbound_domains, error.
Backlinks
The list of inbound backlinks pointing at a target, with per-link authority, anchor text, and follow status.
Returns target-level totals plus a backlinks[] array.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetrequired | string | — | Domain or URL. |
| target_type | string | root_domain | root_domain | domain | url |
| limit | int | 100 | Rows to return. |
| offset | int | 0 | Pagination offset. |
| sort_field | string | page_ascore | Field to sort by. |
| sort_type | string | desc | asc | desc |
curl -X POST https://api.seo1.us/v1/backlinks \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"target": "example.com", "target_type": "root_domain", "limit": 100}'
Each row in backlinks[] includes
source_url, anchor,
is_nofollow, source_ascore,
and domain_ascore, alongside top-level
authority_score, total_backlinks,
and referring_domains.
The full backlinks dataset as a CSV file, in one request.
Query parameters
| Param | Default | Notes |
|---|---|---|
| targetrequired | — | Domain or URL. |
| target_type | root_domain | root_domain | domain | url |
| export_type | backlinks | backlinks | backlinks_refdomains | backlinks_anchors |
| sort_field | — | Field to sort by. |
| sort_type | desc | asc | desc |
| per_domain | — | 1 = one row per referring domain. |
curl -o backlinks.csv \ "https://api.seo1.us/v1/backlinks/export?target=example.com&target_type=root_domain&export_type=backlinks" \ -H "X-API-Key: smg_your_api_key"
Referring domains
Backlinks aggregated to the domain level — one row per referring domain instead of per link.
Same body shape as /v1/backlinks; defaults to sorting by domain_ascore.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetrequired | string | — | Domain or URL. |
| target_type | string | root_domain | root_domain | domain | url |
| limit | int | 100 | Rows to return. |
| offset | int | 0 | Pagination offset. |
| sort_field | string | domain_ascore | Field to sort by. |
| sort_type | string | desc | asc | desc |
curl -X POST https://api.seo1.us/v1/referring-domains \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"target": "example.com", "target_type": "root_domain", "limit": 100}'
Each row includes domain, domain_ascore,
backlinks_num, ip,
country, first_seen, and
last_seen.
The top N referring domains (default and max 1000), ordered by domain_ascore descending. CSV by default, or JSON with format=json.
The native upstream CSV export is disabled by every data source, so the gateway assembles this itself: it paginates the working POST /v1/referring-domains endpoint (100 rows per page, offsets 0…900) through the load-balancer, then dedupes, sorts, and truncates to max_rows. You get the highest-authority referring domains first.
Query parameters
| Param | Default | Notes |
|---|---|---|
| targetrequired | — | Domain or URL. |
| target_type | root_domain | root_domain | domain | url |
| max_rows | 1000 | Rows to return. Hard cap 1000. |
| sort_field | domain_ascore | Field to sort by. |
| sort_type | desc | asc | desc |
| format | csv | csv | json |
curl -o referring_domains.csv \ "https://api.seo1.us/v1/referring-domains/export?target=backlinko.com&target_type=root_domain" \ -H "X-API-Key: smg_your_api_key"
CSV (default): text/csv attachment. Columns, in order:
domain, domain_ascore,
backlinks_num, ip,
country, first_seen,
last_seen.
JSON (format=json):
{ success, target, target_type, total_available, returned, referring_domains[], request_id }.
Response headers:
X-Source: paginated,
X-Provider: composite,
X-Total-Available (e.g. 73370),
X-Rows-Returned, and
X-Partial: true only if an internal page hard-failed.
Small sites stop early once a page returns fewer than 100 rows.
Indexed pages
The pages on a target that have received backlinks — useful for finding a site's most-linked URLs.
The gateway automatically routes this to a data source that supports it.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetrequired | string | — | Domain or URL. |
| target_type | string | root_domain | root_domain | domain | url |
| limit | int | 100 | Rows to return. |
| broken_only | bool | false | Only return pages that 4xx/5xx. |
curl -X POST https://api.seo1.us/v1/indexed-pages \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"target": "example.com", "target_type": "root_domain", "limit": 100}'
Anchors
The distribution of anchor text across a target's backlinks.
The gateway automatically routes this to the data source that supports anchor data.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetrequired | string | — | Domain or URL. |
| target_type | string | root_domain | root_domain | domain | url |
| limit | int | 100 | Rows to return. |
| offset | int | 0 | Pagination offset. |
curl -X POST https://api.seo1.us/v1/anchors \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"target": "example.com", "target_type": "root_domain", "limit": 100}'
Outbound domains
The external domains a site links out to, ranked by authority.
Returns top-level overview counts plus an outbound_domains[] array.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| domainrequired | string | — | Domain to inspect. |
| limit | int | 100 | Rows to return. |
| sort_field | string | firsttime | Field to sort by. |
| sort_type | string | desc | asc | desc |
curl -X POST https://api.seo1.us/v1/outbound-domains \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com", "limit": 100}'
Includes top-level authority_score,
referring_domains, total_backlinks,
and outbound_links.
The outbound domains a site links out to (default and max 1000), ordered by domain_ascore descending. CSV by default, or JSON with format=json.
The native upstream CSV export is disabled by every data source, so the gateway assembles this from the working POST /v1/outbound-domains endpoint. That POST caps at 100 rows/call and ignores pagination, but different sort orders return different 100-row slices — so for max_rows > 100 the gateway fans out across 8 sort orders concurrently and unions the distinct domains (verified: full set recovered), then dedupes, sorts by your sort_field/sort_type, and truncates to max_rows. Requests with max_rows ≤ 100 take a single-call fast path. Quota counts once.
Query parameters
| Param | Default | Notes |
|---|---|---|
| domainrequired | — | Domain to inspect (alias: target). |
| max_rows | 1000 | Rows to return. Hard cap 1000. |
| sort_field | domain_ascore | Field to sort by. |
| sort_type | desc | asc | desc |
| format | csv | csv | json |
curl -o outbound_domains.csv \ "https://api.seo1.us/v1/outbound-domains/export?domain=hasselblad.com" \ -H "X-API-Key: smg_your_api_key"
CSV (default): text/csv attachment. Columns, in order:
domain, domain_ascore,
links_num, category,
first_seen, last_seen.
JSON (format=json):
{ success, domain, total_available, returned, outbound_domains[], request_id }.
Response headers:
X-Source (multi-sort-union or single-call),
X-Provider: composite,
X-Total-Available,
X-Rows-Returned, and
X-Partial: true if a sort slice failed.
Backlinks gap
Compare the backlink profiles of 2–5 domains — find referring domains that link to competitors but not to you.
Returns a domains[] array with per-target backlink counts. Backed only by NoxTools — retry after ~60s if rate-limited.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetsrequired | string[] | — | 2–5 domains. First is "your" domain. |
| target_types | string[] | all root_domain | Per-domain type (root_domain | domain). |
| gap_type | string | all | best | weak | strong | shared | unique | all |
| limit | int | 100 | Rows to return. |
| offset | int | 0 | Pagination offset. |
| sort_field | string | matchesnum | Field to sort by. |
| sort_type | string | desc | asc | desc |
| sort_target | int | 0 | Index of target domain to sort by. |
| authority_score_min | int | — | Min authority-score filter. |
| authority_score_max | int | — | Max authority-score filter. |
| countries | string[] | — | Country codes, e.g. ["us","gb","au"]. |
| display_filter | string | — | Raw display_filter override (advanced). |
curl -X POST https://api.seo1.us/v1/backlinks-gap \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"targets": ["backlinko.com", "ahrefs.com"], "gap_type": "all", "limit": 2}'
Each row in domains[] includes domain,
domain_ascore, matches_num,
backlinks_num (per-target counts in targets order),
and traffic.
Ranked keywords
The organic keywords a domain ranks for in a given Semrush database (country).
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| domainrequired | string | — | Domain to analyze. |
| database | string | us | Country code — see country codes. |
| search_type | string | domain | domain | subdomain | url | subfolder |
| page | int | 1 | Page number. |
| page_size | int | 100 | Rows per page (max 200). |
| order_by | string | trafficPercent | Field to sort by. |
| order_dir | string | desc | asc | desc |
curl -X POST https://api.seo1.us/v1/ranked-keywords \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"database": "us",
"page_size": 100,
"order_by": "trafficPercent",
"order_dir": "desc"
}'
database has data for a domain? Call
/v1/top-countries
first to see where it has traffic.
Keyword research
The Keyword Magic Tool — expand a seed phrase into related keywords with volume, difficulty, CPC, intent, and SERP features.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| phraserequired | string | — | Seed keyword. |
| database | string | us | Country code — see country codes. |
| page_size | int | 100 | Rows per page. |
| max_pages | int | 1 | Up to 300 (≈ 30,000 rows). |
| match_type | string | broad | broad | phrase | exact | related | all |
| questions_only | bool | false | Return only question keywords. |
| filters | object | — | See below. |
filters object
All optional: volume_min/volume_max,
kd_min/kd_max,
cpc_min/cpc_max,
word_count_min/word_count_max,
include_phrases[], exclude_phrases[],
intent[], serp_features[].
curl -X POST https://api.seo1.us/v1/keyword-research \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phrase": "running shoes",
"database": "us",
"match_type": "broad",
"max_pages": 2,
"filters": { "volume_min": 500, "kd_max": 40 }
}'
The same keyword data, streamed back as text/csv. Takes the same body minus page/page_size; defaults to max_pages: 10.
curl -o keywords.csv -X POST https://api.seo1.us/v1/keyword-research/csv \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"phrase": "running shoes", "database": "us", "max_pages": 10}'
Response headers include X-Total-Available and
X-Rows-Returned so you can tell how much data was
available versus returned.
Top countries
Where a domain has organic traffic, broken down by Semrush database. The fastest way to pick the right database before a ranked-keywords call.
Returns a sorted countries[] array with per-database traffic and positions.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| domainrequired | string | — | Domain to inspect. |
curl -X POST https://api.seo1.us/v1/top-countries \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'
Keyword gap
Compare the organic keyword profiles of 2–5 domains — find keywords competitors rank for that you don't.
Native-first with automatic composite fallback. The native upstream (NoxTools) is tried first; it is often Semrush rate-limited. When native is unavailable the gateway transparently computes the gap by diffing ranked-keywords (served by all 5 providers), so the endpoint stays reliable. The path used is reported via the X-Gap-Source header (native | composite) and the source field (composite only). Quota counts once per request regardless of internal calls.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| targetsrequired | string[] | — | 2–5 domains. First is "your" domain. Alias: target + competitors. |
| database | string | us | Country code — see country codes. |
| type | int | 7 | Native only. 1=Shared 2=Missing 3=Weak 4=Strong 5=Untapped 6=Unique 7=All |
| limit | int | 100 | Rows to return (composite caps at 200). |
| offset | int | 0 | Pagination offset (native only). |
| order_by | string | volume | Field to sort by (native only). |
| order_dir | string | desc | asc | desc (native only). |
| filters | string | "" | Raw Semrush filter string (native only). |
| min_volume | int | 0 | Composite only — drop gap keywords below this volume. |
curl -X POST https://api.seo1.us/v1/keyword-gap \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"targets": ["backlinko.com", "ahrefs.com"], "database": "us", "type": 2, "limit": 2}'
Native response (X-Gap-Source: native) — passed through verbatim.
Each row in keywords[] includes keyword,
volume, keyword_difficulty,
cpc, competition,
intents, plus positions and
urls (per-target, in targets order).
Composite response (X-Gap-Source: composite) — the keywords
any competitor ranks for that the target (first domain) does not. Adds top-level
source, target, competitors
and a note. Each row in keywords[] includes
phrase, volume,
keyword_difficulty, cpc,
intents, plus competitor,
competitor_position (best/lowest) and url.
If the target's own ranked-keywords lookup fails, the response adds
target_ranked_unavailable: true (diff is incomplete). If
ranked-keywords fails for every domain, you get the standard 503.
Bulk keyword volume
Look up search volume and metrics for up to 2000 keywords in a single request.
Returns a keywords[] array plus total_volume.
Body parameters
| Field | Type | Default | Notes |
|---|---|---|---|
| keywordsrequired | string[] | — | 1–2000 keyword phrases. |
| database | string | us | Country code — see country codes. |
curl -X POST https://api.seo1.us/v1/bulk-keyword-volume \
-H "X-API-Key: smg_your_api_key" \
-H "Content-Type: application/json" \
-d '{"keywords": ["cnc machining", "metal fabrication"], "database": "us"}'
Each row in keywords[] includes phrase,
volume, cpc, competition,
keyword_difficulty, intents,
serp_features, trends, and
click_potential. Top-level: requested,
returned, total_volume.
Account usage
Your key's current usage against its limits. Authenticated, but does not count toward your quota.
curl https://api.seo1.us/v1/me/usage \ -H "X-API-Key: smg_your_api_key"
{
"key_name": "Acme Corp",
"requests_today": 137,
"daily_quota": 5000,
"rate_limit_per_min": 60,
"requests_total": 18452
}
Health
A public status check — no API key required. Returns overall status and a per-source summary.
curl https://api.seo1.us/health
{
"status": "ok",
"providers": [
{"name": "a", "enabled": true, "healthy": true, "breaker": "closed", "in_flight": 0},
{"name": "b", "enabled": true, "healthy": true, "breaker": "closed", "in_flight": 2}
]
}
As long as status is ok and at least one source is healthy, the API serves requests — failover handles the rest.
Country (database) codes
Endpoints that take a database accept standard Semrush country codes. Common ones:
us uk au ca in jp nl sg my de fr es it br mx se hk id
If you're unsure which database has data for a domain, call /v1/top-countries first.
Semrush Gateway · unified Semrush data API · automatic failover across multiple sources. Questions or a new key? Contact the gateway administrator.