curl --request GET \
--url https://api.glasser.ai/v1/solutions/gtm/seo_research/result \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.glasser.ai/v1/solutions/gtm/seo_research/result"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.glasser.ai/v1/solutions/gtm/seo_research/result', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.glasser.ai/v1/solutions/gtm/seo_research/result",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.glasser.ai/v1/solutions/gtm/seo_research/result"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.glasser.ai/v1/solutions/gtm/seo_research/result")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.glasser.ai/v1/solutions/gtm/seo_research/result")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "<string>",
"run_url": "<string>",
"provider": "<string>",
"endpoint": "<string>",
"endpoint_version": 123,
"status": "QUEUED",
"failure": {
"kind": "TIMED_OUT",
"message": "<string>"
},
"input": {},
"output": {
"items": [
{
"keyword": "<string>",
"volume": 123,
"cpc_usd": 123,
"semrush_keyword_difficulty": 123,
"serpstat_keyword_difficulty": 123,
"dataforseo_keyword_difficulty": 123,
"ahrefs_keyword_difficulty": 123,
"intent": "<string>"
}
]
},
"provider_response": {
"http_status": 349,
"error": null
},
"charge_usd": "<string>",
"charge_basis": {
"clause": "rule",
"quantity": 1
},
"stoppable": true,
"stop_requested_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"task_id": "<string>",
"metadata": {},
"action": "keyword_overview",
"result_status": "success",
"error": {
"code": "<string>",
"message": "<string>"
},
"runs": [
{
"id": "<string>",
"run_url": "<string>",
"provider": "<string>",
"endpoint": "<string>",
"endpoint_version": 123,
"status": "QUEUED",
"failure": {
"kind": "TIMED_OUT",
"message": "<string>"
},
"input": {
"omitted": true
},
"output": {
"omitted": true
},
"provider_response": {
"http_status": 349
},
"charge_usd": "<string>",
"charge_basis": {
"clause": "rule",
"quantity": 1
},
"stoppable": true,
"stop_requested_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"task_id": "<string>",
"metadata": {}
}
],
"result_url": "<string>"
}{
"error": {
"code": "validation_failed",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "unauthorized",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "forbidden",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "not_found",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "rate_limited",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}Read an SEO research result
Read normalized output, delivery status, and total charges for an existing SEO research request. Follow the result_url returned by the call.
curl --request GET \
--url https://api.glasser.ai/v1/solutions/gtm/seo_research/result \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.glasser.ai/v1/solutions/gtm/seo_research/result"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.glasser.ai/v1/solutions/gtm/seo_research/result', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.glasser.ai/v1/solutions/gtm/seo_research/result",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.glasser.ai/v1/solutions/gtm/seo_research/result"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.glasser.ai/v1/solutions/gtm/seo_research/result")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.glasser.ai/v1/solutions/gtm/seo_research/result")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "<string>",
"run_url": "<string>",
"provider": "<string>",
"endpoint": "<string>",
"endpoint_version": 123,
"status": "QUEUED",
"failure": {
"kind": "TIMED_OUT",
"message": "<string>"
},
"input": {},
"output": {
"items": [
{
"keyword": "<string>",
"volume": 123,
"cpc_usd": 123,
"semrush_keyword_difficulty": 123,
"serpstat_keyword_difficulty": 123,
"dataforseo_keyword_difficulty": 123,
"ahrefs_keyword_difficulty": 123,
"intent": "<string>"
}
]
},
"provider_response": {
"http_status": 349,
"error": null
},
"charge_usd": "<string>",
"charge_basis": {
"clause": "rule",
"quantity": 1
},
"stoppable": true,
"stop_requested_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"task_id": "<string>",
"metadata": {},
"action": "keyword_overview",
"result_status": "success",
"error": {
"code": "<string>",
"message": "<string>"
},
"runs": [
{
"id": "<string>",
"run_url": "<string>",
"provider": "<string>",
"endpoint": "<string>",
"endpoint_version": 123,
"status": "QUEUED",
"failure": {
"kind": "TIMED_OUT",
"message": "<string>"
},
"input": {
"omitted": true
},
"output": {
"omitted": true
},
"provider_response": {
"http_status": 349
},
"charge_usd": "<string>",
"charge_basis": {
"clause": "rule",
"quantity": 1
},
"stoppable": true,
"stop_requested_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z",
"started_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"task_id": "<string>",
"metadata": {}
}
],
"result_url": "<string>"
}{
"error": {
"code": "validation_failed",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "unauthorized",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "forbidden",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "not_found",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}{
"error": {
"code": "rate_limited",
"message": "<string>",
"reason": "<string>",
"control": "<string>",
"retry_after_ms": 123,
"details": {}
},
"request_id": "<string>"
}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
Opaque result context returned in result_url. Keep it unchanged and authenticate with a Key permitted to read every listed Run.
1 - 16384Response
SeoResearchResult
Open this Run in the console. Requires Workspace membership.
^[a-z0-9][a-z0-9-]*$^\/.+$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.
QUEUED, RUNNING, COMPLETED, FAILED, STOPPED Why the Run failed. Non-null only when status is FAILED.
Show child attributes
Show child attributes
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
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
- Option 10
- Option 11
- Option 12
- Option 13
- Option 14
- Option 15
- Option 16
Show child attributes
Show child attributes
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.
Show child attributes
Show child attributes
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.
^(0|[1-9]\d*)\.(\d{2}|\d{2}\d{0,3}[1-9])$Single-Run charge basis, or null for a multi-Run total. Read runs[].charge_basis for each charge.
Show child attributes
Show child attributes
Whether a stop request can currently be accepted. This does not guarantee that the Run can stop before dispatch.
When a stop was requested. The Run can still continue if dispatch had already started.
ISO-8601 UTC timestamp
ISO-8601 UTC timestamp
ISO-8601 UTC timestamp
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.
Show child attributes
Show child attributes
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 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.
success, no_result, error, pending Show child attributes
Show child attributes
Show child attributes
Show child attributes
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.