# DataForGEO API > DataForGEO asks one prompt across several AI search products, returns the answers, and computes GEO metrics for a brand (mentions, citations, recommendation rank and more). Usage is billed in credits. - Base URL: https://api.dataforgeo.dev - Auth: `x-api-key` header (https://dashboard.dataforgeo.dev/keys) - OpenAPI: https://docs.dataforgeo.dev/openapi.json - Web version: https://docs.dataforgeo.dev/ ## Quickstart Every endpoint authenticates with one header: x-api-key. Requests and responses are JSON, and all times are ISO 8601 (UTC). | Key | Description | | --- | --- | | Base URL | `https://api.dataforgeo.dev` | | Auth | `x-api-key` — Create keys in the console. Pro: 5 keys; Scale and Enterprise: unlimited | | Free credits | `100 credits` — On sign-up. Enough for 100 standard-engine calls | ### First request ```bash curl https://api.dataforgeo.dev/v1/geo/answer \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{ "prompt": "东京有哪些适合家庭入住的民宿?", "engines": [ "chatgpt" ], "brand": "晓宅", "location": "JP", "language": "zh-CN" }' ``` ### Example response ```json { "request_id": "req_8f2c1a", "prompt": "东京有哪些适合家庭入住的民宿?", "credits_used": 1, "answers": [ { "engine": "chatgpt", "sample": 1, "status": "ok", "cached": false, "generated_at": "2026-11-02T00:31:12Z", "text": "……", "brand": { "mentioned": true, "mention_count": 2, "first_position": 3, "sentiment": "positive" }, "entities": [ { "name": "晓宅", "type": "brand", "position": 3, "is_competitor": false }, { "name": "Example Stay", "type": "brand", "position": 1, "is_competitor": true } ], "citations": [ { "url": "https://example.com/guide", "domain": "example.com", "position": 1, "is_brand_domain": false } ] } ] } ``` ## MCP, Skills & SDKs Besides plain HTTP there are three official open-source ways in: an MCP server for AI assistants, Agent Skills for agents, and TypeScript / Python SDKs for your own code. All three need one environment variable, DATAFORGEO_API_KEY, and bill exactly like direct API calls. | Option | Best for | Install | | --- | --- | --- | | MCP server | Gives Claude, Cursor and other AI assistants query tools | `npx -y @dataforgeo/mcp` | | Agent Skills | Lets an agent run audits, competitor benchmarks, citation audits and weekly reports end to end | `/plugin install dataforgeo@dataforgeo` | | SDKs | Integrate from your own TypeScript / Python code | `npm install @dataforgeo/client` | ### MCP server Adds six tools to Claude Code, Claude Desktop, Cursor, Windsurf and any other MCP client. Runs over stdio with npx and needs Node.js 20 or later. Every tool description states its cost, so the assistant knows what a call will spend before it makes it. ```bash claude mcp add dataforgeo -e DATAFORGEO_API_KEY=$DATAFORGEO_API_KEY -- npx -y @dataforgeo/mcp ``` ```json { "mcpServers": { "dataforgeo": { "command": "npx", "args": [ "-y", "@dataforgeo/mcp" ], "env": { "DATAFORGEO_API_KEY": "YOUR_API_KEY" } } } } ``` | tool | API | Description | Cost | | --- | --- | --- | --- | | `geo_answer` | POST /v1/geo/answer | Answers for one prompt across engines, with mentions, citations and competitors | Same as API | | `geo_index` | POST /v1/geo/index | Same, plus GEO Index KPIs (Pro and up) | Same as API | | `geo_index_summary` | GET /v1/geo/index/summary | KPIs, trends and breakdowns of past runs | 0 | | `get_job` | GET /v1/jobs/{job_id} | Fetch an async job; wait_seconds polls until done | 0 | | `get_account` | GET /v1/account | Plan, balance and this month’s usage | 0 | | `estimate_credits` | local | Estimates the credits a call or batch would hold, without calling the API | 0 | | Variable | Required | Description | | --- | --- | --- | | `DATAFORGEO_API_KEY` | Yes | Your API key, sent as the x-api-key header | | `DATAFORGEO_BASE_URL` | No | Overrides the API base URL | | `DATAFORGEO_MAX_CREDITS_PER_CALL` | No | Max credits a single geo_answer / geo_index may hold; above it the call is refused locally and never sent. Recommended for autonomous agents | https://github.com/DataForGEO/mcp ### Agent Skills Ready-made GEO workflows for Claude Code, Claude.ai and any other agent that supports Agent Skills (Codex CLI, Cursor, Gemini CLI and more). Skills that spend credits check your balance, estimate the hold and ask before any batch run. Each skill ships a zero-dependency CLI, dfgeo.py, that needs only Python 3.8+. ```text /plugin marketplace add DataForGEO/skills /plugin install dataforgeo@dataforgeo ``` | skill | Description | Spends credits | | --- | --- | --- | | `dataforgeo-api` | Basics: single answer / index / summary calls, balance, credit estimates, async jobs | Per call | | `geo-prompt-set-builder` | Designs a prompt set by intent × audience × language, exports JSONL and estimates cost | No | | `geo-visibility-audit` | Brand AI visibility audit: runs prompts × engines and writes an evidence-backed report with actions | Yes, after an estimate and your OK | | `geo-competitor-benchmark` | Competitor benchmark: share of voice, top-pick rate, average position, per-prompt wins | Yes, same as above | | `geo-citation-audit` | Citation audit: which sites AI cites, your own citation rate, content / outreach / PR to-dos | Reuses existing results at no extra cost | | `geo-index-report` | Weekly / monthly GEO Index reports and a fixed-tag weekly tracker | Reports free; tracking per call | Claude.ai / Claude Desktop: run python3 scripts/package.py in the repo to build zips, upload them under Settings → Capabilities → Skills, and turn on code execution. Skills and MCP work together. https://github.com/DataForGEO/skills ### SDK TypeScript / JavaScript: @dataforgeo/client. Python: dataforgeo. ```bash npm install @dataforgeo/client pip install dataforgeo ``` ```ts import { DataForGEO } from "@dataforgeo/client"; const client = new DataForGEO(); // reads DATAFORGEO_API_KEY const res = await client.geo.answer({ prompt: "东京有哪些适合家庭入住的民宿?", engines: ["chatgpt"], brand: "晓宅", location: "JP", language: "zh-CN", }); console.log(res.answers[0].brand?.mentioned, res._meta.creditsRemaining); ``` ```python from dataforgeo import DataForGEO client = DataForGEO() # reads DATAFORGEO_API_KEY res = client.geo.answer( prompt="东京有哪些适合家庭入住的民宿?", engines=["chatgpt"], brand="晓宅", location="JP", language="zh-CN", ) print(res["answers"][0]["brand"]["mentioned"], res.meta.credits_remaining) ``` - The two SDKs mirror each other and cover every endpoint, error code, usage header (x-credits-used and friends), async job and webhook signature check. - Request params and response fields match the API (snake_case), with no renaming. - 429, 408 and 5xx are retried automatically (errors are free). Network errors retry GET only, never POST, so you are never charged twice. https://github.com/DataForGEO/client ## Billing & credits You pay only for answers actually returned. Credits are held up front as prompt × engines × samples and settled when the request finishes. Credits are bought once and never expire. | Call | Cost per answer | | --- | --- | | GEO data, standard engines (chatgpt, google-aio, chatgpt-api, perplexity, grok) | 1 | | GEO data, premium engines (gemini, gemini-api, claude) | 2 | | Index analysis (/v1/geo/index KPIs on each answer) | +1 | | Cache hit | 0 | | Engine timeout, failure or no answer | 0 | | 503 engines_unavailable (every requested engine is unavailable) | 0 | | /v1/geo/index/summary、/v1/jobs、/v1/account、/v1/status | 0 | ### Response headers | Header | Meaning | | --- | --- | | `x-credits-used` | Credits actually charged for this request | | `x-credits-remaining` | Balance after the charge | | `x-cache` | hit, miss or partial (some answers hit) | ### Cache Cache key is prompt + engine + location + language + sample index, kept 24 hours and shared across accounts. Pass fresh: true to skip the cache at the normal price. ## Engines & locations engines accepts the 8 ids below, several per request. Each engine is billed and returned separately. Engines come in two answer types: a product answer is what a real user sees in that AI product's interface; a model answer is what the model returns through its API with its own web search turned on. | Engine id | Platform | Answer type | Cost per answer | | --- | --- | --- | --- | | `chatgpt` | ChatGPT | Product answer | 1 | | `gemini` | Gemini | Product answer | 2 | | `google-aio` | Google AI Overview | Product answer | 1 | | `chatgpt-api` | ChatGPT API | Model answer | 1 | | `gemini-api` | Gemini API | Model answer | 2 | | `claude` | Claude | Model answer | 2 | | `perplexity` | Perplexity | Model answer | 1 | | `grok` | Grok | Model answer | 1 | - Product answers: chatgpt, gemini, google-aio. Model answers: chatgpt-api, gemini-api, claude, perplexity, grok. - chatgpt and chatgpt-api, gemini and gemini-api are separate engines. For the same question the product interface and the API can give different answers and citations, and KPIs and trends are counted separately. Use product answers to see what users actually see; use model answers for reproducible, model-by-model comparisons. - When an engine is unavailable its answers fail; we never substitute the other answer type, so data under one engine id always means the same thing. - location: ISO 3166-1 alpha-2 country code such as JP or US. Sets the region for search results and AI Overview. Default US. - language: BCP 47 tag such as ja, zh-CN or en. Inferred from location by default. - google-aio only answers when the results page shows an AI Overview; otherwise status is no_answer and nothing is charged. ## Get AI answers `POST https://api.dataforgeo.dev/v1/geo/answer` · all plans Get AI answers for one prompt across engines, with brand mentions, citations and competitor entities marked. Available on all plans. ### Parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `prompt` | string | Yes | The question to ask, up to 2,000 characters | | `engines` | string[] | Yes | Engine ids. See Engines & locations | | `brand` | string | No | Brand to detect. Omit to get answers and entities only | | `brand_variants` | string[] | No | Brand aliases, e.g. katakana or English names | | `competitors` | string[] | No | Competitor names, up to 20 | | `location` | string | No | Country code. Default US | | `language` | string | No | Language tag. Inferred from location | | `samples` | integer | No | Samples per engine, 1–10. Default 1 | | `fresh` | boolean | No | true skips the cache. Default false | | `async` | boolean | No | true returns a job_id immediately. See Async jobs | | `webhook_url` | string | No | URL to call when the async job finishes | ### Example request ```bash curl https://api.dataforgeo.dev/v1/geo/answer \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{ "prompt": "东京有哪些适合家庭入住的民宿?", "engines": [ "chatgpt" ], "brand": "晓宅", "location": "JP", "language": "zh-CN" }' ``` ### Example response ```json { "request_id": "req_8f2c1a", "prompt": "东京有哪些适合家庭入住的民宿?", "credits_used": 1, "answers": [ { "engine": "chatgpt", "sample": 1, "status": "ok", "cached": false, "generated_at": "2026-11-02T00:31:12Z", "text": "……", "brand": { "mentioned": true, "mention_count": 2, "first_position": 3, "sentiment": "positive" }, "entities": [ { "name": "晓宅", "type": "brand", "position": 3, "is_competitor": false }, { "name": "Example Stay", "type": "brand", "position": 1, "is_competitor": true } ], "citations": [ { "url": "https://example.com/guide", "domain": "example.com", "position": 1, "is_brand_domain": false } ] } ] } ``` - status is ok, no_answer, timeout or failed. The last three are free. - When status is timeout or failed, the answer carries an error field with the reason (values below). ok and no_answer answers have no error field. - first_position is where the brand first appears in the answer’s recommendation list, or null. ### The answer error field | error | Meaning | | --- | --- | | `upstream_timeout` | The engine did not answer within the time limit (retries included) | | `upstream_error` | The engine returned an error and still failed after automatic retries | | `engine_unavailable` | The engine is temporarily unavailable and was not called. Check /v1/status | ### Example failed answer ```json { "engine": "grok", "sample": 1, "status": "failed", "error": "engine_unavailable", "cached": false } ``` ## Compute GEO Index `POST https://api.dataforgeo.dev/v1/geo/index` · Pro / Scale / Enterprise Computes GEO Index KPIs on top of /v1/geo/answer results. Pro, Scale and Enterprise. Starter gets 403 plan_not_allowed. ### Parameters Same parameters as /v1/geo/answer, with brand required, plus: | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `brand` | string | Yes | Brand to compute KPIs for | | `tag` | string | No | Custom grouping tag, e.g. osaka-family-q4. /summary aggregates by it | | `intent` | string | No | Prompt intent: compare, cost, discover, evaluate, howto, recommend | | `persona` | string | No | Who is asking, free text, e.g. families with kids | ### Example request ```bash curl https://api.dataforgeo.dev/v1/geo/index \ -H "x-api-key: YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{ "prompt": "大阪带孩子住哪里方便?", "engines": [ "chatgpt", "perplexity", "grok" ], "brand": "晓宅", "tag": "osaka-family-q4", "intent": "recommend", "persona": "带孩子的家庭", "location": "JP", "language": "zh-CN" }' ``` ### Example response ```json { "request_id": "req_91d0b3", "brand": "晓宅", "tag": "osaka-family-q4", "credits_used": 6, "answers": [ "…与 /v1/geo/answer 相同…" ], "kpis": { "visibility": { "value": 0.667, "numerator": 2, "denominator": 3, "samples": 3 }, "share_of_voice": { "value": 0.25, "numerator": 2, "denominator": 8, "samples": 3 }, "top_rate": { "value": 0, "numerator": 0, "denominator": 3, "samples": 3 }, "competitor_gap": { "value": -1.33, "samples": 3 }, "positive_ratio": { "value": 1, "numerator": 2, "denominator": 2, "samples": 3 } }, "kpi_tier": "pro" } ``` - The example is 3 standard engines × 1 answer: 3 × 1 credit + 3 × 1 credit Index = 6 credits. - kpi_tier pro returns core KPIs only; scale returns all of them. - A single request has few samples, so treat its KPIs as indicative. Use /v1/geo/index/summary for trends and comparisons. ## Index summary & trends `GET https://api.dataforgeo.dev/v1/geo/index/summary` · Pro / Scale / Enterprise Aggregates your past /v1/geo/index results into KPIs and trends over a time window. No new AI calls, no charge. Pro gets totals and trends; group_by breakdowns are Scale and Enterprise only. ### Parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `brand` | string | Yes | Brand, same as in index requests | | `tag` | string | No | Only aggregate requests with this tag | | `from / to` | string | No | Time window, ISO 8601. Default last 30 days | | `interval` | string | No | Trend granularity: day, week, month. Default week | | `engines` | string | No | Comma-separated engines to include | | `kpis` | string | No | Comma-separated KPIs to return. Default: all for your plan | | `group_by` | string | No | engine, intent, persona or prompt, one at a time. Scale only (Scale) | ### Example request ```bash curl "https://api.dataforgeo.dev/v1/geo/index/summary?brand=晓宅&tag=osaka-family-q4&interval=week&group_by=engine" \ -H "x-api-key: YOUR_API_KEY" ``` ### Example response ```json { "brand": "晓宅", "window": { "from": "2026-10-03T00:00:00Z", "to": "2026-11-02T00:00:00Z" }, "answers_counted": 498, "kpis": { "findability": { "value": 42.1 }, "visibility": { "value": 0.262, "numerator": 130, "denominator": 498 } }, "series": [ { "period": "2026-10-05", "visibility": 0.255, "share_of_voice": 0.018 }, { "period": "2026-10-12", "visibility": 0.263, "share_of_voice": 0.026 } ], "groups": [ { "key": "chatgpt", "answers_counted": 166, "kpis": { "visibility": { "value": 0.301 } } }, { "key": "gemini", "answers_counted": 166, "kpis": { "visibility": { "value": 0.241 } } } ] } ``` - Some KPIs only exist in summaries, e.g. knowledge_half_life and category_capture_gap. - Summaries use only your account’s index results. Only the answer cache is shared across accounts, never KPIs or tags. ## Async jobs & account Multi-engine, multi-sample requests can take a minute or two. Pass async: true to get a job_id immediately and fetch results by polling or webhook. Sync requests over 120 s switch to async and return 202. ### Endpoints | Endpoints | Description | credits | | --- | --- | --- | | `GET /v1/jobs/{job_id}` | Job status and result | 0 | | `GET /v1/account` | Balance, plan and usage this month | 0 | ### Async response ```json { "job_id": "job_3b7e90", "status": "queued", "credits_held": 9 } ``` `GET /v1/account` ```json { "plan": "scale", "credits_remaining": 10422, "credits_used_this_month": 1578 } ``` - status goes queued → running → completed or failed. On completed, result matches the sync response. - In async requests, answers that time out or fail are retried once more about 2 minutes later and only then marked final. Sync requests are not retried this way. - credits_held is the hold. It settles against answers actually returned and the rest is refunded. - With webhook_url set, the full result is POSTed there with an x-dataforgeo-signature header (HMAC-SHA256, secret in the console). - Job results are kept for 7 days. ## Engine status `GET https://api.dataforgeo.dev/v1/status` · public, no auth Returns each engine’s current status, its success rate and median latency over the last 24 hours, and its daily availability over the last 30 days. No auth, no charge: check it before sending requests. The public status page reads the same data. ### Example request ```bash curl "https://api.dataforgeo.dev/v1/status" ``` ### Example response ```json { "updated_at": "2026-11-02T05:40:00Z", "status": "degraded", "engines": [ { "id": "chatgpt", "name": "ChatGPT", "answer_type": "product", "status": "operational", "last_24h": { "success_rate": 0.987, "median_latency_ms": 21400, "calls": 1234 }, "daily": [ { "date": "2026-10-04", "uptime": 0.998 }, "… 30 项 …", { "date": "2026-11-02", "uptime": 1 } ] }, { "id": "claude", "name": "Claude", "answer_type": "model", "status": "degraded", "last_24h": { "success_rate": 0.912, "median_latency_ms": 9800, "calls": 640 }, "daily": [ "…" ] }, { "id": "grok", "name": "Grok", "answer_type": "model", "status": "unavailable", "last_24h": { "success_rate": 0.64, "median_latency_ms": 7300, "calls": 210 }, "daily": [ "…" ] }, "…" ] } ``` ### engines[].status values | status | Meaning | | --- | --- | | `operational` | Working normally | | `degraded` | Degraded: success rate under 90% over the last 15 minutes. Requests still go out but may fail more often | | `unavailable` | Unavailable: answers for this engine return failed / engine_unavailable at no charge | - The top-level status rolls up all engines: outage when every engine is unavailable, degraded when any engine is degraded or unavailable, otherwise operational. - last_24h covers real calls over the last 24 hours: success_rate is a 0–1 fraction, median_latency_ms the median latency, calls the call count. The first two are null when there were no calls. A google-aio no_answer counts as a success; cache hits are not counted. - daily always has 30 entries by UTC date, oldest first, the last one being today. uptime is null on days with no calls. - No auth, CORS *, responses cached for 30 seconds; data is aggregated every 5 minutes. The web version is at dataforgeo.dev/status. ## KPI field reference Pro returns 11 core KPIs; Scale and Enterprise add 8 advanced ones. Public definitions only; formulas are not published. | Field | Name | Definition | Unit | Plan | Returned in | | --- | --- | --- | --- | --- | --- | | `findability` | Findability | Composite score of how often the brand is found and recommended in AI answers | score | pro | Single & summary | | `visibility` | Visibility | Share of answers that mention the brand | ratio | pro | Single & summary | | `own_mentions` | Own mentions | Average brand mentions per answer | count | pro | Single & summary | | `citations` | Citations | Average sources cited per answer | count | pro | Single & summary | | `citation_rate` | Citation rate | Share of answers citing a brand-owned source | ratio | pro | Single & summary | | `share_of_voice` | Share of voice | Brand mentions over brand plus competitor mentions | ratio | pro | Single & summary | | `competitor_mentions` | Competitor mentions | Average competitor mentions per answer | count | pro | Single & summary | | `competitor_gap` | Competitor gap | Own mentions minus competitor mentions | count | pro | Single & summary | | `top_rate` | Top rate | Share of answers recommending the brand first | ratio | pro | Single & summary | | `positive_ratio` | Positive ratio | Share of brand mentions that are positive | ratio | pro | Single & summary | | `tone` | Tone | Overall tone when the brand is mentioned, −1 to 1 | score | pro | Single & summary | | `weighted_share` | Weighted share | Share of voice weighted by mention position | ratio | scale | Summary only | | `rank_gap` | Rank gap | Recommendation rank gap to each competitor, one per competitor | rank | scale | Single & summary | | `recommendation_position` | Recommendation position | Score for the brand’s place in recommendation lists | score | scale | Single & summary | | `purchase_intent_rate` | Purchase-intent rate | Share of purchase-intent prompts where the brand is recommended | ratio | scale | Single & summary | | `conclusion_bias` | Conclusion bias | Whether answers conclude for or against the brand, −1 to 1 | score | scale | Single & summary | | `category_capture_gap` | Category capture gap | How much of its category’s prompts the brand fails to capture | score | scale | Summary only | | `knowledge_half_life` | Knowledge half-life | How fast AI knowledge of the brand decays | weeks | scale | Summary only | | `features` | Features | Brand selling points mentioned in answers | list | scale | Single & summary | - Ratio fields are 0 to 1. Fields with numerator and denominator can be checked directly. - purchase_intent_rate only counts purchase-intent prompts, so send intent with the request. ## Errors & limits Errors share one shape and are never charged. One engine failing does not fail the request: that answer gets status timeout or failed with an error field giving the reason, is free, and the other engines return as usual. Only when every requested engine is unavailable does the whole request return 503. | HTTP | code | Meaning | | --- | --- | --- | | 400 | `invalid_request` | Missing or malformed parameter; message names the field | | 400 | `unsupported_engine` | Unknown engine id in engines | | 401 | `invalid_api_key` | API key missing, wrong or disabled | | 402 | `insufficient_credits` | Balance too low to hold this request | | 403 | `plan_not_allowed` | Your plan can’t use this endpoint or parameter, e.g. index on Starter, group_by on Pro | | 404 | `not_found` | job_id does not exist or has expired | | 500 | `internal_error` | Server error. Contact support with the request_id | | 503 | `engines_unavailable` | Every requested engine is temporarily unavailable. The response has Retry-After (seconds); nothing is held or charged. If any one engine is available the request runs as usual | ```json { "error": { "code": "insufficient_credits", "message": "本次请求需预占 9 credits,余额 4", "request_id": "req_7a11c2" } } ``` ```json { "error": { "code": "engines_unavailable", "message": "请求的引擎当前都不可用:claude、grok", "request_id": "req_5c02e4" } } ``` ### Limits - No account-level rate limit. Keep concurrency under 100 requests for stability (to be load-tested). - At most 8 engines × 10 samples = 80 answers per request. - Transient engine errors (network, timeout, rate limit, 5xx) are retried automatically up to 2 times within the time limit, at no extra charge. - prompt up to 2,000 characters; up to 20 competitors. - Cache lasts 24 hours; async job results are kept 7 days.