CitationsList Citations

Citations

URL-level citation rollup for a tracked domain — every page that appeared as a cited source in AI responses to the domain's tracked prompts.

curl --request GET \
  --url 'https://api.aiclicks.io/api/v1/citations?domain_id=8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3&days=30' \
  --header 'Authorization: Bearer ak_live_xxx'
{
  "data": {
    "items": [
      {
        "url": "aiclicks.io/blog/best-ai-optimization-tools",
        "domain": "aiclicks.io",
        "page_type": "Listicle",
        "page_title": "14 Best AI Search Visibility Optimization Tools in 2026",
        "frequency": 75,
        "mentions": 118,
         "prompt_count": 28,
        "prompt_ids": [
          "3b7c1e90-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
          "9f0e1d2c-3b4a-5968-8776-1a2b3c4d5e6f"
        ],
        "models": [
          "google-ai-mode",
          "google-ai-overviews",
          "gpt-4o-search"
        ],
        "first_seen": "2026-04-27T08:58:37.268809+00:00",
        "last_seen": "2026-06-15T08:20:57.676821+00:00"
      },
      {
        "url": "rankability.com/perplexity-rank-tracker",
        "domain": "rankability.com",
        "page_type": "Generic Article",
        "page_title": "Perplexity Rank Tracker — Track Citations, Sources & Brand Mentions | Rankability",
        "frequency": 72,
        "mentions": 96,
        "prompt_count": 23,
        "prompt_ids": [
          "3b7c1e90-1a2b-4c3d-9e4f-5a6b7c8d9e0f"
        ],
        "models": [
          "google-ai-mode",
          "google-ai-overviews",
          "gpt-4o-search"
        ],
        "first_seen": "2026-04-27T08:58:37.268809+00:00",
        "last_seen": "2026-06-15T08:20:57.676821+00:00"
      }
    ]
  },
  "domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
  "days": 30,
  "generated_at": "2026-06-17T10:00:11.218Z"
}

One row per URL that appeared as a citation in any AI response triggered by one of the domain's tracked prompts during the window. Includes every cited URL, not just URLs on the brand's own domain — so the same call surfaces:

  • the brand's own pages (filter domain == <your brand> client-side),
  • competitor pages cited for the same prompts,
  • third-party listicles, review sites, forums, and YouTube videos cited alongside them.

Each row carries the URL's citation frequency, its aggregate mentions, how many distinct prompts cited it, which AI channels cited it, and the auto-classified page_type (Listicle, How-to, Reviews, Forums, YouTube, Generic Article, Homepage, Social, Alternatives, Comparison, …). Sorted by frequency descending.

domain_id is a required query parameter. Use GET /api/v1/domains to discover which domains the calling key can access.

Authorizations

header
Authorizationstring
Required

Your API key formatted as Bearer ak_live_<your-key>. Create one in the dashboard under Settings → Developers.

header
X-Request-Idstring

Optional UUID for log correlation. If omitted, we generate one and echo it back in the response.

Query parameters

query
domain_idstring
Required

UUID of the domain. Find domains via GET /api/v1/domains. Omitting this returns 400.

query
daysinteger

Look-back window, 1–365. Defaults to 30. Counted as an exact window (today - days + 1), not snapped to buckets.

query
pageinteger

Optional. If you send it, the API returns one page of rows. data then also contains total, page, size and total_pages. Request pages until page is equal to total_pages. If you do not send it, the API returns all rows. For a long window, this list can be very large.

query
sizeinteger

Optional. The number of rows on each page, 1–1000. The default is 200. The API uses it only when you send page.

query
min_frequencyinteger

Optional. Keep only the URLs with a frequency of this value or more. The default is 0.

query
cited_domainstring

Optional. Keep only the URLs on this exact host, for example youtube.com.

query
page_typestring

Optional. Keep only the URLs with this page_type, for example Listicle. The match ignores case.

query
group_bystring

Optional. Set it to domain or page_type to get one row for each group in place of one row for each URL. Each group row has the group key, citations, mentions, url_count and prompt_count. The rows are sorted by citations, descending.

query
is_brand_ownedboolean

Optional. If true, the API keeps only the brand's own pages. If false, it keeps only third-party pages.

query
mentionedboolean

Optional. If true, the API keeps only cited pages that mention the brand. If false, it keeps only cited pages that do not mention the brand.

query
modelstring

Optional. Use one AI channel only. Give the generic name: ChatGPT, Perplexity, Gemini, AI Overviews, AI Mode, Claude, Microsoft Copilot, Grok, Meta AI, Mistral, Qwen, DeepSeek or Naver. The default is all.

query
start_datestring

Optional. The first day of a fixed window, in YYYY-MM-DD format. Send it with end_date. A fixed window overrides days.

query
end_datestring

Optional. The last day of a fixed window, in YYYY-MM-DD format. The window includes this day. Send it with start_date.

query
topic_idsarray

Optional. Use only the prompts in one of these topics. Get the UUIDs from GET /api/v1/topics. Repeat the parameter for each ID. If you also send tag_ids, the API keeps a prompt that matches a topic or a tag.

query
tag_idsarray

Optional. Use only the prompts that have one of these tags. Get the UUIDs from GET /api/v1/tags. Repeat the parameter for each ID.

query
freshboolean

Optional. If true, the API ignores the cached result and calculates a new one. The new result replaces the cached one. The default is false.

Response

dataobject
Required

The list payload.

data.itemsarray
Required

One row per cited URL, sorted by frequency descending. Includes URLs from the brand domain, competitors, and any other source cited in responses to the domain's tracked prompts during the window.

data.summaryobject
Required

Totals for the URLs that pass the filters: total_citations, mentioned_citations, not_mentioned_citations, mentioned_percent, not_mentioned_percent, mentioned_pages and not_mentioned_pages. The percentages are 0–100, with two decimals. Paging does not change the summary.

domain_idstring
Required

Echo of the requested domain.

daysinteger
Required

Echo of the requested window.

generated_atstring
Required

ISO-8601 timestamp of when the server produced (or cached) this response.

Response headers

HeaderDescription
X-CacheHIT or MISS. Indicates whether the response came from cache.
X-Request-IdUnique request id. Echoes incoming if you set one.
X-RateLimit-LimitMax requests per minute for this key.
X-RateLimit-RemainingRequests remaining in current minute.
X-RateLimit-ResetUnix epoch seconds when the window resets.

Caching

Cached for 1 hour per request parameter set. Newly completed analyses appear after the TTL expires.

How citations are rolled up

For every response that ran on one of the domain's tracked prompts in the window:

  1. Walk the response's citation array.
  2. Group entries by normalized url.
  3. Per URL, count the responses that cited it (frequency), sum the per-response mention counts (mentions), collect the distinct prompts that cited it (prompt_ids, with prompt_count its length), union the AI channels (models), and record the first / last response timestamps (first_seen / last_seen).
  4. Annotate each URL with its cached page_type and page_title.
  5. Sort by frequency descending.

The same URL appearing twice in a single response counts as one for frequency (per-response, not per-array-entry), but both occurrences add to mentions. Citation array entries with an unparseable URL are dropped.

Common client-side recipes

The endpoint is intentionally raw so callers can compose it. A few useful one-liners against data.items:

  • Your brand's most-cited pages: filter(domain == <your brand>) → sort by frequency.
  • Top external citers: filter(domain != <your brand>) → group by domain → sum frequency.
  • Source-type breakdown: group by page_type → sum frequency.
  • Reddit / YouTube / Medium share: filter(domain contains "youtube"|"reddit"|"medium") / total frequency.
  • Untracked-competitor discovery: group by domain → exclude tracked competitor list from /api/v1/brand-rankings → sort by frequency.
  • Which prompts drove a URL: take a row's prompt_ids → map against GET /api/v1/prompts for text + estimated_search_volume. Enables retrieval-decay → prompt attribution.

Errors

400 Bad Requesterror

domain_id query parameter missing. Body points you at /api/v1/domains.

401 Unauthorizederror

Missing, malformed, or revoked API key.

403 Forbiddenerror

Team's developer_access flag is off, the domain belongs to a team your API key is not scoped to, or your allowed_domains allowlist excludes it.

404 Not Founderror

domain_id is malformed, does not exist, or your user is not a member of its team.

429 Too Many Requestserror

Rate limit exceeded. Inspect the Retry-After header for how long to wait.

Empty result

If no analyses ran in the window — or none produced citations — the response is a successful 200 with an empty list:

{
  "data": { "items": [] },
  "domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
  "days": 30,
  "generated_at": "2026-06-17T10:00:11.218Z"
}