---
name: citable
description: Pay-per-call SEO and AI-visibility data for AI agents — keyword suggestions, on-page audits, AI-citation checks, Google rank checks. Paid in USDC on Solana via x402. No API key, nothing stored about you.
version: 0.1.0
homepage: https://citable.run
---

# Citable — SEO and AI-visibility checks for agents

Each endpoint answers `402 Payment Required` with its price. Pay the quoted USDC amount with any
x402 client and the same request returns JSON. Nothing to sign up for, nothing to store.

- Protocol: x402 v2, scheme `exact`, network Solana **mainnet** (real USDC)
- Currency: USDC. The agent needs no SOL — the facilitator covers network fees.
- Errors are free: any 4xx/5xx response cancels the payment; only a delivered result is settled.
- Service index (free): `GET https://citable.run/api`

## Which endpoint for what you have

Pick by the input, not by the question. Prices are per call.

| You have | Call | Price |
|---|---|---|
| A topic or phrase people type | `/v1/keyword-suggest?seed=` — phrasings and questions, no volumes. Not a domain: it autocompletes the string literally | $0.005 |
| A topic, and you need volumes | `/v1/keyword-research?seed=` — keywords + questions with volume, CPC, difficulty, intent (cheaper than ideas + metrics) | $0.06 |
| A topic, volumes only, on-topic | `/v1/keyword-ideas?seed=` | $0.05 |
| A list of 1–20 keywords | `/v1/keyword-metrics?keywords=a,b,c` — one price per batch of 20 | $0.03 |
| A keyword + a domain | `/v1/rank-check?keyword=&domain=` | $0.012 |
| A keyword, to see the results page | `/v1/serp?keyword=` | $0.008 |
| A domain | `/v1/domain-overview?domain=` first, then `/v1/domain-keywords?domain=` for what it ranks for (each row carries month-over-month movement), `/v1/backlinks?domain=` for links | $0.03 · $0.04 · $0.10 |
| A domain, and you want its rankings over time | `/v1/domain-history?domain=&months=12` — monthly keywords, top-10 count, traffic, new/up/down/lost, with a trend. Nothing stored, no account | $0.03 |
| One page URL | `/v1/onpage-audit?url=` — one page, not a domain | $0.01 |
| A buyer question the user wrote + a domain | `/v1/ai-visibility?prompt=&domain=` — never invent the prompt; `/v1/cited-prompts` returns observed ones | $0.05/engine |
| A domain, in AI answers | `/v1/cited-prompts` · `/v1/top-cited-pages` · `/v1/ai-mentions-trend` | $0.25 · $0.22 · $0.18 |
| 2–10 domains to compare in AI answers | `/v1/ai-share-of-voice?domains=a,b,c` — one price for the set | $0.18 |
| A domain + a user-written question, full picture | `/v1/citability-report?domain=&prompt=` — audit + visibility + top pages, $0.43 bought separately | $0.30 |

Defaults are `country=us`, `lang=en` (`gl`/`hl` on rank-check and serp); pass both for any other market. Cheapest signal first; batch where a call takes a list; say the price before paying.

## Endpoints

### GET /v1/onpage-audit?url=<absolute http(s) URL> — $0.01
Fetches the page plus its robots.txt/llms.txt (10 s timeout, 2 MiB cap, ≤5 redirects; private
hosts refused) and returns `{ http, page, aiAccess, answerability, score, summary, checks[], topFixes[] }`.
Classic on-page signals (title, meta description, canonical, headings, links, images, JSON-LD)
PLUS AI citability: whether robots.txt blocks GPTBot/ClaudeBot/PerplexityBot/Google-Extended/CCBot
for this path, llms.txt presence, JS-only content detection, question-style headings, and
date/author attribution. 20 weighted checks, 0–100 score. Every non-passing check carries a
`fix` — an imperative instruction an agent can apply — and `topFixes[]` orders them by impact:
audit → apply topFixes → re-audit until clean.
Errors: `400 invalid_url` · `400 blocked_host` · `400 unresolvable_host` · `415 unsupported_content_type`
· `422 too_many_redirects` · `502 fetch_failed`.

### GET /v1/keyword-suggest?seed=<keyword> — $0.005
Params: `lang` (default `en`; `id` supported with localized question words), `country` (default `us`),
`depth` 0|1|2 (default 2), `limit` ≤300, `sources` comma list of `google,youtube,bing`.
Returns `{ suggestions[], questions[], counts, sources }`; each item has `keyword`, `score` (0–1,
relative to the top of its list), `sources[]`, `type`, `matchesSeed` (false = the keyword drifted
from the seed — treat as "no demand for this term", not as an idea). No search volume yet.
Errors: `400 invalid_seed` · `502 upstream_failed`.

### GET /v1/ai-visibility?prompt=<question>&domain=<hostname> — $0.05 per engine
Asks the AI answer engines a prompt through their **official APIs** (never UI scraping) and
reports which ones cite the domain. Params: `prompt` (3–400 chars), `domain` (e.g. `example.com`;
subdomains match), `engines` optional comma list of `perplexity,gemini,openai,anthropic` (default:
all the deployment has configured — the free `/api` index lists them under `engines_configured`).
Returns `{ mentioned, engines[], summary }`; each engine has `ok`, `mentioned`, `position`
(1-based rank in that engine's citation list), and `cited[]` (`{ url, domain }`, deduped, in
order). One run per engine — answers vary between runs, so call 2–3 times for signal.
Errors: `400 invalid_prompt|invalid_domain|invalid_engines` · `502 upstream_failed` (no engine
answered) · `503 not_configured`.

### GET /v1/rank-check?keyword=<query>&domain=<hostname> — $0.012
Google organic position of a domain for a keyword, via a licensed SERP feed (never scraped).
Params: `keyword` (1–120 chars), `domain`, `gl` country (default `us`), `hl` language (default
`en`), `num` results window 10–50 (default 20). Returns `{ position, url, title, checked,
top10[] }` — `position` is 1-based, `null` means not in the checked window; `top10[]` lists
`{ position, domain, url, title }` for the first ten results.
Errors: `400 invalid_keyword|invalid_domain` · `502 upstream_failed` · `503 not_configured`.

### GET /v1/keyword-research?seed=<keyword> — $0.06
**The one to call for keyword work.** Runs public autocomplete and the paid index together
and merges them: `{ keywords: [...], questions: [...] }`, each row with `volume`, `cpc`,
`competition`, `difficulty`, `intent`, plus `sources` (`index`, `autocomplete`, or both) and
`isQuestion`. Params: `seed`, `country` (default us), `lang` (default en), `limit` (1–200,
default 100). Cheaper than keyword-ideas + keyword-metrics separately. The three calls below
are its components — use them when you want to control exactly what you pay for.
Errors: `400` bad params · `502` · `503`.

### GET /v1/keyword-ideas?seed=<keyword> — $0.05
Keyword research in one call: returns keywords that **contain** the seed, each with search
volume, CPC, competition, difficulty and intent, ordered by volume. Full-text matched, so
results stay on topic. Params: `seed`, `limit` (1–100,
default 50), `country` (default us), `lang` (default en). Returns `{ keywords: [{ keyword,
volume, cpc, competition, difficulty, intent }], totalFound }`. Use this to open a topic;
use keyword-metrics when you already have the list. Errors: `400` bad params · `502` · `503`.

### GET /v1/keyword-metrics?keywords=<comma list> — $0.03
Search volume, CPC, competition, difficulty and search intent per keyword — licensed
clickstream-derived estimates (not a Google Keyword Planner passthrough). Params:
`keywords` (1–20, each ≤80 chars), `country` (2-letter, default `us`), `lang` (2-letter,
default `en`). One price covers the whole batch. Returns `{ keywords[], requested,
matched }` in request order; a keyword the source does not know comes back with null
metrics rather than being dropped. Pairs with `/v1/keyword-suggest`: expand a seed there,
then buy metrics for the shortlist here.
Errors: `400 invalid_keywords|invalid_locale` · `502 upstream_failed` · `503 not_configured`.

### GET /v1/top-cited-pages?domain=<hostname> — $0.22
Which pages of a domain AI engines cite most, from an aggregated index of AI answers
(not a live prompt run). Params: `domain`, `limit` (1–50, default 10). Returns
`{ pages[], totalCitedPages, domainTotals }` — per page: `mentions`, `aiSearchVolume`,
per-engine and per-language splits; plus domain-level totals. Off-domain URLs that merely
discuss the target are filtered out. Answers "what already works on this site — write
more of that"; pair with `/v1/onpage-audit` to fix the pages that should be cited but are not.
Errors: `400 invalid_domain` · `502 upstream_failed` · `503 not_configured`.

### GET /v1/cited-prompts?domain=<hostname> — $0.25
The questions AI engines already cite a domain for, from an aggregated index of AI answers.
Params: `domain`, `limit` (1–25, default 10), `platform` (`chat_gpt`|`google`, default both).
Returns `{ prompts[], totalPrompts, coverage }` — per prompt: `question`, `answer` snippet,
`aiSearchVolume`, the `citedUrl`/`citedRank` of this domain's own source, `firstSeen`/`lastSeen`,
`observations`, plus ChatGPT-only `brandEntities` and `fanOutQueries`. Repeated observations of
the same question are collapsed into one entry (`observations` says how many), so `totalPrompts`
— index rows — is larger than `prompts.length`. These are observed prompts, never
generated ones — call this first to find out what to ask, then pass a prompt you chose to
`/v1/ai-visibility`. Point `domain` at a competitor to get the prompts they win and you do not.
Errors: `400 invalid_domain|invalid_platform` · `502 upstream_failed` · `503 not_configured`.

### GET /v1/ai-mentions-trend?domain=<hostname> — $0.18
Stateless prompt tracking: monthly AI mentions and AI search volume with month-over-month
deltas. Params: `domain`, `from`/`to` (yyyy-mm-dd, index starts 2025-08-01), `platform`.
Returns `{ months[], summary, coverage }` — `summary.direction` is up/down/flat/unknown.
No tracker to create and nothing to poll; one call returns the whole series.
Errors: `400 invalid_domain|invalid_date|invalid_platform` · `502` · `503 not_configured`.

### GET /v1/ai-share-of-voice?domains=<a,b,c> — $0.18
Compare 2–10 domains in one call — the price is per call, not per domain. Params: `domains`
(comma list), `platform`. Returns `{ results[], leader, totals, coverage }` — per domain:
`rank`, `mentions`, `aiSearchVolume`, `sharePct` and per-engine/per-language splits.
`sharePct` is share of the set you asked about, not of the whole index. Pair with
`/v1/cited-prompts` to see which prompts drive the leader's lead.
Errors: `400 invalid_domains|invalid_platform` · `502 upstream_failed` · `503 not_configured`.

### GET /v1/domain-overview?domain=<hostname> — $0.03
One domain's organic footprint from a ranking index: keywords ranked, top-3/top-10 counts,
estimated monthly traffic and its value as ads, plus the paid-search side. Params: `domain`,
`country` (default us), `lang` (default en). Returns `{ organic: { keywords, top3, top10,
estTraffic, estTrafficValue }, paid }`. A domain the index has never seen returns zeros.
The first call of any competitive read. Errors: `400` bad params · `502` · `503`.

### GET /v1/domain-keywords?domain=<hostname> — $0.04
The keywords a domain actually ranks for — volume, CPC, position and the ranking URL,
highest volume first. Params: `domain`, `limit` (1–100, default 25), `country`, `lang`.
Returns `{ keywords: [{ keyword, volume, cpc, position, url, previousPosition, change, status }], totalKeywords }`
— `status` is new / up / down / same against the previous monthly index, `change` is places gained. Point it
at a competitor for their playbook. Errors: `400` bad params · `502` · `503`.

### GET /v1/domain-history?domain=<hostname> — $0.03
A domain's organic footprint month by month from the ranking index: `months[]` of
`{ month, organic: { keywords, top3, top10, estTraffic, estTrafficValue, new, up, down, lost }, paid }`
plus `trend` (first vs last month for keywords, top10, estTraffic). Params: `domain`, `months`
(2–24, default 12; the current month may be partial),
`country`, `lang`. The stateless way to track rankings — nothing is stored on our side. Monthly resolution. Errors: `400` bad params · `502` · `503`.

### GET /v1/serp?keyword=<query> — $0.008
The raw Google results page: organic results with snippets plus People-Also-Ask questions
and related searches when present. Params: `keyword`, `gl` (default us), `hl` (default en),
`num` (10–50, default 10). Returns `{ results, peopleAlsoAsk, relatedSearches }`.
Errors: `400 invalid_keyword` · `502` · `503`.

### GET /v1/backlinks?domain=<hostname> — $0.10
Link profile in one call: total backlinks, referring domains, domain rank, broken links,
plus the top referring domains with first-seen dates. Params: `domain`, `limit` (referring
domains, 1–25, default 10). Returns `{ summary, referringDomains }`.
Errors: `400 invalid_domain` · `502` · `503`.

### GET /v1/citability-report?domain=<hostname> — $0.30
The bundle: `/v1/onpage-audit` + `/v1/ai-visibility` (all engines) + `/v1/top-cited-pages`
in one call — $0.30 vs $0.38 separately. Params: `domain`, `prompt` (REQUIRED —
the buyer question to ask the engines; prompts are never auto-written), `limit` (top pages,
default 10). Returns `{ prompt, onpage, visibility, topCitedPages }` — the exact payloads of
the three endpoints. One request answers: can AI read this site, who cites it today, which pages
already work. Errors: `400` bad params · `502 unreachable|upstream_failed` · `503 not_configured`.


## How to pay (TypeScript, official x402 SDK)

```ts
// bun add @x402/fetch @x402/svm @solana/kit
import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { toClientSvmSigner } from "@x402/svm";
import { createKeyPairSignerFromBytes } from "@solana/kit";

const keypair = await createKeyPairSignerFromBytes(secretKey);   // a wallet holding USDC
const client  = new x402Client();
registerExactSvmScheme(client, { signer: toClientSvmSigner(keypair) });
const paidFetch = wrapFetchWithPayment(fetch, client);

const res  = await paidFetch("https://citable.run/v1/onpage-audit?url=example.com");
const data = await res.json();   // settlement tx is in the PAYMENT-RESPONSE header
```

Any other x402 client works the same way (e.g. the `pay` CLI: `pay curl "https://citable.run/v1/keyword-suggest?seed=seo+api"`).

## MCP (Claude, Cursor, any MCP client)

Tools `citable_prices` (free), `citable_keyword_suggest`, `citable_keyword_research`, `citable_keyword_ideas`, `citable_keyword_metrics`,
`citable_rank_check`, `citable_serp`, `citable_onpage_audit`, `citable_ai_visibility`, `citable_top_cited_pages`, `citable_cited_prompts`,
`citable_ai_mentions_trend`, `citable_ai_share_of_voice`, `citable_citability_report`, `citable_domain_overview`, `citable_domain_keywords`, `citable_domain_history`, `citable_backlinks`.
Plus `citable_connect` (free): sets up a sign-in wallet for people who do not use crypto — the agent gives the user a link, they sign in at citable.run with Google or email, add funds by card, click Connect. Otherwise the server pays from a keypair it creates at `~/.config/citable/agent.json`; `citable_prices` shows the address, `walletMode`, `balanceUsdc` and, when empty, `setup` with both options.
The Claude Code plugin adds a free `get-started` skill with the same input → tool map and spend rules.
Env: `CITABLE_WALLET` (keypair JSON path; default `~/.config/citable/agent.json`, created when first needed), `CITABLE_API` (default https://citable.run),
`CITABLE_MAX_PRICE` (refuse calls priced above this USD amount; default 0.30).

```bash
claude mcp add citable -- npx -y citable-mcp        # Codex: codex mcp add citable -- npx -y citable-mcp
```

Human-readable docs with real responses per endpoint: https://citable.run/docs

## Read the price without paying

```bash
curl -i "https://citable.run/v1/keyword-suggest?seed=seo+api"
# HTTP/1.1 402 Payment Required
# PAYMENT-REQUIRED: base64 JSON — accepts[0].amount (USDC base units), .payTo, .network, .maxTimeoutSeconds
```

The price in the 402 header is the price you pay. Prices may change; always read the header.
