Skip to navigation

SEO API

Twelve tools give an application or an agent SEO data: how much a keyword is searched and how hard it is to rank for, which keywords and pages bring a domain its search traffic, who competes with it, who links to it, its Domain Rating, and how many visits the site gets. Each tool is one POST under /v1/seo/, uses your BeatAPI API key and answers in one response. Agents reach the same tools as capabilities.

Quick start

curl https://api.beatapi.io/v1/seo/keyword-overview \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"keywords":["redis cluster"],"country":"us"}'
{
"object": "seo.keyword_overview",
"request_id": "task_xxx",
"country": "us",
"language": "en",
"results": [
{
"keyword": "redis cluster",
"search_volume": 590,
"cpc_usd": 15.62,
"competition": 0.17,
"keyword_difficulty": 15,
"intent": "informational",
"updated_at": "2026-10-16 01:53:48 +00:00",
"monthly_searches": [{ "year": 2026, "month": 9, "search_volume": 590 }]
}
],
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0183",
"price_version": "web_3f9a1c2b7d10"
}
}

Authentication

Send your BeatAPI API key on every call as Authorization: Bearer <key>; the key works with or without the sk- prefix. Create one in Dashboard → API Keys. All tools live on https://api.beatapi.io and spend your account balance.

Choose a tool

You wantTool
Volume, CPC, difficulty, intent and monthly history of keywords you already havekeyword-overview
New keywords around a topickeyword-ideas
Only the difficulty of a list of keywordskeyword-difficulty
A domain’s search footprint in one marketdomain-overview
The keywords a domain ranks for, or its position for one keywordranked-keywords
A domain’s pages by search traffictop-pages
Domains competing for the same keywordscompetitors
A domain’s link profile in numbersbacklink-summary
The links themselvesbacklinks

For Domain Rating and total site visits, see Domain Rating and site traffic.

There is no live results-page tool. To check a domain’s position for a keyword, call ranked-keywords with contains. That is the last recorded position, not a live one.

Country and language

country and language choose the market: Google search in that country, in that language. The keyword tools, domain-overview, ranked-keywords, top-pages and competitors take them. The backlink tools have no market and reject both fields.

FieldDefaultRule
countryusTwo-letter lowercase code, such as us, gb, de or jp. About 90 markets are covered. China (cn) is not.
languageThe market’s main languageOne of the market’s languages, such as es in us, fr in ca or zh-TW in hk.

A country or language outside the list is rejected with 400 bad_request, and the message says what the market has: language 'de' is not covered in us; use one of: en, es.

Limit and offset

keyword-ideas, ranked-keywords, top-pages, competitors and backlinks return pages of rows.

FieldDefaultRange
limit1001–1000 rows, 1–999 on competitors. Every row returned is billed, so ask for what you need.
offset00–10000 rows to skip. Add limit to it for the next page.

These responses repeat limit and offset, and carry total, the rows available in all, when it is known.

Responses

Every tool answers synchronously: the result is in the response to your POST, so there is nothing to poll. Most calls take a few seconds.

FieldDescription
objectThe tool, such as seo.ranked_keywords.
request_idQuote it in support requests. GET /v1/tasks/{id} with this id returns the same result under output, so a lost response is not a lost result.
country, languageThe market that was used, on tools that take one.
domainThe domain that was used, on tools that take one. A full URL is reduced to its domain and a leading www. is dropped.
results[]The rows, on tools that return a list. Other tools put their fields at the top level.
billed_rowsThe rows this call was charged for.
has_dataOn domain-overview, backlink-summary and the Domain Rating and site-traffic tools. false means the domain is not on record.
usageEnds each response: what the call cost.

usage.price_usd is the settled charge in US dollars. billing_unit is always request and quantity always 1; billed_rows says how many rows the charge covers.

A number the data does not have is left out of the row, so check for the field before using it. Unknown request fields are rejected with 400 bad_request: a misspelt field that was silently dropped would answer a different question from the one you asked.

Keyword tools

Keyword overview

POST https://api.beatapi.io/v1/seo/keyword-overview

Volume, CPC, competition, difficulty, intent and monthly history for keywords you already have. A keyword nobody searches comes back missing, not as zero.

FieldTypeRequiredDescription
keywordsstring[]Yes1–100 keywords of up to 80 characters each. They are lowercased, and duplicates count once.
countrystringNoSee Country and language.
languagestringNoSee Country and language.

The response is in the quick start. Fields of each row:

FieldDescription
keywordThe keyword.
search_volumeMonthly searches in this market.
cpc_usdCost per click of paid ads, in US dollars.
competitionAdvertiser competition, from 0 to 1.
keyword_difficultyHow hard the keyword is to rank for, from 0 to 100.
intentMain search intent, such as informational or navigational.
updated_atWhen this keyword’s data was last refreshed.
monthly_searches[]year, month and search_volume for each month. Only on keyword-overview.

Keyword ideas

POST https://api.beatapi.io/v1/seo/keyword-ideas

Related keywords for your seed keywords, most relevant first. Rows have the fields of keyword-overview without monthly_searches.

FieldTypeRequiredDescription
keywordsstring[]Yes1–20 seed keywords of up to 80 characters each.
limit, offsetintegerNoSee Limit and offset.
country, languagestringNoSee Country and language.
curl https://api.beatapi.io/v1/seo/keyword-ideas \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"keywords":["redis sentinel"],"limit":20}'

Keyword difficulty

POST https://api.beatapi.io/v1/seo/keyword-difficulty

Only keyword_difficulty, for many keywords at once. It takes the same fields as keyword-overview.

{
"object": "seo.keyword_difficulty",
"request_id": "task_xxx",
"country": "us",
"language": "en",
"results": [
{ "keyword": "redis cluster", "keyword_difficulty": 15 },
{ "keyword": "redis sentinel", "keyword_difficulty": 2 }
],
"billed_rows": 2,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0186",
"price_version": "web_3f9a1c2b7d10"
}
}

Domain tools

All four take a domain such as example.com, plus country and language.

Domain overview

POST https://api.beatapi.io/v1/seo/domain-overview

A domain’s organic and paid search footprint in one market. This is search traffic only, not total visits to the site.

curl https://api.beatapi.io/v1/seo/domain-overview \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"redis.io","country":"us"}'
{
"object": "seo.domain_overview",
"request_id": "task_xxx",
"country": "us",
"language": "en",
"domain": "redis.io",
"has_data": true,
"organic": {
"ranking_keywords": 22125,
"search_traffic_estimate": 108626.58,
"traffic_value_usd_estimate": 483101.98,
"rank_distribution": { "1": 987, "2-3": 491, "4-10": 2245, "11-20": 5678, "21-30": 3224, "31-100": 9500 },
"new": 18919,
"up": 1048,
"down": 1105,
"lost": 4041
},
"paid": { "ranking_keywords": 0, "search_traffic_estimate": 0 },
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0183",
"price_version": "web_3f9a1c2b7d10"
}
}
FieldDescription
ranking_keywordsKeywords ranking in this market.
search_traffic_estimateEstimated monthly search traffic.
traffic_value_usd_estimateEstimated cost of buying that traffic as ads, in US dollars.
rank_distributionKeywords by position: 1, 2-3, 4-10, 11-20, 21-30 and 31-100.
new, up, down, lostKeywords that recently started ranking, moved up, moved down or stopped ranking.

A domain with nothing on record comes back with has_data: false and neither organic nor paid. That is no data, not zero traffic, and the call is billed.

Ranked keywords

POST https://api.beatapi.io/v1/seo/ranked-keywords

The keywords a domain ranks for in one market, highest search volume first.

FieldTypeRequiredDescription
domainstringYesThe domain.
containsstringNoOnly keywords containing this text, up to 80 characters, ignoring case. % and _ are rejected.
limit, offsetintegerNoSee Limit and offset.
country, languagestringNoSee Country and language.
curl https://api.beatapi.io/v1/seo/ranked-keywords \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"redis.io","contains":"cluster","limit":1}'
{
"object": "seo.ranked_keywords",
"request_id": "task_xxx",
"country": "us",
"language": "en",
"domain": "redis.io",
"limit": 1,
"offset": 0,
"total": 108,
"results": [
{
"keyword": "redis cluster",
"search_volume": 590,
"cpc_usd": 15.62,
"competition": 0.17,
"keyword_difficulty": 15,
"intent": "informational",
"updated_at": "2026-10-16 01:53:48 +00:00",
"position": 1,
"absolute_position": 2,
"url": "https://redis.io/docs/cluster/",
"title": "Redis cluster",
"position_seen_at": "2026-09-14 04:53:47 +00:00",
"search_traffic_estimate": 12.35
}
],
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0183",
"price_version": "web_3f9a1c2b7d10"
}
}

Rows add these fields to those of keyword-ideas:

FieldDescription
positionPosition among the organic results.
absolute_positionPosition counting everything on the results page.
url, titleThe page that ranks.
position_seen_atWhen that position was recorded. Positions are a snapshot and can be weeks or months old.
search_traffic_estimateEstimated monthly search traffic this keyword brings the page.

Top pages

POST https://api.beatapi.io/v1/seo/top-pages

A domain’s pages, highest estimated search traffic first. Fields: domain, limit, offset, country, language. Each row has a url and the footprint fields of that page.

{
"object": "seo.top_pages",
"request_id": "task_xxx",
"country": "us",
"language": "en",
"domain": "redis.io",
"limit": 1,
"offset": 0,
"total": 1104,
"results": [
{
"url": "https://redis.io/",
"ranking_keywords": 22125,
"search_traffic_estimate": 108626.58,
"traffic_value_usd_estimate": 483101.98,
"rank_distribution": { "1": 987, "2-3": 491, "4-10": 2245, "11-20": 5678, "21-30": 3224, "31-100": 9500 },
"new": 18919,
"up": 1048,
"down": 1105,
"lost": 4041
}
],
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0183",
"price_version": "web_3f9a1c2b7d10"
}
}

Competitors

POST https://api.beatapi.io/v1/seo/competitors

Domains competing with a domain for the same search keywords. Fields: domain, limit, offset, country, language. The domain itself and the largest general sites, such as youtube.com and wikipedia.org, are left out of the rows, and total counts what is left.

{
"object": "seo.competitors",
"request_id": "task_xxx",
"country": "us",
"language": "en",
"domain": "redis.io",
"limit": 1,
"offset": 0,
"total": 9,
"results": [
{
"domain": "example.com",
"shared_keywords": 4706,
"avg_position": 29.6,
"ranking_keywords": 22125,
"search_traffic_estimate": 108626.58
}
],
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0183",
"price_version": "web_3f9a1c2b7d10"
}
}
FieldDescription
domainThe competing domain.
shared_keywordsKeywords both domains rank for.
avg_positionIts average position on those keywords.
ranking_keywordsKeywords it ranks for in all.
search_traffic_estimateIts estimated monthly search traffic.

POST https://api.beatapi.io/v1/seo/backlink-summary

A domain’s link profile. The only field is domain.

{
"object": "seo.backlink_summary",
"request_id": "task_xxx",
"domain": "redis.io",
"has_data": true,
"link_authority": 500,
"backlinks": 920428,
"referring_domains": 32647,
"referring_main_domains": 28596,
"referring_pages": 748383,
"referring_ips": 19191,
"broken_backlinks": 8482,
"broken_pages": 2773,
"spam_score": 28,
"first_seen": "2019-01-15 22:56:44 +00:00",
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0352",
"price_version": "web_3f9a1c2b7d10"
}
}
FieldDescription
link_authorityAuthority of the link profile, from 0 to 1000 on this data set’s own scale.
backlinksLinks found.
referring_domains, referring_main_domainsDomains linking to it, and the same with subdomains counted as one.
referring_pages, referring_ipsPages and IP addresses linking to it.
broken_backlinks, broken_pagesLinks that lead nowhere, and the domain’s pages that no longer work.
spam_scoreSpam score of the profile. Higher means more likely spam.
first_seenWhen a link to the domain was first seen.

has_data is false when the domain is not on record, with no other fields, and the call is billed.

POST https://api.beatapi.io/v1/seo/backlinks

The links pointing at a domain, strongest first.

FieldTypeRequiredDescription
domainstringYesThe domain.
one_per_domainbooleanNoDefaults to true: one link per linking domain. Set false to get every link.
limit, offsetintegerNoSee Limit and offset.
{
"object": "seo.backlinks",
"request_id": "task_xxx",
"domain": "redis.io",
"limit": 1,
"offset": 0,
"total": 28843,
"results": [
{
"source_url": "https://blog.example.com/caching",
"source_domain": "blog.example.com",
"source_title": "Caching notes",
"source_authority": 493,
"source_country": "CZ",
"target_url": "https://redis.io/",
"anchor": "Redis",
"link_type": "anchor",
"dofollow": true,
"spam_score": 3,
"first_seen": "2025-04-26 07:12:33 +00:00",
"last_seen": "2026-06-18 19:14:30 +00:00",
"is_new": false,
"is_lost": false,
"is_broken": false
}
],
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.0352",
"price_version": "web_3f9a1c2b7d10"
}
}
FieldDescription
source_url, source_domain, source_titleThe page that links.
source_authorityLink authority of the linking domain, from 0 to 1000.
source_countryCountry of the linking domain.
target_url, anchorThe page linked to, and the link text.
link_typeKind of link, such as anchor.
dofollowWhether the link passes authority.
spam_scoreSpam score of this link. Higher means more likely spam.
first_seen, last_seenWhen the link was first and last seen.
is_new, is_lost, is_brokenWhether the link is new, has gone, or leads nowhere.

Reading the numbers

  • Search traffic. search_traffic_estimate is estimated search traffic, not total visits to a site.
  • Estimates. Traffic and value figures are estimates, not measurements of the site.
  • Link authority. link_authority runs from 0 to 1000 on this data set’s own scale.
  • Keyword difficulty is this data set’s own scale. Do not compare it with another tool’s score.
  • Missing is not zero. A number that is missing is unknown, never zero.
  • No live results page. There is no live results-page tool. To check a domain’s position for a keyword, use ranked-keywords with contains. That is the last recorded position, not a live one.
  • Domain Rating and site traffic have their own notes in Reading these numbers.

Billing

ToolPer callPer row returned
keyword-overview, keyword-ideas, keyword-difficulty, domain-overview, ranked-keywords, top-pages, competitors$0.018$0.0003
backlink-summary, backlinks$0.035$0.0002

Domain Rating and site traffic have their own prices.

  • domain-overview and backlink-summary return one record, which counts as one row, and none when has_data is false. A call that returns 100 rows of ranked-keywords costs $0.048.
  • A call that finds nothing still costs its per-call part. A rejected request (400) costs nothing, and neither does a failed call (503).
  • Before a call runs, your balance must cover the most it can cost: every row of limit, or every keyword sent. You are then charged only for what came back.
  • Send an Idempotency-Key header when your client may retry the same request. Reusing a key with a different body is rejected with 409 idempotency_conflict.

Agents

There is no separate MCP tool for SEO data. On the BeatAPI MCP endpoint https://beatapi.io/mcp, agents use capabilities_search, capabilities_inspect and capabilities_run with these references:

ReferenceTool
data:seo.keyword_overviewkeyword-overview
data:seo.keyword_ideaskeyword-ideas
data:seo.keyword_difficultykeyword-difficulty
data:seo.domain_overviewdomain-overview
data:seo.ranked_keywordsranked-keywords
data:seo.top_pagestop-pages
data:seo.competitorscompetitors
data:seo.backlink_summarybacklink-summary
data:seo.backlinksbacklinks

The references for Domain Rating and site traffic are listed with those tools.

Inspect shows the input schema and the current price. input is the request body of the tool, and Run answers with the result:

{ "reference": "data:seo.ranked_keywords", "operation": "start", "input": { "domain": "redis.io", "contains": "cluster", "limit": 20 } }

The same three calls work over REST at POST /v1/capabilities/search, /v1/capabilities/inspect and /v1/capabilities/run. See Capabilities API.

Errors

Errors use the standard BeatAPI error envelope, with retryable on every error. See Errors for the retry policy.

{
"error": {
"code": "bad_request",
"message": "country 'cn' is not covered; use a two-letter code such as us, gb, de or jp (China is not covered)",
"retryable": false,
"request_id": "req_abc123"
}
}
HTTP statusCodeMeaning
400bad_requestAn invalid or unknown field, a keyword or domain that is not valid, or a market that is not covered. The message names the field.
401missing_api_key / invalid_api_keyNo key was sent, or the key is wrong, expired, disabled, or exhausted.
402insufficient_creditsInsufficient balance: the balance is zero or below the price of the call.
403forbiddenThe key or account is not permitted to make this call.
409idempotency_conflictThe Idempotency-Key was reused with a different body.
429rate_limit_exceededToo many requests. Wait retry_after_seconds.
500internal_errorUnexpected internal error. Retryable.
503processing_unavailable, processing_timeout or processing_failedSEO data is temporarily unavailable, or the call took too long. Retry with backoff.

A 503 is never charged. BeatAPI does not answer 502.

Domain Rating and site traffic

Three more tools work the same way: same key, same answer in the response, same usage and errors. They take a domain and have no market, so country and language are rejected. There is no limit or offset.

You wantTool
The 0–100 score SEO teams compare domains bydomain-rating
That score month by monthdomain-rating-history
Total visits to a site, where they come from and how visitors behavesite-traffic

Domain Rating

POST https://api.beatapi.io/v1/seo/domain-rating

curl https://api.beatapi.io/v1/seo/domain-rating \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"redis.io"}'
{
"object": "seo.domain_rating",
"request_id": "task_xxx",
"domain": "redis.io",
"has_data": true,
"domain_rating": 90,
"link_rank": 1927,
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.005",
"price_version": "web_3f9a1c2b7d10"
}
}

domain_rating runs from 0 to 100. link_rank is the domain’s global rank by links; lower is stronger.

Domain Rating history

POST https://api.beatapi.io/v1/seo/domain-rating-history

{
"object": "seo.domain_rating_history",
"request_id": "task_xxx",
"domain": "redis.io",
"has_data": true,
"results": [
{ "month": "2025-12", "domain_rating": 89 },
{ "month": "2026-01", "domain_rating": 89 },
{ "month": "2026-02", "domain_rating": 89 },
{ "month": "2026-03", "domain_rating": 89 },
{ "month": "2026-04", "domain_rating": 89 },
{ "month": "2026-05", "domain_rating": 89 },
{ "month": "2026-06", "domain_rating": 90 },
{ "month": "2026-07", "domain_rating": 90 },
{ "month": "2026-08", "domain_rating": 90 },
{ "month": "2026-09", "domain_rating": 90 },
{ "month": "2026-10", "domain_rating": 90 }
],
"billed_rows": 11,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.044",
"price_version": "web_3f9a1c2b7d10"
}
}

An unknown domain returns has_data: false, an empty results and billed_rows: 1.

Site traffic

POST https://api.beatapi.io/v1/seo/site-traffic

Estimated total visits to a website from all channels, with sources, countries, rank and engagement.

FieldTypeRequiredDescription
domainstringYesThe domain.
monthsintegerNoMonths of history: 3 (default), 6 or 12.
curl https://api.beatapi.io/v1/seo/site-traffic \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"redis.io","months":3}'
{
"object": "seo.site_traffic",
"request_id": "task_xxx",
"domain": "redis.io",
"months": 3,
"has_data": true,
"site_visits_estimate": 734682,
"monthly_visits": [
{ "month": "2026-07", "visits": 827501 },
{ "month": "2026-08", "visits": 789909 },
{ "month": "2026-09", "visits": 734682 }
],
"global_rank": 66213,
"country_rank": { "country": "IN", "rank": 19016 },
"category": "computers_electronics_and_technology/programming_and_developer_software",
"category_rank": 634,
"bounce_rate": 0.4254,
"pages_per_visit": 3.3174,
"avg_visit_seconds": 116.2764,
"traffic_sources": {
"direct": 0.3293,
"search": 0.4836,
"paid_search": 0.0072,
"social": 0.0435,
"paid_social": 0.0026,
"referrals": 0.0992,
"mail": 0.0114,
"display_ads": 0.0035,
"affiliate": 0,
"gen_ai": 0.0197
},
"top_countries": [{ "country": "IN", "share": 0.1995 }],
"top_keywords": [{ "keyword": "redis", "search_volume": 193980, "cpc_usd": 2.78 }],
"title": "Redis - Real-time data for agents & apps",
"description": "Developers love Redis. Unlock the full potential of the Redis database with Redis Enterprise and start building blazing fast apps.",
"billed_rows": 1,
"usage": {
"billing_unit": "request",
"quantity": 1,
"price_usd": "0.005",
"price_version": "web_3f9a1c2b7d10"
}
}
FieldDescription
site_visits_estimateEstimated visits in the latest month.
monthly_visits[]month and visits for each month.
global_rankRank among all sites. Lower is bigger.
country_rankRank in one country: country and rank.
category, category_rankThe site’s category and its rank there.
bounce_rateShare of visits that leave after one page, from 0 to 1.
pages_per_visit, avg_visit_secondsPages viewed per visit and the average visit length in seconds.
traffic_sourcesShare of visits from each channel, from 0 to 1.
top_countries[]Countries sending the most visits, each with its share from 0 to 1.
top_keywords[]Keywords bringing search visits, with search_volume and cpc_usd.
title, descriptionThe site’s own title and description.

Reading these numbers

  • Two kinds of traffic. search_traffic_estimate is estimated search traffic; site_visits_estimate is total visits from all channels. They are different measures: never add or compare them. Visits are not unique visitors.
  • Two authority scores. link_authority runs from 0 to 1000 on this data set’s own scale; domain_rating runs from 0 to 100. They cannot be converted to each other.
  • Not on record. has_data: false means the domain is not on record, which differs from a rating or traffic of zero: a new site can have a rating of 0 and still have a link_rank.

Prices of these tools

ToolPrice
domain-rating$0.005 per call
domain-rating-history$0.004 per month returned
site-traffic, 3 months$0.005
site-traffic, 6 months$0.01
site-traffic, 12 months$0.015
  • domain-rating and site-traffic are billed when has_data is false. domain-rating-history for an unknown domain returns no months and costs one, $0.004.
  • A rejected request (400) and a failed call (503) cost nothing.
  • Before domain-rating-history runs, your balance must cover 24 months of history. You are then charged only for what came back.

References for agents

ReferenceTool
data:seo.domain_ratingdomain-rating
data:seo.domain_rating_historydomain-rating-history
data:seo.site_trafficsite-traffic