Skip to main content
GET
Read an SEO research result
Use result_url unchanged on the API origin and authenticate with a Key permitted to read every Run. The response uses the same fields as the initial Solution response. HTTP 200 can still carry result_status: pending while a crawl runs; keep polling until status is terminal. This read creates no Runs, calls no Providers and does not resume fallback.

Query Parameters

token
string
required

Opaque result context returned in result_url. Keep it unchanged and authenticate with a Key permitted to read every listed Run.

Required string length: 1 - 16384

Response

SeoResearchResult

id
string
required
run_url
string
required

Open this Run in the console. Requires Workspace membership.

provider
string
required
Pattern: ^[a-z0-9][a-z0-9-]*$
endpoint
string
required
Pattern: ^\/.+$
endpoint_version
integer
required
status
enum<string>
required

QUEUED or RUNNING: still in progress. COMPLETED: a Provider response is available, including non-2xx responses. FAILED: read failure. STOPPED: stopped before dispatch. Read charge_usd separately from status.

Available options:
QUEUED,
RUNNING,
COMPLETED,
FAILED,
STOPPED
failure
object | null
required

Why the Run failed. Non-null only when status is FAILED.

input
object
required
output
object
required

Normalized fields per action. The Provider's native output is not repeated here; it stays on the Run (GET /v1/runs/:id). Values a Provider does not report are null. Money is USD. Authority scores keep the answering Provider's own metric and name and are never converted: ahrefs_domain_rating (Ahrefs Domain Rating, 0–100), semrush_authority_score (Semrush Authority Score, 0–100), dataforseo_rank (DataForSEO rank, 0–1000), serpstat_domain_rank (Serpstat Domain Rank, 0–100). Keyword difficulty is split the same way (semrush_keyword_difficulty, serpstat_keyword_difficulty, dataforseo_keyword_difficulty, ahrefs_keyword_difficulty, each 0–100 on its own model). Only the field of the Provider that answered is filled; the others are null. volume, traffic and cpc are the answering Provider's estimates; compare them only within one Provider (runs[].provider). An empty answer (result_status no_result) keeps the action's shape: items is [], or every field is null.

  • keyword_overview: items[]: keyword, volume, cpc_usd, semrush_keyword_difficulty, serpstat_keyword_difficulty, dataforseo_keyword_difficulty, ahrefs_keyword_difficulty, intent
  • keyword_ideas: items[]: same row as keyword_overview
  • serp: items[]: position (organic position: 1 = first organic result; SERP features not counted), url, domain, title, snippet (organic results only)
  • domain_overview: organic_keywords, organic_traffic, organic_traffic_value_usd, paid_keywords, paid_traffic
  • ranked_keywords: items[]: keyword, position (organic position: 1 = first organic result; SERP features not counted), url, volume, cpc_usd, traffic
  • organic_competitors: items[]: domain, common_keywords, organic_keywords (the competitor's total), organic_traffic (the target domain and its subdomains are left out)
  • backlinks_overview: backlinks, referring_domains, semrush_authority_score, dataforseo_rank, serpstat_domain_rank, dofollow_backlinks, nofollow_backlinks
  • backlinks: items[]: source_url, target_url, anchor, source_ahrefs_domain_rating, source_semrush_authority_score, source_dataforseo_rank, dofollow, first_seen_at (ISO 8601 UTC; Semrush: zone not stated, no Z suffix). One row per link from every Provider
  • referring_domains: items[]: domain, ahrefs_domain_rating, semrush_authority_score, dataforseo_rank, backlinks, dofollow_backlinks, first_seen_at (ISO 8601 UTC; Semrush: zone not stated, no Z suffix)
  • domain_rating: ahrefs_domain_rating
  • site_audit: dataforseo_task_id (input to DataForSEO's OnPage report endpoints), crawl_progress, crawl_stop_reason (DataForSEO's vocabulary), pages_crawled, dataforseo_onpage_score, broken_links, non_indexable, duplicate_title, duplicate_description, duplicate_content
  • keyword_gap: items[]: keyword, volume, cpc_usd, semrush_keyword_difficulty, dataforseo_keyword_difficulty, positions {domain: organic position; null = not in the top 100}, keyed by the domains sent. Semrush rows are keywords any of the domains ranks for (union); DataForSEO rows are keywords the competitor ranks for and you do not
  • backlink_gap: items[]: domain, dataforseo_rank, backlinks_per_target {competitor domain: count}
  • page_audit: url, status_code, title, description, canonical, dataforseo_onpage_score, internal_links, external_links
  • visibility_history: items[]: month, organic_keywords, organic_traffic, organic_traffic_value_usd, paid_keywords, paid_traffic
  • ai_citations: items[]: url, platform, mentions, ai_search_volume, is_target_domain (pages most cited in AI answers that mention the domain, from any site)
  • ai_visibility: ahrefs_ai_citations, ahrefs_ai_cited_pages, ahrefs_ai_citations_by_platform {platform: citations}, dataforseo_ai_mentions_by_platform {platform: mentions}, dataforseo_ai_search_volume_by_platform {platform: AI search volume}. Each Provider fills only its own fields; no cross-Provider total
provider_response
object | null
required

Non-null only when status is COMPLETED.

  • For 2xx responses, output contains Provider-native JSON (any shape), or null for an empty body.
  • For non-2xx responses, error contains the Provider's error body and output is null.

Sensitive fields are removed from both bodies.

charge_usd
string | null
required

Total charge of runs in this Solution response. Null until all listed Runs settle. Each Run retains its own charge; failures can also be charged.

Pattern: ^(0|[1-9]\d*)\.(\d{2}|\d{2}\d{0,3}[1-9])$
charge_basis
object | null
required

Single-Run charge basis, or null for a multi-Run total. Read runs[].charge_basis for each charge.

stoppable
boolean
required

Whether a stop request can currently be accepted. This does not guarantee that the Run can stop before dispatch.

stop_requested_at
string<date-time> | null
required

When a stop was requested. The Run can still continue if dispatch had already started.

created_at
string<date-time>
required

ISO-8601 UTC timestamp

started_at
string<date-time> | null
required

ISO-8601 UTC timestamp

completed_at
string<date-time> | null
required

ISO-8601 UTC timestamp

task_id
string | null
required
metadata
object
required

Your stored tracking fields. Use them to filter Runs. They never go to the Provider or affect routing, Price, or idempotency.

  • Up to 20 keys: lowercase letters, digits, and underscores; start with a letter; at most 40 characters.
  • Values: strings of 1–500 characters.
  • user_id: your End user's opaque ID, never their email address or name.
action
enum<string>
required
Available options:
keyword_overview,
keyword_ideas,
serp,
domain_overview,
ranked_keywords,
organic_competitors,
backlinks_overview,
backlinks,
referring_domains,
domain_rating,
site_audit,
ai_visibility,
keyword_gap,
backlink_gap,
page_audit,
visibility_history,
ai_citations
result_status
enum<string>
required

Business delivery status, independent of the final Run lifecycle in status. no_result is an empty but valid answer; error means the Provider output could not be delivered.

Available options:
success,
no_result,
error,
pending
error
object | null
required
runs
object[]
required
result_url
string
required

Authenticated, read-only URL path for polling this normalized result and total charge. Use with the API origin. Does not create Runs or resume fallback. GET /v1/runs/:id still returns the native, individual Run.