ChatGPT AI Visibility API — Getting Started — FetchLayer Docs

Getting Started

Find out whether ChatGPT names your brand for the questions your buyers ask, whether it cites your pages when it does, and how that holds up across repeated answers.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. There is no OpenAI account or OpenAI API key involved.

Create free account →

2. Check one answer

Ask the question a buyer would ask, and name the brands to look for. Each one comes back with whether the answer mentioned it, how often, the sentence around each mention, and — separately — whether the answer cited one of its pages.

bash
curl -X POST https://api.fetchlayer.dev/chatgpt/mentions \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"best reddit api alternatives","targets":[{"name":"FetchLayer","domains":["fetchlayer.dev"]},"Apify"]}'

Mentioned is not cited

A brand can be recommended in the prose while none of its pages are among the sources, and a page can be cited without the brand being recommended. mentioned and citedAsSource are reported separately for exactly that reason. Watch retrieved too: it means ChatGPT read one of your pages and then cited someone else.

The response also carries otherBrandsMentioned — the brands the answer itself put forward, with the rank it gave them. That is your competitor set for that prompt, as ChatGPT sees it, and you did not have to guess it in advance.

3. Track it over several runs

One answer is a snapshot: ask again in ten minutes and the wording, the order and sometimes the whole list will differ. /ai-rank asks the same prompt several times and reports the aggregate, which is the number worth putting on a dashboard.

bash
curl -X POST https://api.fetchlayer.dev/chatgpt/ai-rank \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"best reddit api alternatives","target":{"name":"FetchLayer","domains":["fetchlayer.dev"]},"competitors":["Apify"],"runs":5}'

Read the aggregate, not a run

Each run reports rank, citationRank and the method the answer used to lay its options out. aggregate turns them into mentionRate, averageRank, bestRank, worstRank, citationRate and averageCitationRank, with the same figures per competitor. Keep the prompt, country and webSearch fixed when comparing week to week.

The runs are generated alongside each other, so more runs cost more credits but little extra time. Billing is one credit per completed answer, and a run that could not be completed is noted and not billed.

4. Find the prompts worth tracking

Rather than guessing which prompts matter, start from a topic or your brand name. /prompt-discovery returns the prompts it asked, the web searches ChatGPT actually ran while answering them, and prompts it suggests people use.

bash
curl -X POST https://api.fetchlayer.dev/chatgpt/prompt-discovery \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"seed":"reddit api alternatives","maxPrompts":3}'

The field names are exact

The seed field is seed and the count is maxPrompts. brand, domain, topic or promptCount return a 400 naming the field. Unrecognised fields are never quietly ignored on any route.

Both lists hold objects, not strings: read text for the wording. observedSearchQueries carry source: "observed_search_query" — searches ChatGPT ran, so the queries your pages have to rank for to be readable by it. suggestedPrompts carry source: "generated_prompt" — suggestions, not observations. Feed the ones that matter back into /ai-rank.

5. Compare a whole market

/visibility-report runs several prompts against several brands in one request and returns share of voice per brand plus a per-prompt breakdown. Extra brands are free — they are read out of the same answers — so the size is bounded by answers: prompts × runs must be at most 10.

bash
curl -X POST https://api.fetchlayer.dev/chatgpt/visibility-report \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"prompts":["best reddit api alternatives","how to scrape reddit"],"targets":["FetchLayer","Apify"],"runs":2}'

Tip

For tracking wider than ten answers, run several smaller reports on a schedule and store the results, rather than trying to fit everything into one call.

Errors and retries

A 400 names the field to fix; unrecognised fields are rejected rather than ignored. A 503 means no finished answer could be obtained within the time allowed, and is always worth retrying — a partial or cut-off answer is never returned as a success. An answer that says "I don't know" is a normal 200, and so is a prompt where none of your brands appear. Read notes on every successful response too: a country that could not be served, runs that could not be completed and details the answer did not show all announce themselves there.

Error responses

Every error, on every route, is JSON in one shape: error (what went wrong, in words), status, code, retryable and requestId, which also comes as the X-Request-Id header. Branch on code and retryable rather than on the text; on a 5xx the text is generic. Quote requestId when reporting a problem.

json
{
  "error": "Missing required parameter: url",
  "status": 400,
  "missing": ["url"],
  "code": "invalid_input",
  "retryable": false,
  "requestId": "922874e3-c0d0-40d8-8866-d198af132420"
}
codeStatusRetryableMeaning
invalid_input400noThe request needs fixing; error names the field. A missing required parameter also lists it in missing.
unauthorized401noThe API key is missing or not valid.
insufficient_credits402noNothing left to spend: no credits, a used-up free trial, or a monthly allowance and credit balance both spent. error says which. Waiting does not help; topping up does.
forbidden403noThis key or account is not allowed this request.
not_found404noWhat the request names does not exist or is not public.
expired410noWhat the request refers to is no longer available; ask for it afresh.
quota_exceeded429noA request-rate limit was reached; error says which. Slow down before sending more.
cancelled499noThe caller closed or cancelled the request before it finished. The work was stopped and nothing was charged.
time_limit503yesYour timeoutMs ran out before there was anything to return; ask again, or allow more time.
temporarily_unavailable503yesNot available right now; ask again shortly.
posts_unavailable403noTikTok user videos: TikTok is not showing this account’s posts publicly right now. Asking again at once returns the same answer; nothing is charged.
store_unreachable503yesStore routes (Shopify, WooCommerce): the store itself can’t be reached right now — it may be offline. Ask again later. Where a store route doesn’t say so yet, an offline store is temporarily_unavailable.
upstream_error502yesThe source answered in a way that could not be used; ask again later.
internal_error500yesSomething failed on our side; ask again later, and quote requestId if it persists.
tool_error—noMCP only: a tool call failed in a way no other code describes; error says what.

More codes may be added; treat one you do not recognise by its retryable flag. Over MCP, a tool error carries the same fields in structuredContent, and as a JSON text block beside the message.

Next steps

Product overviewChatGPT AI Visibility API: what it returns, pricing and use cases