API reference · v1.0.0
Ranksify Public API
Read access to AI-visibility analytics: rollup-backed timeseries and rankings with Wilson 95% confidence intervals, cited sources with verification status, and statistically honest day-over-day change events. Authenticate with `Authorization: Bearer rk_…`.
8 endpoints. This page is generated from the machine-readable spec — https://app.ranksify.ai/api/v1/openapi.json.
Authentication
Authorization: Bearer rk_…
Workspace API key (`rk_…`), created in Settings → API keys. Send it as `Authorization: Bearer rk_…` from a server: anything in a browser bundle is public. Keys carry scopes (`read`, `ingest`, `mcp`, `mcp:write`) and a per-key rate limit. `read` covers every endpoint on this page; `mcp` is what the MCP server checks; `ingest` is write-only and is used by the beacon/log ingest endpoints, not here. A key without the scope an endpoint needs gets `403` — never partial data. Rate limiting is a fixed 60-second window at the key's own limit (60 requests/minute by default). Over it, the response is `429` with `Retry-After` in seconds; that header is the only rate-limit signal, and there are no `X-RateLimit-*` headers on success. A key stops working the moment it is revoked, or if the workspace's plan loses API access.
curl -H "Authorization: Bearer $RANKSIFY_API_KEY" "https://app.ranksify.ai/api/v1/projects/<id>/citations"
Keys are created per workspace in Settings → API keys and are scoped: a key without the scope an endpoint needs gets 403, not partial data. Requests are rate limited per key; a 429 carries Retry-After in seconds. Send the key from a server — anything in a browser bundle is public.
Endpoints
Every endpoint in the v1 API
GET/v1/projects
List projects
Every project in the key's workspace, oldest first — the id lookup for every other path on this page, so nobody has to copy a uuid out of the app's URL bar. A project-scoped key sees only its own project. Requires a key with the `read` scope.
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the `{ data: … }` success envelope. |
|
| 401 | Missing or unknown API key. |
|
| 403 | The key lacks the required scope, or the workspace's plan does not include API access. |
|
| 429 | Per-key rate limit exceeded (fixed 60-second window at the key's own limit). |
|
| 500 | Unexpected server error. Not caused by the request; retry with backoff. |
|
Example
cURL
curl \ -H "Authorization: Bearer $RANKSIFY_API_KEY" \ "https://app.ranksify.ai/api/v1/projects"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/projects",
{
headers: { Authorization: `Bearer ${process.env.RANKSIFY_API_KEY}` },
},
);
if (!res.ok) {
// On 429, res.headers.get("retry-after") holds the seconds to wait
// before trying again. It is the only rate-limit signal sent.
// Every failure is the same envelope — branch on error.code, never on the message.
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();Response
{
"data": {
"projects": [
{
"id": "8c2f1e4a-53b7-4d90-a1f6-2e7c9b04d5a8",
"name": "Acme Analytics",
"domain": "acme.example"
}
]
}
}GET/v1/projects/{id}/visibility/timeseries
Daily visibility timeseries
One point per day: visibility %, Wilson 95% CI bounds, sample size, mentions, citations, share of voice, average position, sentiment. Requires a key with the `read` scope.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string · uuid | Project (brand) id. |
| from | query | string · date | Range start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window). |
| to | query | string · date | Range end (YYYY-MM-DD, inclusive). Defaults to today. |
| engineId | query | string · uuid | Filter to one answer engine (uuid). Unset = all engines. |
| topicId | query | string · uuid | Filter to one topic (uuid). Unset = all topics. |
| location | query | string | Filter to one sampling location. Unset = all locations. |
| branded | query | "true" | "false" | Restrict to branded (true) or unbranded (false) prompts. |
| subjectType | query | "brand" | "competitor" | Whose series to return. Defaults to the project's brand. |
| subjectId | query | string · uuid | Competitor id — required when subjectType=competitor. |
* required
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the `{ data: … }` success envelope. |
|
| 400 | Invalid filters (bad date format, `from` after `to`). |
|
| 401 | Missing or unknown API key. |
|
| 403 | The key lacks the required scope, or the workspace's plan does not include API access. |
|
| 404 | No such project in the key's workspace — also returned when a project-scoped key asks for a different project, so a key cannot probe which ids exist. |
|
| 429 | Per-key rate limit exceeded (fixed 60-second window at the key's own limit). |
|
| 500 | Unexpected server error. Not caused by the request; retry with backoff. |
|
Example
cURL
curl \ -H "Authorization: Bearer $RANKSIFY_API_KEY" \ "https://app.ranksify.ai/api/v1/projects/<id>/visibility/timeseries?from=2026-01-01&to=2026-01-28"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/projects/<id>/visibility/timeseries?from=2026-01-01&to=2026-01-28",
{
headers: { Authorization: `Bearer ${process.env.RANKSIFY_API_KEY}` },
},
);
if (!res.ok) {
// On 429, res.headers.get("retry-after") holds the seconds to wait
// before trying again. It is the only rate-limit signal sent.
// Every failure is the same envelope — branch on error.code, never on the message.
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();Response
{
"data": {
"subject": {
"subjectType": "brand",
"subjectId": "8c2f1e4a-53b7-4d90-a1f6-2e7c9b04d5a8"
},
"filters": {
"from": "2026-01-01",
"to": "2026-01-28",
"engineId": null,
"topicId": null,
"location": null,
"branded": null
},
"series": [
{
"day": "2026-01-26",
"sampleSize": 3,
"mentionRuns": 1,
"visibility": null,
"ciLow": 6.149,
"ciHigh": 79.2345,
"rateWithheld": "Too few runs for a rate (n=3, needs 5)",
"shareOfVoice": 33.3333,
"avgPosition": 3,
"sentiment": 58,
"mentions": 1,
"citations": 1
},
{
"day": "2026-01-27",
"sampleSize": 24,
"mentionRuns": 7,
"visibility": 29.1667,
"ciLow": 14.9145,
"ciHigh": 49.1681,
"shareOfVoice": 58.3333,
"avgPosition": 2.6,
"sentiment": 61.4,
"mentions": 7,
"citations": 4
},
{
"day": "2026-01-28",
"sampleSize": 24,
"mentionRuns": 9,
"visibility": 37.5,
"ciLow": 21.1591,
"ciHigh": 57.2904,
"shareOfVoice": 60,
"avgPosition": 2.1,
"sentiment": 64.2,
"mentions": 9,
"citations": 6
}
]
}
}GET/v1/projects/{id}/visibility/ranking
Visibility leaderboard
Brand vs tracked competitors over the range: visibility with CI, share of voice, average position, sentiment, mentions, citations. Requires a key with the `read` scope.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string · uuid | Project (brand) id. |
| from | query | string · date | Range start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window). |
| to | query | string · date | Range end (YYYY-MM-DD, inclusive). Defaults to today. |
| engineId | query | string · uuid | Filter to one answer engine (uuid). Unset = all engines. |
| topicId | query | string · uuid | Filter to one topic (uuid). Unset = all topics. |
| location | query | string | Filter to one sampling location. Unset = all locations. |
| branded | query | "true" | "false" | Restrict to branded (true) or unbranded (false) prompts. |
* required
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the `{ data: … }` success envelope. |
|
| 400 | Invalid filters (bad date format, `from` after `to`). |
|
| 401 | Missing or unknown API key. |
|
| 403 | The key lacks the required scope, or the workspace's plan does not include API access. |
|
| 404 | No such project in the key's workspace — also returned when a project-scoped key asks for a different project, so a key cannot probe which ids exist. |
|
| 429 | Per-key rate limit exceeded (fixed 60-second window at the key's own limit). |
|
| 500 | Unexpected server error. Not caused by the request; retry with backoff. |
|
Example
cURL
curl \ -H "Authorization: Bearer $RANKSIFY_API_KEY" \ "https://app.ranksify.ai/api/v1/projects/<id>/visibility/ranking?from=2026-01-01&to=2026-01-28"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/projects/<id>/visibility/ranking?from=2026-01-01&to=2026-01-28",
{
headers: { Authorization: `Bearer ${process.env.RANKSIFY_API_KEY}` },
},
);
if (!res.ok) {
// On 429, res.headers.get("retry-after") holds the seconds to wait
// before trying again. It is the only rate-limit signal sent.
// Every failure is the same envelope — branch on error.code, never on the message.
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();Response
{
"data": {
"filters": {
"from": "2026-01-01",
"to": "2026-01-28",
"engineId": null,
"topicId": null,
"location": null,
"branded": null
},
"rows": [
{
"subjectType": "brand",
"subjectId": "8c2f1e4a-53b7-4d90-a1f6-2e7c9b04d5a8",
"name": "Acme Analytics",
"color": null,
"domain": "acme.example",
"sampleSize": 168,
"mentionRuns": 57,
"visibility": 33.9286,
"ciLow": 27.1996,
"ciHigh": 41.3761,
"shareOfVoice": 58.1633,
"avgPosition": 2.1,
"sentiment": 64.2,
"mentions": 57,
"citations": 34
},
{
"subjectType": "competitor",
"subjectId": "3f9a2c51-7d64-4b18-9e02-5a1c8f6d0b73",
"name": "Northwind",
"color": "#7C5CFF",
"domain": "northwind.example",
"sampleSize": 168,
"mentionRuns": 41,
"visibility": 24.4048,
"ciLow": 18.5294,
"ciHigh": 31.4245,
"shareOfVoice": 41.8367,
"avgPosition": 3.4,
"sentiment": 55.8,
"mentions": 41,
"citations": 19
}
]
}
}GET/v1/projects/{id}/citations
Cited source domains
Domains cited by AI answers with counts per verification status (verified/broken/unchecked); hallucinated citations are listed but excluded from cited counts. Requires a key with the `read` scope.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string · uuid | Project (brand) id. |
| from | query | string · date | Range start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window). |
| to | query | string · date | Range end (YYYY-MM-DD, inclusive). Defaults to today. |
| engineId | query | string · uuid | Filter to one answer engine (uuid). Unset = all engines. |
| topicId | query | string · uuid | Filter to one topic (uuid). Unset = all topics. |
| location | query | string | Filter to one sampling location. Unset = all locations. |
| branded | query | "true" | "false" | Restrict to branded (true) or unbranded (false) prompts. |
* required
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the `{ data: … }` success envelope. |
|
| 400 | Invalid filters (bad date format, `from` after `to`). |
|
| 401 | Missing or unknown API key. |
|
| 403 | The key lacks the required scope, or the workspace's plan does not include API access. |
|
| 404 | No such project in the key's workspace — also returned when a project-scoped key asks for a different project, so a key cannot probe which ids exist. |
|
| 429 | Per-key rate limit exceeded (fixed 60-second window at the key's own limit). |
|
| 500 | Unexpected server error. Not caused by the request; retry with backoff. |
|
Example
cURL
curl \ -H "Authorization: Bearer $RANKSIFY_API_KEY" \ "https://app.ranksify.ai/api/v1/projects/<id>/citations?from=2026-01-01&to=2026-01-28"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/projects/<id>/citations?from=2026-01-01&to=2026-01-28",
{
headers: { Authorization: `Bearer ${process.env.RANKSIFY_API_KEY}` },
},
);
if (!res.ok) {
// On 429, res.headers.get("retry-after") holds the seconds to wait
// before trying again. It is the only rate-limit signal sent.
// Every failure is the same envelope — branch on error.code, never on the message.
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();Response
{
"data": {
"filters": {
"from": "2026-01-01",
"to": "2026-01-28",
"engineId": null,
"topicId": null,
"location": null,
"branded": null
},
"domains": [
{
"domain": "acme.example",
"citedCount": 21,
"totalCount": 23,
"statusCounts": {
"verified": 20,
"broken": 1,
"hallucinated": 2,
"unchecked": 0,
"stale": 0
},
"urls": 9,
"articleTypes": [
"docs",
"blog",
"homepage"
],
"lastSeen": "2026-01-28",
"sourceType": "owned"
},
{
"domain": "g2.com",
"citedCount": 13,
"totalCount": 13,
"statusCounts": {
"verified": 11,
"broken": 0,
"hallucinated": 0,
"unchecked": 2,
"stale": 0
},
"urls": 4,
"articleTypes": [
"review",
"comparison"
],
"lastSeen": "2026-01-26",
"sourceType": "review"
}
]
}
}GET/v1/projects/{id}/changes
Change events
Statistically significant day-over-day changes: visibility swings (non-overlapping CIs + effect-size floor), leaderboard rank moves, citations gained/lost. The 50 most recent in the window, newest first. This endpoint filters on the date window ONLY — engine/topic/location/branded are not applied here. Requires a key with the `read` scope.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string · uuid | Project (brand) id. |
| from | query | string · date | Range start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window). |
| to | query | string · date | Range end (YYYY-MM-DD, inclusive). Defaults to today. |
* required
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the `{ data: … }` success envelope. |
|
| 400 | Invalid filters (bad date format, `from` after `to`). |
|
| 401 | Missing or unknown API key. |
|
| 403 | The key lacks the required scope, or the workspace's plan does not include API access. |
|
| 404 | No such project in the key's workspace — also returned when a project-scoped key asks for a different project, so a key cannot probe which ids exist. |
|
| 429 | Per-key rate limit exceeded (fixed 60-second window at the key's own limit). |
|
| 500 | Unexpected server error. Not caused by the request; retry with backoff. |
|
Example
cURL
curl \ -H "Authorization: Bearer $RANKSIFY_API_KEY" \ "https://app.ranksify.ai/api/v1/projects/<id>/changes?from=2026-01-01&to=2026-01-28"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/projects/<id>/changes?from=2026-01-01&to=2026-01-28",
{
headers: { Authorization: `Bearer ${process.env.RANKSIFY_API_KEY}` },
},
);
if (!res.ok) {
// On 429, res.headers.get("retry-after") holds the seconds to wait
// before trying again. It is the only rate-limit signal sent.
// Every failure is the same envelope — branch on error.code, never on the message.
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();Response
{
"data": {
"filters": {
"from": "2026-01-01",
"to": "2026-01-28"
},
"events": [
{
"id": "c1a7e0d2-4f83-4a16-b5c9-0d2e6f8a3b41",
"day": "2026-01-21",
"kind": "visibility_gained",
"kindLabel": "Visibility gained",
"subjectType": "brand",
"subjectName": "Acme Analytics",
"engineLabel": "ChatGPT (OpenAI)",
"summary": "4.2% → 37.5% visibility",
"detail": {
"prevDay": "2026-01-20",
"visibility": 37.5,
"prevVisibility": 4.1667,
"delta": 33.3333,
"ci": [
21.1591,
57.2904
],
"prevCi": [
0.7393,
20.2422
],
"sampleSize": 24,
"prevSampleSize": 24
},
"detectedAt": "2026-01-22T03:14:07.412Z"
}
]
}
}GET/v1/usage
Credit balance and price list
This month's credit balance for the key's WORKSPACE (credits are bought per workspace, so there is no project in this path), plus the full published price table so an automated caller can cost a scan before running it. `credits.enforced` says whether running out currently refuses work — treat a measured balance as a hard limit only when it is true. Requires a key with the `read` scope.
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the `{ data: … }` success envelope. |
|
| 401 | Missing or unknown API key. |
|
| 403 | The key lacks the required scope, or the workspace's plan does not include API access. |
|
| 429 | Per-key rate limit exceeded (fixed 60-second window at the key's own limit). |
|
| 500 | Unexpected server error. Not caused by the request; retry with backoff. |
|
Example
cURL
curl \ -H "Authorization: Bearer $RANKSIFY_API_KEY" \ "https://app.ranksify.ai/api/v1/usage"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/usage",
{
headers: { Authorization: `Bearer ${process.env.RANKSIFY_API_KEY}` },
},
);
if (!res.ok) {
// On 429, res.headers.get("retry-after") holds the seconds to wait
// before trying again. It is the only rate-limit signal sent.
// Every failure is the same envelope — branch on error.code, never on the message.
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();Response
{
"data": {
"plan": "pro",
"credits": {
"period": "2026-01-01",
"available": 11400,
"allowance": 12000,
"rollover": 2400,
"granted": 0,
"consumed": 3000,
"enforced": false
},
"pricing": {
"usdPerCredit": 0.002,
"perEngineCheck": {
"openai": 1,
"google_aio": 1,
"grok": 2,
"google_ai_mode": 2,
"perplexity": 3,
"anthropic": 5,
"google_gemini": 15
},
"perAction": {
"agent_chat": 10,
"agent_chat_deep": 75,
"content_generate": 60,
"prompts_generate": 25,
"topics_generate": 25,
"competitors_discover": 25,
"keyword_metrics": 1,
"prompt_research": 18,
"issue_fix": 50,
"content_seo": 50,
"content_interview": 25,
"opportunity_explain": 5,
"insight_narrative": 5,
"detected_market": 25,
"autodetect": 25,
"query_fanout": 10,
"domain_authority": 1,
"sentiment_score": 0
}
},
"dailySpend": {
"spentUsd": 3.3333,
"ceilingUsd": 20
}
}
}GET/v1/bots.json
AI crawler registry
The exact crawler list Ranksify classifies beacon traffic against — public, unauthenticated, cacheable for an hour, and CORS-open, so you can drive your own robots.txt or edge rules from it. Not wrapped in the `{ data }` envelope. Additive changes leave `schemaVersion` alone; renames and removals bump it.
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — the registry, served flat. |
|
Example
cURL
curl \ "https://app.ranksify.ai/api/v1/bots.json"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/bots.json",
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();Response
{
"schemaVersion": 1,
"source": "https://ranksify.ai/docs/bot-registry",
"count": 20,
"bots": [
{
"name": "GPTBot",
"platform": "openai",
"platformLabel": "OpenAI / ChatGPT",
"token": "gptbot"
},
{
"name": "ChatGPT-User",
"platform": "openai",
"platformLabel": "OpenAI / ChatGPT",
"token": "chatgpt-user"
},
{
"name": "OAI-SearchBot",
"platform": "openai",
"platformLabel": "OpenAI / ChatGPT",
"token": "oai-searchbot"
},
{
"name": "ClaudeBot",
"platform": "anthropic",
"platformLabel": "Anthropic / Claude",
"token": "claudebot"
},
{
"name": "Claude-SearchBot",
"platform": "anthropic",
"platformLabel": "Anthropic / Claude",
"token": "claude-searchbot"
},
{
"name": "Claude-Web",
"platform": "anthropic",
"platformLabel": "Anthropic / Claude",
"token": "claude-web"
},
{
"name": "Claude-User",
"platform": "anthropic",
"platformLabel": "Anthropic / Claude",
"token": "claude-user"
},
{
"name": "anthropic-ai",
"platform": "anthropic",
"platformLabel": "Anthropic / Claude",
"token": "anthropic-ai"
},
{
"name": "PerplexityBot",
"platform": "perplexity",
"platformLabel": "Perplexity",
"token": "perplexitybot"
},
{
"name": "Perplexity-User",
"platform": "perplexity",
"platformLabel": "Perplexity",
"token": "perplexity-user"
},
{
"name": "Google-Agent",
"platform": "google",
"platformLabel": "Google / Gemini",
"token": "google-agent"
},
{
"name": "Bytespider",
"platform": "bytedance",
"platformLabel": "ByteDance",
"token": "bytespider"
},
{
"name": "CCBot",
"platform": "commoncrawl",
"platformLabel": "Common Crawl",
"token": "ccbot"
},
{
"name": "Applebot",
"platform": "apple",
"platformLabel": "Apple Intelligence",
"token": "applebot"
},
{
"name": "cohere-ai",
"platform": "cohere",
"platformLabel": "Cohere",
"token": "cohere-ai"
},
{
"name": "Meta-ExternalAgent",
"platform": "meta",
"platformLabel": "Meta AI",
"token": "meta-externalagent"
},
{
"name": "Claude-Code",
"platform": "anthropic",
"platformLabel": "Anthropic / Claude",
"token": "claude-code"
},
{
"name": "Cursor",
"platform": "cursor",
"platformLabel": "Cursor",
"token": "cursor"
},
{
"name": "Devin",
"platform": "cognition",
"platformLabel": "Devin / Cognition",
"token": "devin"
},
{
"name": "opencode",
"platform": "opencode",
"platformLabel": "OpenCode",
"token": "opencode"
}
],
"platforms": {
"openai": "OpenAI / ChatGPT",
"anthropic": "Anthropic / Claude",
"perplexity": "Perplexity",
"google": "Google / Gemini",
"microsoft": "Microsoft Copilot",
"bytedance": "ByteDance",
"commoncrawl": "Common Crawl",
"apple": "Apple Intelligence",
"cohere": "Cohere",
"meta": "Meta AI",
"you": "You.com",
"poe": "Poe",
"deepseek": "DeepSeek",
"xai": "xAI / Grok",
"mistral": "Mistral / Le Chat",
"kagi": "Kagi",
"phind": "Phind",
"duckduckgo": "DuckDuckGo / Duck.ai",
"cursor": "Cursor",
"cognition": "Devin / Cognition",
"opencode": "OpenCode"
}
}GET/v1/openapi.json
This document
The machine-readable OpenAPI 3.0.3 spec for everything above. Public and unauthenticated — point a client generator straight at it.
Responses
| Status | Description | Body |
|---|---|---|
| 200 | OK — an OpenAPI 3.0.3 document. | object |
Example
cURL
curl \ "https://app.ranksify.ai/api/v1/openapi.json"
TypeScript
const res = await fetch(
"https://app.ranksify.ai/api/v1/openapi.json",
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();Schemas
Shared schemas
Error
- error objectrequired
- code "bad_request" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "quota_exceeded" | "internal"required
"bad_request" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "quota_exceeded" | "internal"
- message stringrequired
Human-readable, not stable across releases.
string — Human-readable, not stable across releases.
- details object
Present only on validation failures (the offending fields).
object — Present only on validation failures (the offending fields).
- code "bad_request" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "quota_exceeded" | "internal"required
InsightsFilters
- from string · daterequired
string · date
- to string · daterequired
string · date
- engineId string · uuid
string · uuid
- topicId string · uuid
string · uuid
- location string
string
- branded boolean
boolean
Subject
- subjectType "brand" | "competitor"required
"brand" | "competitor"
- subjectId string · uuidrequired
string · uuid
TimeseriesPoint
- day string · daterequired
string · date
- sampleSize integerrequired
Ok runs that day — the visibility denominator.
integer — Ok runs that day — the visibility denominator.
- mentionRuns integerrequired
Ok runs mentioning the subject — the numerator.
integer — Ok runs mentioning the subject — the numerator.
- visibility numberrequired
BE-D02 — `mentionRuns / sampleSize` as a percentage (0-100), or NULL when the sample cannot resolve a rate (fewer than 5 completed runs). Never 0 for an unmeasured subject: `null` means "we have not measured this", which is a different fact from a measured 0%. `sampleSize`, `mentionRuns`, `ciLow` and `ciHigh` are NEVER withheld and are what remain — at n=0 the Wilson band is the unconstrained 0-100.
number — BE-D02 — `mentionRuns / sampleSize` as a percentage (0-100), or NULL when the sample cannot resolve a rate (fewer than 5 completed runs). Never 0 for an unmeasured subject: `null` means "we have not measured this", which is a different fact from a measured 0%. `sampleSize`, `mentionRuns`, `ciLow` and `ciHigh` are NEVER withheld and are what remain — at n=0 the Wilson band is the unconstrained 0-100.
- rateWithheld string
Present ONLY when `visibility` is null, so its presence is the signal. The reason, as one sentence: "Not sampled yet" or "Too few runs for a rate (n=3, needs 5)".
string — Present ONLY when `visibility` is null, so its presence is the signal. The reason, as one sentence: "Not sampled yet" or "Too few runs for a rate (n=3, needs 5)".
- ciLow numberrequired
Wilson 95% lower bound on visibility (0-100).
number — Wilson 95% lower bound on visibility (0-100).
- ciHigh numberrequired
Wilson 95% upper bound on visibility (0-100).
number — Wilson 95% upper bound on visibility (0-100).
- shareOfVoice number
Percentage of all mentions that day that were this subject. Null when nobody was mentioned.
number — Percentage of all mentions that day that were this subject. Null when nobody was mentioned.
- avgPosition number
Mean placement within the answer. Null when the subject was never mentioned.
number — Mean placement within the answer. Null when the subject was never mentioned.
- sentiment number
Mean sentiment 0-100. Null when nothing was scored — never fabricated as 50.
number — Mean sentiment 0-100. Null when nothing was scored — never fabricated as 50.
- sentimentCount integer
Mentions carrying a sentiment score — the DENOMINATOR of `sentiment`. `sentiment` is a mean, so this, not `sampleSize`, is its sample size; pooling `sentiment` across days without weighting by it is a mean of means.
integer — Mentions carrying a sentiment score — the DENOMINATOR of `sentiment`. `sentiment` is a mean, so this, not `sampleSize`, is its sample size; pooling `sentiment` across days without weighting by it is a mean of means.
- mentions integer
integer
- citations integer
Citations of the subject's domain, hallucinated ones excluded.
integer — Citations of the subject's domain, hallucinated ones excluded.
RankingRow
- subjectType "brand" | "competitor"required
"brand" | "competitor"
- subjectId string · uuidrequired
string · uuid
- name stringrequired
string
- color string
Chart colour assigned in-app.
string — Chart colour assigned in-app.
- domain string
string
- sampleSize integerrequired
integer
- mentionRuns integer
integer
- visibility numberrequired
BE-D02 — `mentionRuns / sampleSize` as a percentage (0-100), or NULL when the sample cannot resolve a rate (fewer than 5 completed runs). Never 0 for an unmeasured subject: `null` means "we have not measured this", which is a different fact from a measured 0%. `sampleSize`, `mentionRuns`, `ciLow` and `ciHigh` are NEVER withheld and are what remain — at n=0 the Wilson band is the unconstrained 0-100.
number — BE-D02 — `mentionRuns / sampleSize` as a percentage (0-100), or NULL when the sample cannot resolve a rate (fewer than 5 completed runs). Never 0 for an unmeasured subject: `null` means "we have not measured this", which is a different fact from a measured 0%. `sampleSize`, `mentionRuns`, `ciLow` and `ciHigh` are NEVER withheld and are what remain — at n=0 the Wilson band is the unconstrained 0-100.
- rateWithheld string
Present ONLY when `visibility` is null, so its presence is the signal. The reason, as one sentence.
string — Present ONLY when `visibility` is null, so its presence is the signal. The reason, as one sentence.
- ciLow numberrequired
number
- ciHigh numberrequired
number
- shareOfVoice number
number
- avgPosition number
number
- sentiment number
number
- mentions integer
integer
- citations integer
integer
StatusCounts
- verified integerrequired
integer
- broken integerrequired
integer
- hallucinated integerrequired
URLs the engine invented. Counted here, excluded from citedCount.
integer — URLs the engine invented. Counted here, excluded from citedCount.
- unchecked integerrequired
integer
- stale integerrequired
integer
SourceDomain
- domain stringrequired
string
- citedCount integerrequired
Citations that count — everything except hallucinated.
integer — Citations that count — everything except hallucinated.
- totalCount integerrequired
All citations, hallucinated included.
integer — All citations, hallucinated included.
- statusCounts StatusCountsrequired
- verified integerrequired
integer
- broken integerrequired
integer
- hallucinated integerrequired
URLs the engine invented. Counted here, excluded from citedCount.
integer — URLs the engine invented. Counted here, excluded from citedCount.
- unchecked integerrequired
integer
- stale integerrequired
integer
- verified integerrequired
- urls integerrequired
Distinct URLs seen under this domain.
integer — Distinct URLs seen under this domain.
- articleTypes array
Page kinds cited on this domain.
array of"video" | "forum" | "comparison" | "listicle" | "review" | "docs" | "news" | "blog" | "homepage" | "other"
- lastSeen string · daterequired
string · date
- sourceType "owned" | "competitor" | "editorial" | "ugc" | "corporate" | "institutional" | "review" | "social" | "other"required
Editorial relationship to your brand.
"owned" | "competitor" | "editorial" | "ugc" | "corporate" | "institutional" | "review" | "social" | "other" — Editorial relationship to your brand.
ChangeEvent
- id string · uuidrequired
string · uuid
- day string · daterequired
The day evaluated.
string · date — The day evaluated.
- kind "visibility_gained" | "visibility_dropped" | "rank_improved" | "rank_dropped" | "citation_gained" | "citation_lost" | "rival_ad_on_branded_prompt"required
"visibility_gained" | "visibility_dropped" | "rank_improved" | "rank_dropped" | "citation_gained" | "citation_lost" | "rival_ad_on_branded_prompt"
- kindLabel stringrequired
Display label for `kind`.
string — Display label for `kind`.
- subjectType "brand" | "competitor"required
"brand" | "competitor"
- subjectName stringrequired
string
- engineLabel string
Null when the move is across all engines.
string — Null when the move is across all engines.
- summary stringrequired
One-line human summary, e.g. `12.5% → 37.5% visibility`.
string — One-line human summary, e.g. `12.5% → 37.5% visibility`.
- detail objectrequired
Kind-specific evidence: the two values, the delta, both confidence intervals and both sample sizes.
object — Kind-specific evidence: the two values, the delta, both confidence intervals and both sample sizes.
- detectedAt string · date-timerequired
string · date-time
ProjectSummary
- id string · uuidrequired
string · uuid
- name stringrequired
string
- domain stringrequired
Bare host, no scheme and no `www.`.
string — Bare host, no scheme and no `www.`.
Crawler
- name stringrequired
Canonical bot name as it appears in the user-agent.
string — Canonical bot name as it appears in the user-agent.
- platform stringrequired
Machine key, e.g. `openai`.
string — Machine key, e.g. `openai`.
- platformLabel stringrequired
string
- token stringrequired
Lowercase substring matched in the user-agent.
string — Lowercase substring matched in the user-agent.