# SEO tools

Keywords, rankings, SERPs, domains, backlinks, and technical audits.

## Get keyword ideas

Tool: `seo_search_keywords`

Candidate keywords around a seed, with volume, difficulty, intent, CPC and trend for each. Use for "find keywords for X" or "keyword ideas for a page about X". Rows already carry the metrics, so do not call seo_get_keyword_metrics afterwards. For one query's top results use seo_get_serp.

Returns a list; each row has keyword, volume, kd (0 to 100), intent, cpc (USD), competition (0 to 1), bid_low, bid_high (Google Ads top-of-page bid range, USD), trend[12] (month, volume). null = provider has no data; 0 = measured zero.

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_search_keywords`

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "seed": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "Required. The seed keyword or topic."
    },
    "mode": {
      "default": "suggestions",
      "description": "suggestions: keywords that contain the seed. related: keywords whose results page is like the seed's. ideas: keywords in the same category.",
      "type": "string",
      "enum": [
        "suggestions",
        "related",
        "ideas"
      ]
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "limit": {
      "default": 100,
      "description": "Max rows. Default 100, cap 1000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "cursor": {
      "description": "The next page: meta.cursor from the previous result.",
      "type": "string"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "seed"
  ]
}
```

## Check keyword metrics

Tool: `seo_get_keyword_metrics`

Volume, difficulty, intent, CPC and 12-month trend for keywords you already have, with AI-engine prompt volume on request (ai_volume: true). Not for discovering keywords: use seo_search_keywords.

Returns one row per input keyword in the order given: keyword, volume, kd, intent, cpc, competition, bid_low, bid_high, trend[12], ai_volume, ai_trend[12]. With source google_ads, kd and intent are always null and keywords over 80 characters come back all null. ai_volume and ai_trend are null unless ai_volume is true. AI volume is DataForSEO's modelled estimate, not assistant logs.

Cost: 5 + 5 per 100 keywords with source labs; 24 flat with source google_ads; ai_volume adds 4 + 4 per 100 keywords. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_keyword_metrics`

Cost: 5 + 5 per 100 keywords with source labs; 24 flat with source google_ads; ai_volume adds 4 + 4 per 100 keywords. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "keywords": {
      "minItems": 1,
      "maxItems": 1000,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "Required. At most 1000."
    },
    "source": {
      "default": "labs",
      "description": "labs, or google_ads for Keyword Planner's own volume (slower; no kd or intent).",
      "type": "string",
      "enum": [
        "labs",
        "google_ads"
      ]
    },
    "ai_volume": {
      "default": false,
      "description": "Also return AI-engine prompt volume.",
      "type": "boolean"
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "keywords"
  ]
}
```

## Get search results for a query

Tool: `seo_get_serp`

The top organic results and SERP features Google shows for one query: "what ranks for X", "is there a featured snippet or AI overview for X". For one site's position use seo_get_position.

Returns features[], results[] (rank, organic_rank, type, url, domain, title, snippet, published_at, rating), related_searches[], ai_overview (text, references[]) or null, results_count, check_url. Each People Also Ask question and each forum thread is its own row at its block's rank. location localises the local pack, not the organic rows of a bare service search: put the town in the keyword.

Cost: 1 credit per 10 results, +1 with ai_overview. Default top 10 = 1 credit. Cached 24 hours.

Endpoint: `POST /api/seo/tools/seo_get_serp`

Cost: 1 credit per 10 results, +1 with ai_overview. Default top 10 = 1 credit. Cached 24 hours.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "keyword": {
      "type": "string",
      "minLength": 1,
      "maxLength": 700
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "device": {
      "default": "desktop",
      "type": "string",
      "enum": [
        "desktop",
        "mobile"
      ]
    },
    "depth": {
      "default": 10,
      "description": "How many results to fetch. Default 10, max 100.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "ai_overview": {
      "default": false,
      "description": "Also return Google's AI overview and its cited sources. +1 credit.",
      "type": "boolean"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "keyword"
  ]
}
```

## Get a site's position for a keyword

Tool: `seo_get_position`

Where one target ranks for one keyword, scanning the top 100 server-side. For every keyword a domain ranks for use seo_get_ranked_keywords.

Returns keyword, target, rank (absolute, null when not in the top 100), organic_rank, url, title, scanned_depth, above[] (rank, domain, url of the organic results ahead, at most 10).

Cost: 6 credits flat, also when the target is not in the top 100. NoData (1 credit) only when the keyword has no organic results. Cached 24 hours.

Endpoint: `POST /api/seo/tools/seo_get_position`

Cost: 6 credits flat, also when the target is not in the top 100. NoData (1 credit) only when the keyword has no organic results. Cached 24 hours.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "keyword": {
      "type": "string",
      "minLength": 1,
      "maxLength": 700
    },
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "device": {
      "default": "desktop",
      "type": "string",
      "enum": [
        "desktop",
        "mobile"
      ]
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "keyword",
    "target"
  ]
}
```

## Get a domain overview

Tool: `seo_get_domain_overview`

Traffic estimate, ranked keyword count, position spread, top pages and domain rank for one domain. The canonical source of domain rank. For the keywords behind the traffic use seo_get_ranked_keywords; for many domains use seo_get_domain_ratings or seo_get_traffic_estimates.

Returns domain_rank (0 to 1000, DataForSEO's scale, not Ahrefs DR), organic_traffic, organic_keywords, organic_traffic_cost (USD), positions (top_3, top_10, top_100), keyword_changes (new, up, down, lost), paid_traffic, paid_keywords, top_pages[] (url, organic_traffic, organic_keywords), history[] (12 months: month, organic_traffic, organic_keywords) only with history true. Traffic is a modelled estimate.

Cost: 15 credits; +56 with history. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_domain_overview`

Cost: 15 credits; +56 with history. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "history": {
      "default": false,
      "description": "Also return 12 months of organic traffic and keywords.",
      "type": "boolean"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target"
  ]
}
```

## Get a domain's ranked keywords

Tool: `seo_get_ranked_keywords`

Keywords a domain or one page ranks for in the top 100, with position, URL and metrics. For keywords a competitor has that you do not, use seo_get_keyword_gap.

Returns a list sorted by traffic: keyword, rank, type (organic, featured_snippet or local_pack), previous_rank (null = did not rank at the previous update), is_new, url, volume, kd, cpc, intent, traffic (estimated monthly visits), serp_features[]. rank counts every element on the page, so an organic top-100 keyword can read rank 104.

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_ranked_keywords`

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "limit": {
      "default": 100,
      "description": "Max rows. Default 100, cap 1000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "cursor": {
      "description": "The next page: meta.cursor from the previous result.",
      "type": "string"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target"
  ]
}
```

## Find search competitors

Tool: `seo_get_serp_competitors`

Domains that share search results with a target, with how many keywords overlap. SERP competitors, not business competitors. Use before seo_get_keyword_gap to choose competitors.

Returns a list sorted by shared keywords: domain, shared_keywords, avg_position, organic_traffic, organic_keywords, paid_traffic, paid_keywords. The target itself is excluded.

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_serp_competitors`

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "limit": {
      "default": 100,
      "description": "Max rows. Default 100, cap 1000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "cursor": {
      "description": "The next page: meta.cursor from the previous result.",
      "type": "string"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target"
  ]
}
```

## Find keyword gaps

Tool: `seo_get_keyword_gap`

Keywords one competitor ranks for in the top 100 that the target does not rank for at all: "what does competitor.com rank for that we do not".

Returns a list sorted by volume: keyword, competitor, competitor_rank, competitor_url, volume, kd, cpc, intent.

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_keyword_gap`

Cost: 5 + 5 per 100 rows. Default 100 rows = 10 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "competitor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Required. One competitor's domain. A URL is read as its host."
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "limit": {
      "default": 100,
      "description": "Max rows. Default 100, cap 1000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "cursor": {
      "description": "The next page: meta.cursor from the previous result.",
      "type": "string"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target",
    "competitor"
  ]
}
```

## Get a backlink summary

Tool: `seo_get_backlink_summary`

Link totals for a domain or page: backlinks, referring domains, dofollow share, link types and domain rank. For individual links use seo_get_backlinks; for linking sites use seo_get_referring_domains.

Returns domain_rank (equals seo_get_domain_overview's), backlinks, referring_domains, referring_main_domains, referring_ips, dofollow_share (0 to 1), broken_backlinks, broken_pages, spam_score (0 to 100), target_spam_score, first_seen, and top-10 counts by link type, attribute, placement, platform, TLD and country.

Cost: 10 credits flat. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_backlink_summary`

Cost: 10 credits flat. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "include_subdomains": {
      "default": true,
      "description": "Count links to subdomains too. Ignored for a URL target.",
      "type": "boolean"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target"
  ]
}
```

## Get backlinks

Tool: `seo_get_backlinks`

Individual backlinks pointing at a domain or page, strongest referring domain first. status lost lists removed links.

Returns a list: url_from, url_to, domain_from, dr_from (linking domain's rank, 0 to 1000), page_title, anchor, dofollow, rel[], type, spam_score (0 to 100), url_to_status, broken, first_seen, last_seen, lost (only with status lost or all).

Cost: 10 + 1.5 per 100 rows. Default 100 rows = 12 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_backlinks`

Cost: 10 + 1.5 per 100 rows. Default 100 rows = 12 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "include_subdomains": {
      "default": true,
      "description": "Count links to subdomains too. Ignored for a URL target.",
      "type": "boolean"
    },
    "status": {
      "default": "live",
      "description": "live: links found at the last check. lost: links since removed. all: both.",
      "type": "string",
      "enum": [
        "live",
        "lost",
        "all"
      ]
    },
    "limit": {
      "default": 100,
      "description": "Max rows. Default 100, cap 1000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "cursor": {
      "description": "The next page: meta.cursor from the previous result.",
      "type": "string"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target"
  ]
}
```

## Get referring domains

Tool: `seo_get_referring_domains`

Domains linking to a target, one row per domain, with the rank each passes, link counts and spam score. Good for link prospecting from a rival's profile; status lost lists sites that stopped linking.

Returns a list sorted by rank_passed: domain, rank_passed (0 to 1000, the rank its links pass to the target, not the domain's own rank), backlinks, referring_pages, dofollow_backlinks, broken_backlinks, spam_score, first_seen, lost (only with status lost or all).

Cost: 10 + 1.5 per 100 rows. Default 100 rows = 12 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_referring_domains`

Cost: 10 + 1.5 per 100 rows. Default 100 rows = 12 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Domain or URL. example.com covers all subdomains, www.example.com is that host only, a full URL is that page only."
    },
    "include_subdomains": {
      "default": true,
      "description": "Count links to subdomains too. Ignored for a URL target.",
      "type": "boolean"
    },
    "status": {
      "default": "live",
      "description": "live: links found at the last check. lost: links since removed. all: both.",
      "type": "string",
      "enum": [
        "live",
        "lost",
        "all"
      ]
    },
    "limit": {
      "default": 100,
      "description": "Max rows. Default 100, cap 1000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "cursor": {
      "description": "The next page: meta.cursor from the previous result.",
      "type": "string"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "target"
  ]
}
```

## Get domain ratings in bulk

Tool: `seo_get_domain_ratings`

Domain rank (and Ahrefs DR when configured) for up to 1,000 domains in one call. The cheap way to rate a list.

Returns one row per distinct domain in first-given order: domain, domain_rank (0 to 1000, for the host as given; 0 for an unknown domain), ahrefs_dr (0 to 100 for the registered domain, null when Ahrefs did not answer). ahrefs_status: ok, partial, failed or not_configured. Wherever you show ahrefs_dr, show ahrefs_attribution's text linked to its url. errors[] lists inputs that were not valid domains.

Cost: 7 + 2.5 per 100 domains. Default 100 domains = 10 credits. Cached 7 days, or 1 hour when ahrefs_status is not ok.

Endpoint: `POST /api/seo/tools/seo_get_domain_ratings`

Cost: 7 + 2.5 per 100 domains. Default 100 domains = 10 credits. Cached 7 days, or 1 hour when ahrefs_status is not ok.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "targets": {
      "minItems": 1,
      "maxItems": 1000,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 2048
      },
      "description": "Required. At most 1000."
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "targets"
  ]
}
```

## Get traffic estimates in bulk

Tool: `seo_get_traffic_estimates`

Estimated monthly organic and paid traffic for up to 1,000 domains in one call. For authority only, use seo_get_domain_ratings (a tenth of the cost).

Returns one row per input domain in the order given: domain, organic_traffic, organic_keywords, paid_traffic. null = provider has no data. errors[] lists inputs that were not valid domains.

Cost: 50 + 50 per 100 domains. Default 100 domains = 100 credits. Cached 7 days.

Endpoint: `POST /api/seo/tools/seo_get_traffic_estimates`

Cost: 50 + 50 per 100 domains. Default 100 domains = 100 credits. Cached 7 days.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "targets": {
      "minItems": 1,
      "maxItems": 1000,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 2048
      },
      "description": "Required. At most 1000."
    },
    "location": {
      "default": "United States",
      "description": "Provider location name or code. Default United States.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1
        },
        {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        }
      ]
    },
    "language": {
      "default": "en",
      "description": "ISO 639-1 language code. Default en.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}(-[A-Za-z]{2,4})?$"
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "targets"
  ]
}
```

## Get an on-page snapshot

Tool: `seo_get_page`

An on-page snapshot of one URL, fetched live: title, meta description, canonical, robots, headings, word count, schema types and link counts. For a whole site use seo_run_technical_crawl.

Returns url, final_url, status, title, meta_description, canonical, robots_meta, lang, h1[], h2[], word_count, schema_types[], links_internal, links_external, images, images_without_alt; with include, links[] (text, url) and text (without nav, header and footer). null = the tag is absent.

Cost: 0 credits, rate limited. Cached 1 hour.

Endpoint: `POST /api/seo/tools/seo_get_page`

Cost: 0 credits, rate limited. Cached 1 hour.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Required. An http or https URL."
    },
    "include": {
      "default": [],
      "description": "Also return the page's links, its visible text, or both.",
      "maxItems": 2,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "links",
          "text"
        ]
      }
    },
    "idempotency_key": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{8,100}$"
    }
  },
  "required": [
    "url"
  ]
}
```

## Run a technical audit

Tool: `seo_run_technical_crawl`

Crawl a site for technical SEO issues. Slow: returns a task_id at once; read the result with get_task. Does not run Lighthouse.

When done, get_task returns target, pages_requested, pages_crawled, rendered, onpage_score, status_codes (crawled pages per HTTP status), broken_links, broken_resources, non_indexable, duplicate_titles, duplicate_descriptions, issues[] (check, pages, sample_urls[]). Results expire after 30 days.

Cost: 5 credits per 100 pages; 50 per 100 pages rendered. Charged on max_pages requested, not pages crawled. 1,000 pages = 50 credits.

Endpoint: `POST /api/seo/tools/seo_run_technical_crawl`

Cost: 5 credits per 100 pages; 50 per 100 pages rendered. Charged on max_pages requested, not pages crawled. 1,000 pages = 50 credits.

### Input JSON schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "target": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "description": "Required. A domain or host. Only that host is crawled."
    },
    "max_pages": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000,
      "description": "Required. Pages to crawl."
    },
    "render": {
      "default": false,
      "description": "Execute JavaScript. 10x the cost.",
      "type": "boolean"
    },
    "include_subdomains": {
      "default": false,
      "description": "Follow links onto the target's subdomains too; they share max_pages.",
      "type": "boolean"
    },
    "idempotency_key": {
      "description": "Retry-safe key: the same key returns the same task.",
      "type": "string",
      "minLength": 8,
      "maxLength": 128
    },
    "max_credits": {
      "description": "Refuse the call with BudgetExceeded if the estimate is higher.",
      "type": "number",
      "minimum": 0
    },
    "dry_run": {
      "default": false,
      "description": "Return only meta with the estimate. Costs nothing.",
      "type": "boolean"
    }
  },
  "required": [
    "target",
    "max_pages"
  ]
}
```
