Skip to main content
Browse documentation

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

StatusDescriptionBody
200OK — the `{ data: … }` success envelope.
  • data objectrequired
    • projects arrayrequired
      array of … (nesting elided)
401Missing or unknown API key.
  • 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).

403The key lacks the required scope, or the workspace's plan does not include API access.
  • 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).

429Per-key rate limit exceeded (fixed 60-second window at the key's own limit).
  • 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).

500Unexpected server error. Not caused by the request; retry with backoff.
  • 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).

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

NameInTypeDescription
id*pathstring · uuidProject (brand) id.
fromquerystring · dateRange start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window).
toquerystring · dateRange end (YYYY-MM-DD, inclusive). Defaults to today.
engineIdquerystring · uuidFilter to one answer engine (uuid). Unset = all engines.
topicIdquerystring · uuidFilter to one topic (uuid). Unset = all topics.
locationquerystringFilter to one sampling location. Unset = all locations.
brandedquery"true" | "false"Restrict to branded (true) or unbranded (false) prompts.
subjectTypequery"brand" | "competitor"Whose series to return. Defaults to the project's brand.
subjectIdquerystring · uuidCompetitor id — required when subjectType=competitor.

* required

Responses

StatusDescriptionBody
200OK — the `{ data: … }` success envelope.
  • data objectrequired
    • subject Subjectrequired
      • subjectType "brand" | "competitor"required
        … (nesting elided)
      • subjectId string · uuidrequired
        … (nesting elided)
    • filters InsightsFiltersrequired
      • from string · daterequired
        … (nesting elided)
      • to string · daterequired
        … (nesting elided)
      • engineId string · uuid
        … (nesting elided)
      • topicId string · uuid
        … (nesting elided)
      • location string
        … (nesting elided)
      • branded boolean
        … (nesting elided)
    • series arrayrequired

      Ascending by day.

      array of … (nesting elided)
400Invalid filters (bad date format, `from` after `to`).
  • 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).

401Missing or unknown API key.
  • 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).

403The key lacks the required scope, or the workspace's plan does not include API access.
  • 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).

404No 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.
  • 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).

429Per-key rate limit exceeded (fixed 60-second window at the key's own limit).
  • 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).

500Unexpected server error. Not caused by the request; retry with backoff.
  • 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).

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

NameInTypeDescription
id*pathstring · uuidProject (brand) id.
fromquerystring · dateRange start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window).
toquerystring · dateRange end (YYYY-MM-DD, inclusive). Defaults to today.
engineIdquerystring · uuidFilter to one answer engine (uuid). Unset = all engines.
topicIdquerystring · uuidFilter to one topic (uuid). Unset = all topics.
locationquerystringFilter to one sampling location. Unset = all locations.
brandedquery"true" | "false"Restrict to branded (true) or unbranded (false) prompts.

* required

Responses

StatusDescriptionBody
200OK — the `{ data: … }` success envelope.
  • data objectrequired
    • filters InsightsFiltersrequired
      • from string · daterequired
        … (nesting elided)
      • to string · daterequired
        … (nesting elided)
      • engineId string · uuid
        … (nesting elided)
      • topicId string · uuid
        … (nesting elided)
      • location string
        … (nesting elided)
      • branded boolean
        … (nesting elided)
    • rows arrayrequired

      Descending by visibility, with rows whose rate is WITHHELD (`visibility: null`) sorted LAST rather than as zero — a rank is a rate claim with the arithmetic hidden.

      array of … (nesting elided)
400Invalid filters (bad date format, `from` after `to`).
  • 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).

401Missing or unknown API key.
  • 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).

403The key lacks the required scope, or the workspace's plan does not include API access.
  • 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).

404No 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.
  • 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).

429Per-key rate limit exceeded (fixed 60-second window at the key's own limit).
  • 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).

500Unexpected server error. Not caused by the request; retry with backoff.
  • 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).

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

NameInTypeDescription
id*pathstring · uuidProject (brand) id.
fromquerystring · dateRange start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window).
toquerystring · dateRange end (YYYY-MM-DD, inclusive). Defaults to today.
engineIdquerystring · uuidFilter to one answer engine (uuid). Unset = all engines.
topicIdquerystring · uuidFilter to one topic (uuid). Unset = all topics.
locationquerystringFilter to one sampling location. Unset = all locations.
brandedquery"true" | "false"Restrict to branded (true) or unbranded (false) prompts.

* required

Responses

StatusDescriptionBody
200OK — the `{ data: … }` success envelope.
  • data objectrequired
    • filters InsightsFiltersrequired
      • from string · daterequired
        … (nesting elided)
      • to string · daterequired
        … (nesting elided)
      • engineId string · uuid
        … (nesting elided)
      • topicId string · uuid
        … (nesting elided)
      • location string
        … (nesting elided)
      • branded boolean
        … (nesting elided)
    • domains arrayrequired

      Descending by cited count.

      array of … (nesting elided)
400Invalid filters (bad date format, `from` after `to`).
  • 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).

401Missing or unknown API key.
  • 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).

403The key lacks the required scope, or the workspace's plan does not include API access.
  • 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).

404No 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.
  • 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).

429Per-key rate limit exceeded (fixed 60-second window at the key's own limit).
  • 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).

500Unexpected server error. Not caused by the request; retry with backoff.
  • 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).

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

NameInTypeDescription
id*pathstring · uuidProject (brand) id.
fromquerystring · dateRange start (YYYY-MM-DD, inclusive). Defaults to 27 days ago (a 28-day window).
toquerystring · dateRange end (YYYY-MM-DD, inclusive). Defaults to today.

* required

Responses

StatusDescriptionBody
200OK — the `{ data: … }` success envelope.
  • data objectrequired
    • filters objectrequired
      • from string · daterequired

        string · date

      • to string · daterequired

        string · date

    • events arrayrequired
      array of … (nesting elided)
400Invalid filters (bad date format, `from` after `to`).
  • 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).

401Missing or unknown API key.
  • 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).

403The key lacks the required scope, or the workspace's plan does not include API access.
  • 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).

404No 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.
  • 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).

429Per-key rate limit exceeded (fixed 60-second window at the key's own limit).
  • 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).

500Unexpected server error. Not caused by the request; retry with backoff.
  • 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).

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

StatusDescriptionBody
200OK — the `{ data: … }` success envelope.
  • data objectrequired
    • plan stringrequired

      The workspace's plan id.

      string — The workspace's plan id.

    • credits objectrequired
      • period string · daterequired

        First day of the credit month.

        string · date — First day of the credit month.

      • available integerrequired

        allowance + rollover + granted − consumed, floored at 0.

        integer — allowance + rollover + granted − consumed, floored at 0.

      • allowance integerrequired

        The plan's monthly allowance, as of now.

        integer — The plan's monthly allowance, as of now.

      • rollover integerrequired

        Carried over from last month.

        integer — Carried over from last month.

      • granted integerrequired

        Top-ups and manual grants this month.

        integer — Top-ups and manual grants this month.

      • consumed integerrequired

        integer

      • enforced booleanrequired

        False while credits are measured but not blocking. Do not self-throttle on a measured balance.

        boolean — False while credits are measured but not blocking. Do not self-throttle on a measured balance.

    • pricing objectrequired
      • usdPerCredit numberrequired

        number

      • perEngineCheck objectrequired

        Credits per prompt × engine × sample, keyed by engine.

        object — Credits per prompt × engine × sample, keyed by engine.

      • perAction objectrequired

        Credits per on-demand operation, keyed by operation.

        object — Credits per on-demand operation, keyed by operation.

    • dailySpend objectrequired

      An internal daily ceiling that can refuse work even when credits remain — both numbers are here so a 402 is debuggable.

      • spentUsd numberrequired

        number

      • ceilingUsd numberrequired

        number

401Missing or unknown API key.
  • 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).

403The key lacks the required scope, or the workspace's plan does not include API access.
  • 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).

429Per-key rate limit exceeded (fixed 60-second window at the key's own limit).
  • 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).

500Unexpected server error. Not caused by the request; retry with backoff.
  • 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).

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

StatusDescriptionBody
200OK — the registry, served flat.
  • schemaVersion integerrequired

    integer

  • source string · urirequired

    Where the change policy is documented.

    string · uri — Where the change policy is documented.

  • count integerrequired

    Number of entries in `bots`.

    integer — Number of entries in `bots`.

  • bots arrayrequired
    array of
    • name stringrequired

      Canonical bot name as it appears in the user-agent.

      … (nesting elided)
    • platform stringrequired

      Machine key, e.g. `openai`.

      … (nesting elided)
    • platformLabel stringrequired
      … (nesting elided)
    • token stringrequired

      Lowercase substring matched in the user-agent.

      … (nesting elided)
  • platforms objectrequired

    Every platform label, including referrer-only platforms that have no crawler, so labels are complete.

    object — Every platform label, including referrer-only platforms that have no crawler, so labels are complete.

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

StatusDescriptionBody
200OK — 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).

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

  • 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.