# Getting started Source: https://geondex.com/docs > Get a key, connect your agent over MCP or call the REST API, and ask four AI engines whether they cite your site. Geondex asks Perplexity, ChatGPT, Claude and Gemini the question a buyer would ask, each with web search on, and tells you whether your site is cited, at what position, and who is cited instead. It stores each answer, so the next identical check comes back with what changed. You use it from your agent through the [MCP server](/docs/mcp), or from any script through the [REST API](/docs/api). Both expose the same seven calls and return the same JSON. ## Quickstart 1. [Sign in with Google](/signup) and create a key on your [account page](/account). Keys start with `gx_`. 2. Install the [MCP server](/docs/mcp) in Claude Code, Cursor, VS Code, Windsurf, Codex or Claude Desktop, or use the [REST API](/docs/api). 3. Ask: *"Check mysite.com for 'best X for Y' on all four engines."* 4. Ask again next week. The delta comes back with the answer. 200 free credits arrive every month, on every account. No card. Want to see a real response first? The [playground](/playground) builds the exact request for any call and runs it from your browser. ## Your first call over HTTP Replace `gx_YOUR_KEY` with your key. `GET /me` is free and shows your balance: ```bash curl -s https://geondex.com/api/v1/me -H "Authorization: Bearer gx_YOUR_KEY" ``` Then run a check. This one costs 3 credits per engine that answers, so 12 for all four: ```bash curl -s https://geondex.com/api/v1/check -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -d '{"domain":"ttsensei.com","prompt":"best table tennis blade for beginners"}' ``` ## Where to go next * [How a check works](/docs/how-a-check-works): what "cited", "position" and the delta mean, and what every response looks like. * [Authentication](/docs/authentication): keys, headers and the free `GET /me`. * [Credits & pricing rules](/docs/credits): what each call costs and when credits reset. * [Errors & rate limits](/docs/errors): every `error.code` and what to do about it. * [API reference](/docs/api): one page per endpoint, with a full request and response. * [MCP server](/docs/mcp) and [agent skills](/docs/mcp/skills): let your agent make the calls. ## Support Email [hello@geondex.com](mailto:hello@geondex.com). The [FAQ](/faq) covers the common questions; [status](/status) shows current availability. --- # How a check works Source: https://geondex.com/docs/how-a-check-works > What cited, position and the delta mean, which engines are asked, and the shape of every response. ## One check, four engines One check sends your prompt to four AI engines at the same time, each with web search on. Every engine answers with the pages it cited. You are **cited** when a cited page's host is your domain or a subdomain of it; **position** is that page's place in the engine's citation list. Every host that is not yours is counted as **cited instead**, ranked by how many engines chose it. Because each engine builds its citation list differently, read a position against the same engine over time rather than against another engine. Each engine gets 20 seconds. One that does not answer is reported as timed out, the others still come back, and the timeout is not charged. The whole call returns in under 25 seconds. ## The delta The result is stored under your key. The next check with the same domain and the same prompt (case and punctuation ignored) returns a **delta**: per engine, cited before and now, position before and now. Nothing runs on a schedule; if you want a weekly check, have your agent or a cron job call it. [`history`](/docs/api/history) returns your past checks for a domain, newest first, and is free. Call it before re-checking to see whether a fresh check is worth the credits. ## Page audit [`audit_page`](/docs/api/audit-page) fetches one URL and grades its structured data and answer structure. It is hygiene, not the lever: being readable is necessary, being cited is the goal. ## What comes back Every call, over REST or MCP, returns the same three parts: a one-line `summary` your agent can repeat, the full `data`, and what the call cost. A check, cut short: ```json { "summary": "ttsensei.com cited by 2 of 4 engines that answered (perplexity #2, openai #3). Not cited by claude, gemini. Since 7 days ago: openai #5 → #3; claude dropped you.", "data": { "domain": "ttsensei.com", "prompt": "best table tennis blade for beginners", "engines": [ { "engine": "perplexity", "status": "ok", "cited": true, "position": 2, … }, … ], "citedInstead": [ { "host": "www.tabletennisdaily.com", "engines": ["perplexity", "openai", "gemini"] } ], "delta": { "firstCheck": false, "perEngine": [ … ] }, … }, "credits": { "charged": 12, "remaining": 188 } } ``` On a free call `charged` is `0` and `remaining` is `null`. The full response for every call, field by field, is in the [API reference](/docs/api). ## Engines Perplexity, ChatGPT, Claude and Gemini, each with web search enabled. Google organic rank and AI Overview presence are separate calls ([`google_rank`](/docs/api/google-rank)). Per-engine notes on how citations are read are on [/engines](/engines). `GET /me` lists the engines this server can ask right now, and `check_visibility` accepts an `engines` list if you want fewer than four. --- # Authentication Source: https://geondex.com/docs/authentication > Create an API key, send it as a bearer token on REST and MCP, and check it with the free GET /me. ## Create a key Sign in on [your account page](/account) and create a key. Keys start with `gx_`. You can have several and revoke any of them there; a revoked key stops working at once. Treat a key like a password. Keep it in an environment variable such as `GEONDEX_API_KEY`, not in code you commit. ## Send it on every request Send the key as a bearer token: ```bash curl -s https://geondex.com/api/v1/me -H "Authorization: Bearer gx_YOUR_KEY" ``` An `X-API-Key: gx_…` header works too. The [MCP server](/docs/mcp) takes the same `Authorization: Bearer gx_…` header on `https://geondex.com/mcp`. ## Check a key with GET /me [`GET /api/v1/me`](/docs/api/me) is free. It shows who the key belongs to, the engines this server can ask, and both credit buckets: ```json { "userId": "Zq3vN8pL2mT7wR5kB9cY1dF4", "email": "you@example.com", "engines": ["perplexity", "openai", "claude", "gemini"], "credits": { "plan": "starter", "monthlyAllowance": 5000, "allowanceRemaining": 4388, "packRemaining": 1500, "total": 5888 } } ``` `total` is `allowanceRemaining` plus `packRemaining`. Monthly credits are spent first. See [Credits & pricing rules](/docs/credits). ## When the key is wrong | HTTP | `error.code` | Meaning | | ----- | ----------------- | --------------------------------------------------------------------------------------------------- | | `401` | `unauthorized` | No key was sent. Add the Authorization header. | | `401` | `invalid_api_key` | The key was sent but is wrong or has been revoked. Create a new one on your account page. | | `429` | `rate_limited` | More than 1,000 requests today on this key. Wait the number of seconds in the `Retry-After` header. | The full list is on [Errors & rate limits](/docs/errors). ## Browsers and CORS The API is for scripts and agents; browser calls from other origins are not supported. To try a call in the browser, use the [playground](/playground). ## Machine-readable spec The OpenAPI 3.1 document is at [/api/v1/openapi.json](/api/v1/openapi.json). It needs no key, and it declares the bearer scheme, so a client generated from it sends the header for you. --- # Credits & pricing rules Source: https://geondex.com/docs/credits > What every call costs, the two kinds of credits, when monthly credits reset, and what happens at zero. ## Two kinds of credits You have two kinds of credits. * **Monthly credits** come with your plan. On Free they reset on the 1st of each month (UTC). On a paid plan they reset on your billing date each month. Unused monthly credits do not roll over. * **Purchased credits** come from a pay-as-you-go pack, never expire, and are only spent once the monthly ones are gone. ## Plans and packs | Plan | Price | Credits | | -------- | ----------- | -------------- | | Free | $0 | 200 a month | | Starter | $49 a month | 5,000 a month | | Standard | $99 a month | 15,000 a month | Packs: 1,500 credits for $25, 5,000 for $75. They never expire and are spent after your monthly credits. A check on all four engines costs 12 credits, so Free is about 16 four-engine checks a month, Starter about 416, Standard about 1,250. Plans, packs and the full price list are on [/pricing](/pricing). ## What each call costs | Call | Credits | | ------------------------------------------------ | --------------------------------------------- | | [`check_visibility`](/docs/api/check-visibility) | 3 per engine that answers, so 12 for all four | | [`suggest_prompts`](/docs/api/suggest-prompts) | 1 | | [`audit_page`](/docs/api/audit-page) | 1 | | [`google_rank`](/docs/api/google-rank) | 1 | | [`keyword_volume`](/docs/api/keyword-volume) | 1 per 10 keywords, minimum 20 per call | | [`check_crawlers`](/docs/api/check-crawlers) | Free | | [`history`](/docs/api/history) | Free | | [`GET /me`](/docs/api/me) | Free | Keyword volume costs 1 credit per 10 keywords, with a minimum of 20 credits per call. So 1 to 200 keywords cost 20 credits, and 1,000 keywords cost 100. Send your keywords together in one call, not one at a time. ## What is never charged * An engine that times out is never charged. * A call that returns an error costs nothing. * Free calls never touch your balance. Every response says what it cost in `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ## At zero credits Out of credits → a `402` `credits_exhausted` error with a link to upgrade or buy a pack. The message says how many credits the call needs and how many you have: ```text credits_exhausted: This call needs 12 credits; you have 5. Your free plan gives 200 credits a month — upgrade or buy a pack at https://geondex.com/account. ``` Nothing is queued and nothing is charged automatically. --- # Errors & rate limits Source: https://geondex.com/docs/errors > Every error code the REST API and the MCP server return, what to do about each, and the limits on keys and calls. ## One error body Every REST error has the same body, so branch on `error.code`, not on the message: ```json {"error":{"code":"credits_exhausted","message":"This call needs 12 credits; you have 5. …"}} ``` A call that returns an error is not charged. ## Error codes | HTTP | `error.code` | What to do | | ----- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request` | Bad input or malformed JSON. The message names the field. | | `400` | `invalid_domain` · `blocked_host` · `engine_unavailable` | The domain does not look like example.com, is not a public host, or you asked for an engine this server does not have (the message lists the ones it has). | | `401` | `unauthorized` | No key was sent. Add the Authorization header. | | `401` | `invalid_api_key` | The key was sent but is wrong or has been revoked. Create a new one on your account page. | | `402` | `credits_exhausted` | Not enough credits for this call. The message says how many it needs and links to upgrade or buy a pack. | | `404` | `not_found` | This tool is not available on this server. `GET /api/v1/me` lists what is. | | `415` | `unsupported_media_type` | A POST without `content-type: application/json`. | | `429` | `rate_limited` | More than 1,000 requests today on this key. Wait the number of seconds in the `Retry-After` header. | | `429` | `spend_ceiling` | The daily safety limit on checks for your account was reached. Try again tomorrow. | | `502` | `upstream_failed` · `fetch_failed` · `site_unreachable` · `suggest_failed` | A site we had to fetch, or a data source behind the tool, failed. Safe to retry. | | `502` | `not_configured` · `no_engines` | The tool is not set up on this server. Retrying will not help; tell us. | | `500` | `internal` | Our bug. Retry once, then email us. | ## Errors over MCP A bad or missing key is refused before any tool runs: HTTP `401` with a JSON-RPC error whose `data.code` is `invalid_api_key`. Over 1,000 requests a day on one key gets HTTP `429`, `data.code` `rate_limited`, and a `Retry-After` header. A tool that fails comes back as a normal tool result with `isError: true` and one line of text, `code: message`, using the same codes as the REST API. For example: ```text credits_exhausted: This call needs 12 credits; you have 5. Your free plan gives 200 credits a month — upgrade or buy a pack at https://geondex.com/account. ``` A call that fails is not charged. A raw HTTP call to `https://geondex.com/mcp` whose `accept` header does not list **both** `application/json` and `text/event-stream` gets a `406`. See [Any other client, or raw HTTP](/docs/mcp#any-other-client-or-raw-http). ## Limits * Every tool returns in under 25 seconds. * An engine that does not answer within 20 seconds is reported as timed out and is not charged. * Keys are limited to 1,000 requests a day. The `429` carries a `Retry-After` header. * A daily safety limit on checks protects your account from a runaway loop (`spend_ceiling`). * When credits run out you get a clear error with a link to upgrade or buy a pack; nothing is queued and nothing is charged automatically. Prices are on [/pricing](/pricing). --- # Data & privacy Source: https://geondex.com/docs/privacy > What Geondex stores for your key, and how to delete it. Stored for your key: the domain, the prompt, each engine's cited pages and a short excerpt of its answer, timestamps, and your credit ledger. Nothing else. Delete your account and it all goes. The full text is on [/legal/privacy](/legal/privacy). Security practices are on [/legal/security](/legal/security). Questions: [hello@geondex.com](mailto:hello@geondex.com). --- # REST API Source: https://geondex.com/docs/api > Ask four AI engines about your site over HTTP. One key, seven endpoints plus GET /me, JSON in and out, with a full response example for each call. The same seven calls as the [MCP server](/docs/mcp), over HTTP. For scripts, cron jobs and anything that is not an MCP client. Prefer to click than to curl? The [playground](/playground) runs any of these endpoints from your browser and shows you the exact request to copy. ## Base URL and auth ```http https://geondex.com/api/v1 ``` Create a key on [your account page](/account) and send it on every request as a bearer token. An `X-API-Key: gx_…` header works too. Details: [Authentication](/docs/authentication). ```bash curl -s https://geondex.com/api/v1/me -H "Authorization: Bearer gx_YOUR_KEY" ``` POST bodies are JSON and need `content-type: application/json`. GET calls take query parameters. ## Endpoints | Endpoint | Reference | What it does | Cost | | ------------------------------ | ----------------------------------------------- | ----------------------------------------------------------- | ----------------------- | | `GET /api/v1/me` | [me](/docs/api/me) | Your key, the engines available, and your credit balance | Free | | `POST /api/v1/check` | [check\_visibility](/docs/api/check-visibility) | Ask four AI engines whether your domain is cited, right now | 3 credits / engine | | `GET /api/v1/history` | [history](/docs/api/history) | Your stored record of past checks for your domain | Free | | `POST /api/v1/prompts/suggest` | [suggest\_prompts](/docs/api/suggest-prompts) | Buyer-intent questions to check your domain against | 1 credit | | `GET /api/v1/crawlers` | [check\_crawlers](/docs/api/check-crawlers) | Whether robots.txt blocks the crawlers that cite you | Free | | `POST /api/v1/audit` | [audit\_page](/docs/api/audit-page) | Grade one URL's structured data for AI citation | 1 credit | | `POST /api/v1/rank` | [google\_rank](/docs/api/google-rank) | Google position for a keyword, plus AI Overview presence | 1 credit | | `POST /api/v1/volume` | [keyword\_volume](/docs/api/keyword-volume) | Monthly search volume for up to 1,000 keywords | 1 / 10 keywords, min 20 | ## Requests and responses Every tool response is `{ summary, data, credits: { charged, remaining } }`: a one-line `summary` an agent can repeat, the full `data`, and what the call cost. On a free call `charged` is `0` and `remaining` is `null`. Each endpoint page above has one real-shaped request and response. Field names and types are exactly what the API returns: every example is parsed through the tool's own output schema by a test. Full prices are on [/pricing](/pricing) and in [Credits & pricing rules](/docs/credits). Machine-readable spec: [/api/v1/openapi.json](/api/v1/openapi.json) (OpenAPI 3.1, no key needed). ## Handy one-liners ```bash # Who cites me, and who is cited instead? curl -s https://geondex.com/api/v1/check -H "Authorization: Bearer gx_…" -H "content-type: application/json" -d '{"domain":"ttsensei.com","prompt":"best table tennis blade for beginners"}' | jq '.summary, .data.citedInstead' # What did I check before, and what did it say? curl -s "https://geondex.com/api/v1/history?domain=ttsensei.com" -H "Authorization: Bearer gx_…" | jq '.data.batches[0]' # Google rank next to the AI answer curl -s https://geondex.com/api/v1/rank -H "Authorization: Bearer gx_…" -H "content-type: application/json" -d '{"keyword":"best table tennis blade for beginners","domain":"ttsensei.com"}' | jq .summary ``` ## Errors Every error has the same body, so branch on `error.code`, not on the message: ```json {"error":{"code":"credits_exhausted","message":"This call needs 12 credits; you have 5. …"}} ``` A call that returns an error is not charged. Every code and what to do: [Errors & rate limits](/docs/errors). Prefer your agent to call these itself? Install the [MCP server](/docs/mcp). --- # me Source: https://geondex.com/docs/api/me > Your key, the engines available, and your credit balance. ```http GET https://geondex.com/api/v1/me ``` * **Cost:** Free * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open the playground](/playground) Your key, the engines available, and your credit balance ## Request No parameters. Send only the key. ### Example request ```bash curl -s "https://geondex.com/api/v1/me" -H "Authorization: Bearer gx_YOUR_KEY" ``` ## Response `200` with a JSON object. ### Fields * `userId` · `string` * `email` · `string` * `engines` · `string[]` * `credits` · `object` * `credits.plan` · `"free" | "starter" | "standard"` * `credits.monthlyAllowance` · `number` * `credits.allowanceRemaining` · `number` * `credits.packRemaining` · `number` * `credits.total` · `number` ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "userId": "Zq3vN8pL2mT7wR5kB9cY1dF4", "email": "you@example.com", "engines": [ "perplexity", "openai", "claude", "gemini" ], "credits": { "plan": "starter", "monthlyAllowance": 5000, "allowanceRemaining": 4388, "packRemaining": 1500, "total": 5888 } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | ---------------------- | | `401` | Missing or invalid key | | `429` | Rate limited | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # check_visibility Source: https://geondex.com/docs/api/check-visibility > Ask four AI engines whether your domain is cited, right now. ```http POST https://geondex.com/api/v1/check ``` * **Cost:** 3 credits / engine * **MCP tool:** `check_visibility` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open check\_visibility in the playground](/playground?tool=check_visibility) A bare agent can ask Perplexity, ChatGPT, Claude and Gemini itself, but it would have to reconcile four different citation formats by hand and has no memory between calls. This tool returns one normalized answer across all four — cited yes/no, your position, and who is cited instead — plus a stored delta against the last identical check. Costs 3 credits per engine that answers, so 12 for all four (timeouts are free). Takes up to 25 s. Call `history` first if you only need past results. Not available on every deployment: returns 404 `not_found` when this server is not configured for it. GET /me lists what is configured. ## Request ### Request body (JSON) * `domain` · `string` · required — Your site, e.g. ttsensei.com (no scheme). 3–253 characters * `prompt` · `string` · required — The question a buyer would ask an AI engine, e.g. 'best table tennis blade for beginners'. 3–300 characters * `engines` · `("perplexity" | "openai" | "claude" | "gemini")[]` · optional — Subset of engines. Default: all four. 3 credits per engine that answers. 1–4 items. Each one of `perplexity`, `openai`, `claude`, `gemini` ### Example request ```bash curl -s https://geondex.com/api/v1/check -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -d '{"domain":"ttsensei.com","prompt":"best table tennis blade for beginners"}' ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `domain` · `string` * `prompt` · `string` * `batchId` · `string` * `checkedAt` · `number` * `engines` · `object[]` * `engines[].engine` · `"perplexity" | "openai" | "claude" | "gemini"` * `engines[].status` · `"ok" | "timeout" | "error"` * `engines[].cited` · `boolean` — Whether your domain is cited in this engine's answer * `engines[].position` · `number | null` — 1-based rank among this engine's own citations; null when not cited * `engines[].citations` · `object[]` * `engines[].citations[].url` · `string` * `engines[].citations[].host` · `string` * `engines[].citations[].title` · `string | null` * `engines[].citations[].position` · `number` * `engines[].answerExcerpt` · `string | null` * `engines[].costUsdMicro` · `number` * `engines[].durationMs` · `number` * `engines[].error` · `string` · optional * `citedInstead` · `object[]` * `citedInstead[].host` · `string` * `citedInstead[].engines` · `string[]` * `delta` · `object` — What changed since the previous identical check for this domain+prompt * `delta.firstCheck` · `boolean` — true when there is no earlier check to compare against * `delta.checkedAt` · `number | null` * `delta.perEngine` · `object[]` * `delta.perEngine[].engine` · `"perplexity" | "openai" | "claude" | "gemini"` * `delta.perEngine[].citedBefore` · `boolean | null` * `delta.perEngine[].citedNow` · `boolean` * `delta.perEngine[].positionBefore` · `number | null` * `delta.perEngine[].positionNow` · `number | null` ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "ttsensei.com cited by 2 of 4 engines that answered (perplexity #2, openai #3). Not cited by claude, gemini. Since 7 days ago: openai #5 → #3; claude dropped you.", "data": { "domain": "ttsensei.com", "prompt": "best table tennis blade for beginners", "batchId": "5f0c9e2a-8d4b-4c1e-9a7f-2b6d3e8c1f40", "checkedAt": 1790258712000, "engines": [ { "engine": "perplexity", "status": "ok", "cited": true, "position": 2, "citations": [ { "url": "https://www.tabletennisdaily.com/best-beginner-blades", "host": "www.tabletennisdaily.com", "title": "Best Table Tennis Blades for Beginners", "position": 1 }, { "url": "https://ttsensei.com/blades/beginner", "host": "ttsensei.com", "title": "Beginner blades compared", "position": 2 } ], "answerExcerpt": "For a first blade, pick an all-wood ALL or ALL+ blade. It gives more control than a carbon blade…", "costUsdMicro": 6100, "durationMs": 7420 }, { "engine": "openai", "status": "ok", "cited": true, "position": 3, "citations": [ { "url": "https://www.tabletennisdaily.com/best-beginner-blades", "host": "www.tabletennisdaily.com", "title": "Best Table Tennis Blades for Beginners", "position": 1 }, { "url": "https://www.megaspin.net/blades/", "host": "www.megaspin.net", "title": "Table Tennis Blades", "position": 2 }, { "url": "https://ttsensei.com/blades/beginner", "host": "ttsensei.com", "title": "Beginner blades compared", "position": 3 } ], "answerExcerpt": "Good beginner blades are slower and softer, so you can learn strokes…", "costUsdMicro": 11800, "durationMs": 12950 }, { "engine": "claude", "status": "ok", "cited": false, "position": null, "citations": [ { "url": "https://www.megaspin.net/blades/", "host": "www.megaspin.net", "title": "Table Tennis Blades", "position": 1 } ], "answerExcerpt": "Most coaches suggest an all-round wooden blade for the first year…", "costUsdMicro": 14300, "durationMs": 15210 }, { "engine": "gemini", "status": "ok", "cited": false, "position": null, "citations": [ { "url": "https://www.tabletennisdaily.com/best-beginner-blades", "host": "www.tabletennisdaily.com", "title": null, "position": 1 } ], "answerExcerpt": "A beginner should start with a control-oriented blade such as…", "costUsdMicro": 3900, "durationMs": 6880 } ], "citedInstead": [ { "host": "www.tabletennisdaily.com", "engines": [ "perplexity", "openai", "gemini" ] }, { "host": "www.megaspin.net", "engines": [ "openai", "claude" ] } ], "delta": { "firstCheck": false, "checkedAt": 1789638063000, "perEngine": [ { "engine": "perplexity", "citedBefore": true, "citedNow": true, "positionBefore": 2, "positionNow": 2 }, { "engine": "openai", "citedBefore": true, "citedNow": true, "positionBefore": 5, "positionNow": 3 }, { "engine": "claude", "citedBefore": true, "citedNow": false, "positionBefore": 4, "positionNow": null }, { "engine": "gemini", "citedBefore": false, "citedNow": false, "positionBefore": null, "positionNow": null } ] } }, "credits": { "charged": 12, "remaining": 188 } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # history Source: https://geondex.com/docs/api/history > Your stored record of past checks for your domain. ```http GET https://geondex.com/api/v1/history ``` * **Cost:** Free * **MCP tool:** `history` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open history in the playground](/playground?tool=history) Your stored record of past checks for your domain — an agent has no memory between sessions; this does. Newest first, with per-engine cited/position for each. Free. Use it before re-checking to see whether a fresh check is worth a credit. ## Request ### Query parameters * `domain` · `string` · required — 3–253 characters * `prompt` · `string` · optional — Narrow to one prompt. 3–300 characters * `limit` · `integer` · optional — 1–100. Default `20` ### Example request ```bash curl -s "https://geondex.com/api/v1/history?domain=ttsensei.com&limit=2" -H "Authorization: Bearer gx_YOUR_KEY" ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `domain` · `string` * `batches` · `object[]` * `batches[].batchId` · `string` * `batches[].prompt` · `string` * `batches[].createdAt` · `number` * `batches[].engines` · `object[]` * `batches[].engines[].engine` · `string` * `batches[].engines[].status` · `string` * `batches[].engines[].cited` · `boolean` — Whether your domain was cited by this engine in this batch * `batches[].engines[].position` · `number | null` — 1-based rank among this engine's own citations; null when not cited ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "Showing the 2 most recent checks for ttsensei.com; latest 2026-09-24: cited by 2 of 4 engines.", "data": { "domain": "ttsensei.com", "batches": [ { "batchId": "5f0c9e2a-8d4b-4c1e-9a7f-2b6d3e8c1f40", "prompt": "best table tennis blade for beginners", "createdAt": 1790258712000, "engines": [ { "engine": "perplexity", "status": "ok", "cited": true, "position": 2 }, { "engine": "openai", "status": "ok", "cited": true, "position": 3 }, { "engine": "claude", "status": "ok", "cited": false, "position": null }, { "engine": "gemini", "status": "ok", "cited": false, "position": null } ] }, { "batchId": "a13e7b90-2c5d-4f8a-b6e1-9d0c4f2a7e35", "prompt": "best table tennis blade for beginners", "createdAt": 1789638063000, "engines": [ { "engine": "perplexity", "status": "ok", "cited": true, "position": 2 }, { "engine": "openai", "status": "ok", "cited": true, "position": 5 }, { "engine": "claude", "status": "ok", "cited": true, "position": 4 }, { "engine": "gemini", "status": "ok", "cited": false, "position": null } ] } ] }, "credits": { "charged": 0, "remaining": null } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # suggest_prompts Source: https://geondex.com/docs/api/suggest-prompts > Buyer-intent questions to check your domain against. ```http POST https://geondex.com/api/v1/prompts/suggest ``` * **Cost:** 1 credit * **MCP tool:** `suggest_prompts` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open suggest\_prompts in the playground](/playground?tool=suggest_prompts) The questions real buyers put to AI engines in your domain's niche, grounded in your domain's own homepage — an agent guessing prompts from the domain name alone gets the niche wrong. Returns prompts ready to feed into check\_visibility. 1 credit. If you already know the prompts, skip this and call check\_visibility directly. Not available on every deployment: returns 404 `not_found` when this server is not configured for it. GET /me lists what is configured. ## Request ### Request body (JSON) * `domain` · `string` · required — Your site, e.g. ttsensei.com (no scheme). 3–253 characters * `count` · `integer` · optional — How many prompts to return. Default 5. 1–10. Default `5` ### Example request ```bash curl -s https://geondex.com/api/v1/prompts/suggest -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -d '{"domain":"ttsensei.com","count":3}' ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `domain` · `string` * `prompts` · `object[]` * `prompts[].text` · `string` — Feed this straight into check\_visibility * `prompts[].intent` · `"commercial" | "comparison" | "informational"` — What the buyer is trying to do. suggestKeywords defaults to informational. ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "3 prompts for ttsensei.com: \"best table tennis blade for beginners\", \"Butterfly Viscaria vs Timo Boll ALC\", \"how to choose table tennis rubber\".", "data": { "domain": "ttsensei.com", "prompts": [ { "text": "best table tennis blade for beginners", "intent": "commercial" }, { "text": "Butterfly Viscaria vs Timo Boll ALC", "intent": "comparison" }, { "text": "how to choose table tennis rubber", "intent": "informational" } ] }, "credits": { "charged": 1, "remaining": 187 } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # check_crawlers Source: https://geondex.com/docs/api/check-crawlers > Whether robots.txt blocks the crawlers that cite you. ```http GET https://geondex.com/api/v1/crawlers ``` * **Cost:** Free * **MCP tool:** `check_crawlers` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open check\_crawlers in the playground](/playground?tool=check_crawlers) Which of the crawlers AI engines actually cite from are blocked by your domain's robots.txt — an agent can read robots.txt but not which user-agents matter for citations as opposed to the ones that only train. Free, cached one hour. ## Request ### Query parameters * `domain` · `string` · required — Your site, e.g. ttsensei.com (no scheme). 3–253 characters ### Example request ```bash curl -s "https://geondex.com/api/v1/crawlers?domain=ttsensei.com" -H "Authorization: Bearer gx_YOUR_KEY" ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `domain` · `string` * `hasRobotsTxt` · `boolean` — No robots.txt at all means allow-all, not blocked * `crawlers` · `object[]` * `crawlers[].agent` · `string` * `crawlers[].engine` · `string` * `crawlers[].kind` · `"citation" | "training"` * `crawlers[].blocked` · `boolean` * `crawlers[].advisoryOnly` · `boolean` · optional — A robots.txt disallow is advisory for this agent — it fetches regardless * `blockedCitation` · `object[]` — Citation crawlers genuinely blocked; excludes advisory-only * `blockedCitation[].agent` · `string` * `blockedCitation[].engine` · `string` * `blockedCitation[].kind` · `"citation" | "training"` * `blockedCitation[].blocked` · `boolean` * `blockedCitation[].advisoryOnly` · `boolean` · optional — A robots.txt disallow is advisory for this agent — it fetches regardless * `blockedTraining` · `object[]` * `blockedTraining[].agent` · `string` * `blockedTraining[].engine` · `string` * `blockedTraining[].kind` · `"citation" | "training"` * `blockedTraining[].blocked` · `boolean` * `blockedTraining[].advisoryOnly` · `boolean` · optional — A robots.txt disallow is advisory for this agent — it fetches regardless ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "ttsensei.com allows all citation crawlers (training-only crawlers blocked: GPTBot, CCBot).", "data": { "domain": "ttsensei.com", "hasRobotsTxt": true, "crawlers": [ { "agent": "OAI-SearchBot", "engine": "ChatGPT", "kind": "citation", "blocked": false }, { "agent": "ChatGPT-User", "engine": "ChatGPT", "kind": "citation", "blocked": false }, { "agent": "Claude-SearchBot", "engine": "Claude", "kind": "citation", "blocked": false }, { "agent": "Claude-User", "engine": "Claude", "kind": "citation", "blocked": false }, { "agent": "PerplexityBot", "engine": "Perplexity", "kind": "citation", "blocked": false }, { "agent": "Perplexity-User", "engine": "Perplexity", "kind": "citation", "blocked": false, "advisoryOnly": true }, { "agent": "GPTBot", "engine": "ChatGPT", "kind": "training", "blocked": true }, { "agent": "ClaudeBot", "engine": "Claude", "kind": "training", "blocked": false }, { "agent": "CCBot", "engine": "Common Crawl", "kind": "training", "blocked": true }, { "agent": "Google-Extended", "engine": "Gemini", "kind": "training", "blocked": false } ], "blockedCitation": [], "blockedTraining": [ { "agent": "GPTBot", "engine": "ChatGPT", "kind": "training", "blocked": true }, { "agent": "CCBot", "engine": "Common Crawl", "kind": "training", "blocked": true } ] }, "credits": { "charged": 0, "remaining": null } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # audit_page Source: https://geondex.com/docs/api/audit-page > Grade one URL's structured data for AI citation. ```http POST https://geondex.com/api/v1/audit ``` * **Cost:** 1 credit * **MCP tool:** `audit_page` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open audit\_page in the playground](/playground?tool=audit_page) Graded against what AI engines actually need to quote a page — an agent can read the HTML but has no rubric for citation-readiness. Fetches one URL and returns the schema types present, the issues with fixes, and paste-ready JSON-LD. 1 credit. This is hygiene, not the lever: check\_visibility tells you whether you are cited; this tells you whether the page is readable. ## Request ### Request body (JSON) * `url` · `string` · required — One full page URL, e.g. [https://ttsensei.com/blades](https://ttsensei.com/blades). At most 2,048 characters. A full URL ### Example request ```bash curl -s https://geondex.com/api/v1/audit -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -d '{"url":"https://ttsensei.com/blades"}' ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `url` · `string` * `schemaTypes` · `string[]` * `issues` · `object[]` * `issues[].code` · `string` * `issues[].severity` · `"error" | "warn" | "tip"` * `issues[].title` · `string` * `issues[].fix` · `string` · optional — Plain-text guidance * `issues[].detail` · `string` · optional * `issues[].snippet` · `string` · optional — Paste-ready JSON-LD * `score` · `number` — 0-100 citation-readiness ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "https://ttsensei.com/blades: score 68/100, 2 issues (top: Add Organization schema).", "data": { "url": "https://ttsensei.com/blades", "schemaTypes": [ "Product" ], "issues": [ { "code": "missing_organization", "severity": "warn", "title": "Add Organization schema", "fix": "Organization is the highest-impact schema type. It tells engines who you are, establishes brand identity, and links pages back to a canonical entity. Add once to the homepage or head.", "snippet": "{\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n \"@id\": \"https://ttsensei.com/#organization\",\n \"name\": \"ttsensei.com\",\n \"url\": \"https://ttsensei.com\",\n \"logo\": \"https://ttsensei.com/logo.png\"\n}" }, { "code": "tip_add_breadcrumb", "severity": "tip", "title": "Add BreadcrumbList schema", "fix": "Breadcrumbs help engines understand site hierarchy and improve link previews in answers. Add to every non-home page." } ], "score": 68 }, "credits": { "charged": 1, "remaining": 186 } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # google_rank Source: https://geondex.com/docs/api/google-rank > Google position for a keyword, plus AI Overview presence. ```http POST https://geondex.com/api/v1/rank ``` * **Cost:** 1 credit * **MCP tool:** `google_rank` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open google\_rank in the playground](/playground?tool=google_rank) Google organic position of your domain for the keyword (US, desktop, top 20), plus whether an AI Overview appears and whether it cites your domain — data no LLM API returns. 1 credit. Pair with check\_visibility to see Google rank next to AI citations for the same question. Not available on every deployment: returns 404 `not_found` when this server is not configured for it. GET /me lists what is configured. ## Request ### Request body (JSON) * `keyword` · `string` · required — The search phrase, e.g. best table tennis blade. 1–200 characters * `domain` · `string` · required — Your site, e.g. ttsensei.com (no scheme). 3–253 characters ### Example request ```bash curl -s https://geondex.com/api/v1/rank -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -d '{"keyword":"best table tennis blade for beginners","domain":"ttsensei.com"}' ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `keyword` · `string` * `domain` · `string` * `rankGroup` · `number | null` — 1-based organic rank group; null when the domain is not in the top 20 * `rankAbsolute` · `number | null` — Position counting every SERP block, not just organic results * `url` · `string | null` — The ranking page; null when not ranked * `aiOverviewPresent` · `boolean` — An AI Overview box was on the page at all, whoever it cites * `aiOverviewCitesDomain` · `boolean` ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "\"best table tennis blade for beginners\": ttsensei.com #4 on Google; AI Overview present, does not cite you.", "data": { "keyword": "best table tennis blade for beginners", "domain": "ttsensei.com", "rankGroup": 4, "rankAbsolute": 7, "url": "https://ttsensei.com/blades/beginner", "aiOverviewPresent": true, "aiOverviewCitesDomain": false }, "credits": { "charged": 1, "remaining": 185 } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # keyword_volume Source: https://geondex.com/docs/api/keyword-volume > Monthly search volume for up to 1,000 keywords. ```http POST https://geondex.com/api/v1/volume ``` * **Cost:** 1 / 10 keywords, min 20 * **MCP tool:** `keyword_volume` * **Auth:** `Authorization: Bearer gx_…` (see [Authentication](/docs/authentication)) * **Try it:** [Open keyword\_volume in the playground](/playground?tool=keyword_volume) How many people actually search each phrase every month — an agent can guess demand but cannot measure it. Monthly Google search volume, CPC and competition for up to 1,000 keywords (US) in one call. 1 credit per 10 distinct keywords, minimum 20 credits per call: up to 200 keywords cost 20 credits, 1,000 cost 100. Send every keyword you care about in one call rather than one at a time. Use it to decide which prompts from suggest\_prompts are worth a check. Not available on every deployment: returns 404 `not_found` when this server is not configured for it. GET /me lists what is configured. ## Request ### Request body (JSON) * `keywords` · `string[]` · required — Up to 1,000 phrases in ONE call. Duplicates are collapsed. 1 credit per 10 distinct keywords, minimum 20 credits per call — anything up to 200 keywords costs the same 20 credits, so batch. 1–1,000 items ### Example request ```bash curl -s https://geondex.com/api/v1/volume -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -d '{"keywords":["table tennis blade","best table tennis blade for beginners","ttsensei blade finder"]}' ``` ## Response `200` with the envelope every tool returns: a one-line `summary`, the full `data`, and `credits: { charged, remaining }`. On a free call `charged` is `0` and `remaining` is `null`. ### Fields of `data` * `rows` · `object[]` * `rows[].keyword` · `string` * `rows[].searchVolume` · `number | null` — Average monthly Google searches; null when the API has no data * `rows[].cpc` · `number | null` * `rows[].competition` · `string | null` — Google Ads competition: LOW, MEDIUM or HIGH * `rows[].competitionIndex` · `number | null` — Google Ads competition index, 0-100 ### Example response Real-shaped, checked against the output schema by a test. Long lists are shortened. ```json { "summary": "3 keywords; highest volume \"table tennis blade\" at 6600/mo.", "data": { "rows": [ { "keyword": "table tennis blade", "searchVolume": 6600, "cpc": 0.84, "competition": "HIGH", "competitionIndex": 100 }, { "keyword": "best table tennis blade for beginners", "searchVolume": 390, "cpc": 0.62, "competition": "HIGH", "competitionIndex": 88 }, { "keyword": "ttsensei blade finder", "searchVolume": null, "cpc": null, "competition": null, "competitionIndex": null } ] }, "credits": { "charged": 20, "remaining": 165 } } ``` ## Errors Every error has the body `{"error":{"code":"…","message":"…"}}` and is not charged. | HTTP | When | | ----- | --------------------------------- | | `400` | Bad input | | `401` | Missing or invalid key | | `402` | Out of credits | | `404` | Tool not available on this server | | `429` | Rate limited | | `502` | Upstream failure | Every `error.code` and what to do about it: [Errors & rate limits](/docs/errors). --- # MCP server Source: https://geondex.com/docs/mcp > Install the Geondex MCP server in Claude Code, Cursor, VS Code, Windsurf, Codex or Claude Desktop and ask four AI engines about your site. Your agent can already write GEO advice and read robots.txt. It cannot ask Perplexity, ChatGPT, Claude and Gemini the same question and compare who they cite, and it cannot remember last month. That is what this server adds. ## Install Get a key on [your account page](/account). Every client sends it the same way: an `Authorization: Bearer gx_…` header on `https://geondex.com/mcp`. Replace `gx_YOUR_KEY` below with your key. ### Claude Code Run once in your terminal: ```bash claude mcp add --transport http geondex https://geondex.com/mcp --header "Authorization: Bearer gx_YOUR_KEY" ``` ### Cursor Add to `.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` for every project: ```json { "mcpServers": { "geondex": { "url": "https://geondex.com/mcp", "headers": { "Authorization": "Bearer gx_YOUR_KEY" } } } } ``` ### VS Code (GitHub Copilot agent mode) Add to `.vscode/mcp.json`. VS Code asks for the key once and stores it securely, so it never sits in the file: ```json { "inputs": [ { "type": "promptString", "id": "geondex-key", "description": "Geondex API key", "password": true } ], "servers": { "geondex": { "type": "http", "url": "https://geondex.com/mcp", "headers": { "Authorization": "Bearer ${input:geondex-key}" } } } } ``` ### Windsurf Open the MCP settings in Cascade, choose to view the raw config, and add this to `mcp_config.json`: ```json { "mcpServers": { "geondex": { "serverUrl": "https://geondex.com/mcp", "headers": { "Authorization": "Bearer gx_YOUR_KEY" } } } } ``` ### OpenAI Codex CLI Put your key in the `GEONDEX_API_KEY` environment variable, then run once. Codex reads the key from the variable, so it never sits in a config file: ```bash codex mcp add geondex --url https://geondex.com/mcp --bearer-token-env-var GEONDEX_API_KEY ``` Or add it to `~/.codex/config.toml` by hand: ```toml [mcp_servers.geondex] url = "https://geondex.com/mcp" bearer_token_env_var = "GEONDEX_API_KEY" ``` ### Claude Desktop Claude Desktop reaches remote servers through a small local bridge (needs Node.js). Settings → Developer → Edit Config, then add to `claude_desktop_config.json` and restart: ```json { "mcpServers": { "geondex": { "command": "npx", "args": ["-y", "mcp-remote", "https://geondex.com/mcp", "--header", "Authorization: Bearer ${GEONDEX_API_KEY}"], "env": { "GEONDEX_API_KEY": "gx_YOUR_KEY" } } } } ``` On Windows, spaces inside `"args"` can break. Use this form instead: ```json "args": ["-y", "mcp-remote", "https://geondex.com/mcp", "--header", "Authorization:${GEONDEX_AUTH}"], "env": { "GEONDEX_AUTH": "Bearer gx_YOUR_KEY" } ``` ### Any other client, or raw HTTP The endpoint speaks streamable HTTP and holds no session. Any client that can send a header works. For raw HTTP, your `accept` header must list **both** `application/json` and `text/event-stream`. Sending only one gets you a `406`. ```bash curl -X POST https://geondex.com/mcp -H "Authorization: Bearer gx_YOUR_KEY" -H "content-type: application/json" -H "accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` ## Tools | Tool | What it does | Cost | | ------------------------------------------------ | ----------------------------------------------------------- | ----------------------- | | [`check_visibility`](/docs/api/check-visibility) | Ask four AI engines whether your domain is cited, right now | 3 credits / engine | | [`history`](/docs/api/history) | Your stored record of past checks for your domain | Free | | [`suggest_prompts`](/docs/api/suggest-prompts) | Buyer-intent questions to check your domain against | 1 credit | | [`check_crawlers`](/docs/api/check-crawlers) | Whether robots.txt blocks the crawlers that cite you | Free | | [`audit_page`](/docs/api/audit-page) | Grade one URL's structured data for AI citation | 1 credit | | [`google_rank`](/docs/api/google-rank) | Google position for a keyword, plus AI Overview presence | 1 credit | | [`keyword_volume`](/docs/api/keyword-volume) | Monthly search volume for up to 1,000 keywords | 1 / 10 keywords, min 20 | Response times: `check_visibility` up to 25 s; `suggest_prompts`, `audit_page`, `google_rank` and `keyword_volume` up to 10 s; `check_crawlers` up to 3 s; `history` up to 2 s. `keyword_volume` takes up to 1,000 keywords in one call, and up to 200 keywords cost the same 20 credits, so send them together. Each tool name links to its REST reference: the arguments are the same, and so is the JSON that comes back. ## Three prompts to try 1. Check ttsensei.com for "best table tennis blade for beginners" on all four engines and tell me who is cited instead of me. 2. Suggest five buyer questions for aigregator.com, check each one, and list the engines where I am missing. 3. Re-run last week's checks for ttsensei.com and tell me what changed. Then look up Google rank for the ones where I lost a citation. ## What comes back Every tool returns a one-line summary first, then the full JSON. Agents act on the line and read the JSON when they need detail: ```text ttsensei.com cited by 1 of 4 engines that answered (openai #3). Not cited by perplexity, claude, gemini. Since 6 days ago: openai #5 → #3; claude dropped you. ``` `position` is a page's place in that engine's own citation list, so compare it over time for one engine rather than across two different engines. Every field of every response is in the [API reference](/docs/api) — MCP returns the same JSON. ## Errors A bad or missing key is refused before any tool runs: HTTP `401` with a JSON-RPC error whose `data.code` is `invalid_api_key`. Over 1,000 requests a day on one key gets HTTP `429`, `data.code` `rate_limited`, and a `Retry-After` header. A tool that fails comes back as a normal tool result with `isError: true` and one line of text, `code: message`, using the same codes as the REST API. For example: ```text credits_exhausted: This call needs 12 credits; you have 5. Your free plan gives 200 credits a month — upgrade or buy a pack at https://geondex.com/account. ``` A call that fails is not charged. The full list of codes is on [Errors & rate limits](/docs/errors). ## Limits Every tool returns in under 25 seconds. An engine that does not answer within 20 seconds is reported as timed out and is not charged. Keys are limited to 1,000 requests a day. When credits run out you get a clear error with a link to upgrade or buy a pack; nothing is queued and nothing is charged automatically. Prices are on [/pricing](/pricing). Prefer plain HTTP? The same seven calls are on the [REST API](/docs/api). Want your agent to know how to use them well? Add the [agent skills](/docs/mcp/skills). --- # Agent skills Source: https://geondex.com/docs/mcp/skills > Skills that teach Claude Code, Codex, Cursor and other agents to use Geondex well — install with one command. The [Geondex agent skills](https://github.com/Geondex/skills) teach your coding agent to use Geondex: they turn "does AI recommend my site?" into a few calls and a clear report. They are open source (MIT). ## Install With the [skills CLI](https://skills.sh) (works with Claude Code, Codex, Cursor and many other agents): ```bash npx skills add Geondex/skills --all ``` Or clone the repo and copy the folders in `skills/` into your agent's skills folder (for Claude Code, `~/.claude/skills/`): ```bash git clone https://github.com/Geondex/skills.git geondex-skills && cp -r geondex-skills/skills/* ~/.claude/skills/ ``` ### As a Claude Code plugin Inside Claude Code: ```text /plugin marketplace add Geondex/skills /plugin install geondex@geondex ``` Plugin skills get the plugin name as a prefix, for example `/geondex:check-ai-visibility`. ## Set your API key Create a key on [your account page](/account) (it starts with `gx_`), then put it in your environment: ```bash export GEONDEX_API_KEY=gx_your_key_here ``` The skills read the key from `GEONDEX_API_KEY`. Never commit it. ## The skills | Skill | What it does | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `geondex-api` | The base skill: auth, endpoints, credit costs, errors, rate limits and optional MCP setup. The other skills rely on it. | | `check-ai-visibility` | Is your site cited by the four AI engines for one buyer question? Position, who is cited instead, and what changed since last time. | | `find-buyer-prompts` | Finds the questions buyers ask in your niche, checks the best ones, and shows where you are cited or missing. | | `audit-ai-readiness` | Checks if robots.txt blocks the crawlers AI engines cite from, and audits a page's structured data, with fixes in order. | | `keyword-and-rank-research` | Google rank and AI Overview for a keyword, plus monthly search volume for many keywords in one batch. | The skills tell your agent to say the cost before it spends credits. Costs are in [Credits & pricing rules](/docs/credits). ## Skills or MCP? Both work, and they combine. The [MCP server](/docs/mcp) gives your agent the seven tools; the skills give it the playbook for using them (which call to make first, how to batch keywords, how to report). The skills call the [REST API](/docs/api) by default and include optional MCP setup. --- # llms.txt & Markdown Source: https://geondex.com/docs/mcp/llms-txt > Machine-readable entry points for agents and LLMs — llms.txt, llms-full.txt, a Markdown copy of every docs page, and the OpenAPI spec. Everything in these docs is also available as plain Markdown, so an agent can read it without parsing HTML. ## Entry points | URL | What it is | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | [/llms.txt](/llms.txt) | The [llms.txt](https://llmstxt.org) index: what Geondex is, how to authenticate, and a link to the Markdown version of every docs page and tool. | | [/llms-full.txt](/llms-full.txt) | Every docs page as Markdown, in one file. Paste it into a context window. | | [/api/v1/openapi.json](/api/v1/openapi.json) | The OpenAPI 3.1 document for the REST API. No key needed. | ## A Markdown copy of every page Add `.md` to any docs URL to get that page as Markdown: * [/docs.md](/docs.md) * [/docs/mcp.md](/docs/mcp.md) * [/docs/api/check-visibility.md](/docs/api/check-visibility.md) Each page also has a **Copy Markdown** button and an **Open** menu at the top, which views the Markdown or opens the page in ChatGPT, Claude or Cursor with a prompt to read it. ## For agents Point your agent at `https://geondex.com/llms.txt` first; it links to everything else. For hands-on use, install the [MCP server](/docs/mcp) or the [agent skills](/docs/mcp/skills).