Aura AI Visibility API documentation
Version 1.0.0 · Updated 2026-09-20
The Aura API gives you two things: a free, keyless readiness scan of any domain, and — with an API key — the measurement data behind a Monitor project, including citation sampling, share of voice, competitor comparison, citation sources and AI-crawler traffic. Every sampled rate is returned with the sample size and 95% confidence interval that produced it, and is returned asnull rather than a number when the sample is too small to support one.
The machine-readable description of everything on this page is at /api/openapi.json (OpenAPI 3.1). This page is generated from that same document, so the two cannot disagree.
On this page
How do I authenticate with the Aura API?
Send your key as a bearer token. An API key is a string beginning aai_key_ that you create in Settings → API keys; it is shown once, stored only as a SHA-256 hash, scoped to specific projects and to read or read+write access, and revocable at any time.
curl 'https://www.aaivisibility.com/api/monitor/citation?domain=example.com' \ -H 'Authorization: Bearer $AURA_API_KEY'
Access is checked per project, not per account, because a plan belongs to a subscription rather than to the account that owns it. The same key can answer 200 for one project and 403 with code: "plan_access_required" for another on the same account, if that project's plan does not include API access. Revocation is immediate: the request after you revoke a key returns 401.
Two other credentials appear in this document and are not API keys. The project ingest token (Settings → Connectors) authenticates POST /api/track only; it can write log events and read nothing, so it is safe to put in a log pipeline. The operator token is ours, used by our cron jobs for the write operations that spend AI-provider budget. GET /api/monitor/prompts is operator-only and is therefore not part of this API.
What are the rate limits?
A rate limit is the number of requests you may make in a rolling window before the API starts refusing with 429. Aura enforces one limit per API key and a separate, smaller one per IP on the free keyless endpoints.
Rate limits. Key-authenticated requests are limited to 120 per minute per API key, in one bucket shared with the MCP endpoint — a key has one budget however it calls us. The keyless public endpoints (POST /api/scan, POST /api/generate/llms-txt) are limited to 8 requests per 5 minutes per IP, and /api/scan additionally performs at most one live crawl per domain per IP per 24 hours, serving the stored report instead of refusing. Limited responses are 429 with Retry-After in seconds. Key-authenticated responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). Those counters are advisory: when the shared Redis limiter is unreachable the limiter falls back to per-instance memory for that request, so the numbers then describe one server's view of the window rather than the cluster's.
What do the numbers mean?
Every figure this API returns about AI answers is an estimate from a sample, not a fact, and the response shape says so. This section defines the terms used throughout the endpoint reference.
What the numbers mean. A mention and a citation are different measurements and are never merged: a mention is the brand named in the prose of an answer (detector mention-v2), a citation is a linkable reference to the domain. Both are estimates from a sample of non-deterministic AI answers, so both are reported as Wilson score intervals with the sample size that produced them. rate is null — never 0, never a guess — whenever n < 30 or the 95% interval half-width exceeds 15 percentage points; the reason field says which bar was missed, and n and successes are always present so you can compute your own figure. Sentiment is a lexical classifier, labelled lexical-v1 in every payload that carries it; it is not a model judgement. Agent traffic is computed from access-log events you ship to POST /api/track — AI crawlers do not execute JavaScript, so a browser beacon cannot see them and coverage equals what you send.
Mentions are not citations
A mention is your brand named in the prose of an answer, found by detector mention-v2. A citation is a linkable reference to your domain in that answer. Being talked about and being linked to are different outcomes with different fixes, so they are returned as two separate objects on every brand row and are never added together into a single visibility number.
Why rate is sometimes null
rate is null — not 0 — whenever the sample cannot support a number: fewer than 30 trials, or a 95% Wilson interval half-width wider than ±15 percentage points. When that happens, reason states which bar was missed and n and successes are still returned, so you can compute and caveat your own figure. A null rate means "not measured well enough to say", never "never happened".
Why every rate carries n and an interval
AI answers are non-deterministic and personalised, so the same prompt on the same day can produce different answers. A percentage with no sample size behind it carries almost no information: at n=7, a 43% rate has a 95% interval of roughly 18%–71%. Aura uses the Wilson score interval rather than the textbook normal interval, because the normal interval returns a zero-width interval at 0 or n successes — false certainty at exactly the sample sizes this product operates at.
Sentiment is lexical, and labelled so
Sentiment is produced by a lexical classifier and every payload carrying it also carries method: "lexical-v1". It scores the language around your brand in the sampled answer. It is not a language-model judgement, and this API does not present it as one.
Agent traffic comes from your server logs
AI crawlers do not execute JavaScript, so a browser pixel cannot observe them. Agent traffic is therefore computed only from the access-log events you ship to POST /api/track. Coverage equals what you send: the report describes your logs, and every response repeats that in its disclosure field.
What do the error responses look like?
Every non-2xx response is a JSON object with an error string. The failures this document names also carry a stable code you can branch on, so you never have to match on prose.
| Status | Code | When |
|---|---|---|
400 | — | A required parameter is missing or the JSON body could not be parsed. |
401 | — | No credential was sent, or the key is unknown or revoked. |
402 | — | Returned by the dashboard routes when the account's plan does not include the feature. |
403 | plan_access_required | The project's plan does not include API access. Per project, not per account. |
403 | — | The key is not scoped to this domain, belongs to another account, or the subscription is not active. |
404 | — | Nothing has been sampled or scanned for this domain yet. Not an error in your request. |
429 | rate_limited | Over the rate limit. Retry-After gives the seconds to wait. |
Scan endpoints
The free readiness engine: 18 deterministic checks on a live site. No key required.
POST/api/scan
Runs the 18-check readiness engine against a live URL and returns the full report. Free and keyless. Measures what the site controls — crawler access, machine discovery files, structured data, content citability, technical trust. It does not measure AI answers and never returns a citation rate.
At most one live crawl per domain per IP per 24 hours. When that budget is spent the stored report is returned with cached: true rather than a 429, so a repeat caller still gets their result and the target site is crawled once a day per requester.
curl -X POST 'https://www.aaivisibility.com/api/scan' \
-H 'Content-Type: application/json' \
-d '{"url":"example.com"}'Request body
| Field | Type | Description |
|---|---|---|
url | string | Domain or full URL. Validated and SSRF-guarded server-side. |
Response ScanReport
| Field | Type | Description |
|---|---|---|
score | number | |
grade | string | |
categories | object[] | |
methodology (optional) | string | |
url | string | The URL as requested. |
final_url | string | The URL after redirects — what was actually scored. |
scanned_at | string | |
engine_version | string | |
page_title (optional) | string | |
crawlers (optional) | object[] | Per-crawler robots.txt verdict. |
fetch (optional) | object | |
cached (optional) | boolean | True when the per-domain daily crawl budget was already spent and this is the stored report. |
cached_at (optional) | string | Present only when cached is true. |
share_path (optional) | string | null | Path to the shareable result page, or null when the scan is not publishable (an unreachable site is never cached and never shared). |
Status codes
| Code | Meaning |
|---|---|
200 | The report. cached: true means the stored one. |
400 | Invalid JSON body, or the target could not be fetched (the message says which). |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
500 | The scan failed for a reason that is ours, not the target's. |
GET/api/results/{domain}
Returns the saved report for a domain from cache only — it never triggers a crawl, so it cannot be used to make this service fetch arbitrary hosts. Keyless. 404 when the domain has never been scanned.
curl 'https://www.aaivisibility.com/api/results/example.com'
Parameters
| Name | In | Type | Description |
|---|---|---|---|
domain | path | string | Bare domain. URL-encode it. |
Response CachedScanResult
| Field | Type | Description |
|---|---|---|
domain | string | |
cached_at | string | |
score | number | |
grade | string | |
scanned_at | string | |
engine_version | string | |
final_url | string | |
categories | object[] |
Status codes
| Code | Meaning |
|---|---|
200 | The stored report. |
400 | Not a usable domain. |
404 | No saved result for that domain. |
Tools endpoints
Free generators that read a live site. No key required.
POST/api/generate/llms-txt
Reads a homepage and sitemap and returns a spec-shaped llms.txt (https://llmstxt.org), the signals it was built from, every candidate page so you can curate, and how many sitemap URLs were found, included and omitted. Free and keyless. Send Accept: text/plain to get just the file.
curl -X POST 'https://www.aaivisibility.com/api/generate/llms-txt' \
-H 'Content-Type: application/json' \
-d '{"url":"example.com"}'Request body
| Field | Type | Description |
|---|---|---|
url | string |
Response LlmsTxtResult
| Field | Type | Description |
|---|---|---|
domain | string | |
content | string | The rendered llms.txt file. |
signals | object | What the generator read off the live homepage and sitemap to build the file. |
pages | object[] | Every candidate page found, so you can curate before publishing. |
found | integer | Sitemap URLs discovered. |
omitted | integer | Discovered URLs left out of the file. |
cap | integer | Maximum pages the generator will include. |
Status codes
| Code | Meaning |
|---|---|
200 | The generated file and its provenance. text/plain returns the file alone, with the page count in X-Aura-Pages. |
400 | Invalid JSON body, or the target could not be fetched. |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
500 | Generation failed. |
Monitor endpoints
Per-project measurement over time. Requires an API key on a plan that includes API access.
GET/api/monitor/history
Every stored scan of the project, newest first, with a compact score series for charting and the most recent drift alerts. Re-scans run every Monday at 06:00 UTC; snapshots carry a trigger field so a manual run is never counted as a scheduled one.
curl 'https://www.aaivisibility.com/api/monitor/history?domain=example.com' \ -H 'Authorization: Bearer $AURA_API_KEY'
Parameters
| Name | In | Type | Description |
|---|---|---|---|
domain | query | string | The project domain, bare host form. Normalised server-side, so https://www.example.com/path and example.com resolve to the same project. |
limit (optional) | query | integer | Snapshots to return, 1-104. Clamped, not rejected. |
Response HistoryResponse
| Field | Type | Description |
|---|---|---|
domain | string | |
subscribed | boolean | Whether the subscription is currently active. |
count | integer | Snapshots returned, after the limit was applied. |
series | object[] | Compact score-over-time series for charting. |
history | ScanSnapshot[] | Newest first. |
alerts | DriftReport[] | Up to 20 most recent drift reports. |
Status codes
| Code | Meaning |
|---|---|
200 | History, series and alerts. |
400 | Missing or unparseable ?domain. |
401 | No credential, an unknown key, or a revoked key. |
403 | The key is not scoped to this domain, the key does not belong to this project's account, the subscription is not active, or the project's plan does not include API access (code: plan_access_required). |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
GET/api/monitor/citation
Citation sampling asks a fixed query set of each engine and records whether the domain was cited in the answer. Each project is sampled every week (the benchmark runs at 07:00 UTC on the days a project is due). Every rate is a Wilson interval with its sample size, and is null below the reporting bar — see rate and reason on CitationEstimate.
This response still carries the deprecated point and ci fields and therefore sends the Deprecation, Sunset and Link headers.
curl 'https://www.aaivisibility.com/api/monitor/citation?domain=example.com' \ -H 'Authorization: Bearer $AURA_API_KEY'
Parameters
| Name | In | Type | Description |
|---|---|---|---|
domain | query | string | The project domain, bare host form. Normalised server-side, so https://www.example.com/path and example.com resolve to the same project. |
limit (optional) | query | integer | Runs of history to return, 1-52. Clamped. |
Response CitationResponse
| Field | Type | Description |
|---|---|---|
domain | string | |
latest | CitationSampleReport | Most recent run, or null when the domain has never been sampled. |
history | CitationSampleReport[] | Newest first, up to ?limit. |
Status codes
| Code | Meaning |
|---|---|
200 | Latest run and history. |
400 | Missing or unparseable ?domain. |
401 | No credential, an unknown key, or a revoked key. |
403 | The key is not scoped to this domain, the key does not belong to this project's account, the subscription is not active, or the project's plan does not include API access (code: plan_access_required). |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
GET/api/monitor/compare
Ranks the project's latest scan against the latest scan of each competitor and names the categories where it is behind. Site-readiness only — it says nothing about AI answers; use /api/monitor/sov for that. Competitors with no scan on file are returned in missing rather than dropped.
curl 'https://www.aaivisibility.com/api/monitor/compare?domain=example.com' \ -H 'Authorization: Bearer $AURA_API_KEY'
Parameters
| Name | In | Type | Description |
|---|---|---|---|
domain | query | string | The project domain, bare host form. Normalised server-side, so https://www.example.com/path and example.com resolve to the same project. |
vs (optional) | query | string | Comma-separated competitor domains, up to 25. Defaults to the project's tracked competitors. |
Response ComparisonReport
| Field | Type | Description |
|---|---|---|
subject | string | |
rows | object[] | |
subject_rank | integer | |
field_size | integer | |
gaps | object[] | Categories where at least one competitor beats the subject, worst first. |
headline | string | |
missing | string[] | Requested competitors with no scan on file — surfaced, never silently dropped. |
Status codes
| Code | Meaning |
|---|---|
200 | The comparison. |
400 | Missing or unparseable ?domain. |
401 | No credential, an unknown key, or a revoked key. |
403 | The key is not scoped to this domain, the key does not belong to this project's account, the subscription is not active, or the project's plan does not include API access (code: plan_access_required). |
404 | The subject domain has no scan history yet. |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
GET/api/monitor/sources
Citation-source intelligence computed from the stored share-of-voice trials, so it costs no extra sampling. Hosts are grouped into channels (review sites, communities, editorial, and so on) with a per-channel recommendation. Counted per answer: a host cited three times in one answer counts once.
curl 'https://www.aaivisibility.com/api/monitor/sources?domain=example.com' \ -H 'Authorization: Bearer $AURA_API_KEY'
Parameters
| Name | In | Type | Description |
|---|---|---|---|
domain | query | string | The project domain, bare host form. Normalised server-side, so https://www.example.com/path and example.com resolve to the same project. |
Response SourceReport
| Field | Type | Description |
|---|---|---|
domain | string | |
trials | integer | Answers the shares below are computed over. |
sources | object[] | |
channels | object[] | |
recommendations | object[] | |
disclosure | string |
Status codes
| Code | Meaning |
|---|---|
200 | The source report. |
400 | Missing or unparseable ?domain. |
401 | No credential, an unknown key, or a revoked key. |
403 | The key is not scoped to this domain, the key does not belong to this project's account, the subscription is not active, or the project's plan does not include API access (code: plan_access_required). |
404 | No share-of-voice sample to aggregate yet. |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
GET/api/monitor/agents
Which AI crawlers fetched the site, what they read and how it is trending — aggregated from the access-log events you ship to POST /api/track.
Server-log based, deliberately. AI crawlers do not execute JavaScript, so a browser beacon cannot see them; anyone selling a pixel for this is selling a hole. The consequence is that coverage equals what you send us, and the response says so in its disclosure field.
curl 'https://www.aaivisibility.com/api/monitor/agents?domain=example.com' \ -H 'Authorization: Bearer $AURA_API_KEY'
Parameters
| Name | In | Type | Description |
|---|---|---|---|
domain | query | string | The project domain, bare host form. Normalised server-side, so https://www.example.com/path and example.com resolve to the same project. |
days (optional) | query | integer | Trailing window in days, 1-365. Clamped. |
Response AgentTrafficReport
| Field | Type | Description |
|---|---|---|
domain | string | |
window_days | integer | Trailing window the counts cover, 1-365. |
total_hits | integer | |
by_bot | object[] | |
by_purpose | object[] | |
top_paths | object[] | Up to 25 most-fetched paths. |
by_day | object[] | |
disclosure | string |
Status codes
| Code | Meaning |
|---|---|
200 | The traffic report. |
400 | Missing or unparseable ?domain. |
401 | No credential, an unknown key, or a revoked key. |
403 | The key is not scoped to this domain, the key does not belong to this project's account, the subscription is not active, or the project's plan does not include API access (code: plan_access_required). |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
POST/api/track
Ingests a batch of server access-log events, the input side of /api/monitor/agents. Events whose user-agent is not a recognised AI or search crawler are dropped at ingest and never stored.
Authenticated with the project's ingest token from Settings -> Connectors, not with an API key: the token is scoped to one project and does no reads, so it can live in a log pipeline that should not hold a key that can read your reports.
curl -X POST 'https://www.aaivisibility.com/api/track' \
-H 'Authorization: Bearer $AURA_INGEST_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"domain":"example.com","events":[{"ua":"Mozilla/5.0 (compatible; GPTBot/1.0)","path":"/pricing","ts":"2026-09-20T09:00:00Z","status":200}]}'Request body
| Field | Type | Description |
|---|---|---|
domain | string | |
events | object[] | Up to 1000 events per request. |
Response AgentIngestResult
| Field | Type | Description |
|---|---|---|
ok | "true" | |
received | integer | Events in the batch, after the 1000-event cap. |
ai_events_stored | integer | Events whose user-agent matched a known AI or search crawler. |
ignored_non_bot | integer | Events dropped — browsers and unrecognised agents are not this product's business. |
Status codes
| Code | Meaning |
|---|---|
200 | Counts of what was received, stored and dropped. |
400 | Invalid JSON body, missing domain, or an empty events array. |
401 | Missing or wrong ingest token. |
413 | More than 1000 events in one request. |
MCP endpoints
The Model Context Protocol transport for the same data.
GET/api/mcp
Returns the MCP server info, transport, endpoint and tool catalogue. Keyless — it is a discovery document, not data.
curl 'https://www.aaivisibility.com/api/mcp'
Response
| Field | Type | Description |
|---|---|---|
server | object | |
transport | "streamable-http" | |
endpoint | string | |
auth | string | |
tools | object[] | |
stdio_package (optional) | object | The stdio MCP package, for clients that do not speak Streamable HTTP (Claude Desktop, Claude Code, Cursor). It exposes a different tool set to this endpoint — see AURA-287. |
Status codes
| Code | Meaning |
|---|---|
200 | Server info and the tool list. |
POST/api/mcp
The MCP transport for the same Monitor data, for AI clients that speak Model Context Protocol instead of REST. It takes a single JSON-RPC request or a batch, authenticates with the same aai_key_ bearer tokens, and spends the same 120/minute per-key budget as the REST routes above — one key, one budget, whichever transport it uses. The tool catalogue and its per-tool schemas are documented separately (AURA-287); GET /api/mcp lists the tools this server exposes.
curl -X POST 'https://www.aaivisibility.com/api/mcp' \ -H 'Authorization: Bearer $AURA_API_KEY'
Response
Status codes
| Code | Meaning |
|---|---|
200 | The JSON-RPC response, or an array of them for a batch. |
202 | The batch contained only notifications; nothing to return. |
400 | Unparseable JSON. |
401 | Missing, unknown or revoked API key. |
429 | Rate limited. Retry after the number of seconds in Retry-After. |
How does Aura version this API?
Versioning is the promise about what can change under you. Aura's promise is that these paths describe v1 and will not break in place.
Versioning. Paths are unversioned and describe v1. A breaking change — a field removed, a type changed, a status code repurposed — ships under a new path prefix (/api/v2/...) rather than mutating these routes. Additive changes (new fields, new optional parameters, new endpoints) happen in place and are not breaking; write clients that ignore fields they do not recognise. Deprecation. A deprecated field keeps serving for at least 90 days from the date it is marked. Every response still carrying one sends a Deprecation header (RFC 8594) with the date it was deprecated, a Sunset header with the earliest date it may be removed, and a Link header pointing at this page. Deprecated fields are marked in this document with the date. Currently deprecated: the estimate fields point and ci, deprecated 2026-09-20, serving until at least 2026-12-19 — use rate, ci_low and ci_high.
Which fields are deprecated?
A deprecated field still works. It is a field we intend to stop returning, announced in advance so you can move off it on your own schedule.
| Field | Deprecated | Serving until at least | Use instead |
|---|---|---|---|
point on every estimate | 2026-09-20 | 2026-12-19 | rate, which is null below the reporting bar instead of 0 |
ci on every estimate | 2026-09-20 | 2026-12-19 | ci_low and ci_high |
Responses that still carry these fields send a Deprecation header with the date above, a Sunset header with the earliest removal date, and a Link header pointing back to this section. The minimum notice is 90 days.
Questions
What is the difference between a mention and a citation?
A mention is your brand named in the prose of an AI answer. A citation is a linkable reference to your domain in that answer. They measure different things and Aura never merges them into one number: every share-of-voice brand row carries a separate mention estimate and citation estimate, each with its own sample size and confidence interval.
Why is rate sometimes null instead of a percentage?
Because the sample is too small to support a number. Aura refuses to state a rate when fewer than 30 trials were sampled, or when the 95% Wilson interval is wider than plus or minus 15 percentage points. In that case rate is null, the reason field says which bar was missed, and n and successes are still returned so you can compute and caveat your own figure. It is never reported as 0.
Why does every rate come with n and a confidence interval?
AI answers are non-deterministic and personalised, so any rate is an estimate from a sample rather than a fact. A rate without its sample size carries almost no information: with seven queries, 43% has a 95% interval of roughly 18% to 71%. Aura uses the Wilson score interval rather than the textbook normal interval, because the normal interval collapses to zero width at 0 or n successes and reports false certainty at exactly the sample sizes this product operates at.
How do I authenticate with the Aura API?
Send an API key as an Authorization: Bearer header. Keys start with aai_key_, are created in Settings then API keys, are shown once and stored only as a SHA-256 hash, and are scoped to specific projects with read or read and write access. Revoking a key takes effect immediately: the next request returns 401.
Which plans include API access?
Both Monitor and Agency include API and MCP access. The check is per project rather than per account, because the plan lives on the subscription: if one project on your account is on a plan without API access, the same key can return 200 for another project and 403 with code plan_access_required for that one.
What are the Aura API rate limits?
Key-authenticated requests are limited to 120 per minute per API key, in one budget shared with the MCP endpoint, so splitting traffic between REST and MCP does not double your allowance. The free keyless endpoints allow 8 requests per 5 minutes per IP. Over the limit you get HTTP 429 with a Retry-After header in seconds.
How is sentiment calculated?
With a lexical classifier, labelled lexical-v1 in every payload that carries it. It scores the words around your brand in the sampled answer; it is not a language-model judgement, and Aura labels it so rather than presenting it as one.
How does Aura measure AI crawler traffic?
From server access-log events you ship to POST /api/track. AI crawlers do not execute JavaScript, so a browser pixel or beacon cannot see them at all. The consequence is honest and stated in every response: coverage equals what you send, so the report describes your logs, not the whole internet.
How does Aura version its API and deprecate fields?
Paths are unversioned and describe v1. Additive changes such as new fields and new optional parameters ship in place, so clients should ignore fields they do not recognise. A breaking change ships under a new path prefix instead of mutating these routes. A deprecated field keeps serving for at least 90 days, and every response still carrying one sends Deprecation, Sunset and Link headers.
Can I export Aura data as CSV?
Not through this API yet. No CSV endpoint exists in the product today, so none is described in the OpenAPI document; it is tracked as AURA-285 and will be documented when it ships.
Endpoint summary
This API currently exposes 12 operations. The full machine-readable description is at /api/openapi.json.
| Endpoint | Auth | Summary |
|---|---|---|
POST /api/scan | None — public, no key | Scan a domain for AI-search readiness |
GET /api/results/{domain} | None — public, no key | Read the stored scan for a domain |
POST /api/generate/llms-txt | None — public, no key | Generate an llms.txt from a live site |
GET /api/monitor/history | API key | Scan history and drift alerts for a project |
GET /api/monitor/citation | API key | Citation sampling runs for a project |
GET /api/monitor/sov | API key | Latest share-of-voice report |
GET /api/monitor/compare | API key | Rank the project against competitors on readiness score |
GET /api/monitor/sources | API key | Which hosts AI answers cite in your category |
GET /api/monitor/agents | API key | AI-crawler traffic from your access logs |
POST /api/track | Project ingest token | Ship access-log events for AI-crawler analytics |
GET /api/mcp | None — public, no key | MCP server discovery document |
POST /api/mcp | API key | Model Context Protocol transport (JSON-RPC 2.0) |