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'
import httpx, os
resp = httpx.get(
"https://api.aiclicks.io/api/v1/citations",
params={
"domain_id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"days": 30,
},
headers={"Authorization": f"Bearer {os.environ['AICLICKS_API_KEY']}"},
)
resp.raise_for_status()
for row in resp.json()["data"]["items"][:10]:
print(f"{row['frequency']:>4} {row['domain']:<28} {row['page_type']:<18} {row['url']}")
const url = new URL("https://api.aiclicks.io/api/v1/citations");
url.searchParams.set("domain_id", "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3");
url.searchParams.set("days", "30");
const resp = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.AICLICKS_API_KEY}` },
});
const { data } = await resp.json();
for (const row of data.items.slice(0, 10)) {
console.log(`${row.frequency} ${row.domain} ${row.page_type} ${row.url}`);
}
{
"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,
"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,
"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"
}
{ "detail": "domain_id query parameter is required. List the domains this key can access with GET /api/v1/domains, then pass ?domain_id=<uuid>." }
{ "detail": "Invalid or revoked API key" }
{ "detail": "API access is not enabled for this team. Contact support@aiclicks.io." }
{ "detail": "Domain not found" }
{ "detail": "Too many requests. Please try again later." }
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
Your API key formatted as Bearer ak_live_<your-key>. Create one in the dashboard under Settings → Developers.
Optional UUID for log correlation. If omitted, we generate one and echo it back in the response.
Query parameters
UUID of the domain. Find domains via GET /api/v1/domains. Omitting this returns 400.
Look-back window, 1–365. Defaults to 30. Snapped to the nearest known bucket: 1, 7, 14, 30, 90, 180, 270, or 365 days.
Response
The list payload.
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.
The cited URL, normalized without scheme (e.g. aiclicks.io/blog/best-ai-optimization-tools).
Host portion of url. Use this to filter to your brand, a competitor, or a source category (e.g. domain == "youtube.com").
Auto-classified page type. Common values: Listicle, Generic Article, YouTube, How-to, Reviews, Forums, Homepage, Social, Alternatives, Comparison, Landing, Marketplace.
Cached page title from the last fetch. Can be null for pages that failed to render or block crawlers.
Number of AI responses in the window where this URL appeared as a citation. The primary ranking metric — the list is sorted by it.
Aggregate mention count for this URL across all responses — the sum of per-response mention counts. Can exceed frequency, since a single response may mention the source more than once.
Number of distinct tracked prompts whose responses cited this URL at least once. A scalar — not an enumerable list. To enumerate the prompts themselves, you'd need a separate prompt-view endpoint (not currently public).
The AI channels that cited this URL at least once during the window. Values like gpt-4o-search, google-ai-mode, google-ai-overviews, claude-sonar, etc. Presence only — does not encode per-model mention counts.
ISO-8601 UTC timestamp of the earliest response in the window that cited this URL.
ISO-8601 UTC timestamp of the most recent response in the window that cited this URL.
Echo of the requested domain.
Echo of the requested window.
ISO-8601 timestamp of when the server produced (or cached) this response.
Response headers
| Header | Description |
|---|---|
X-Cache | HIT or MISS. Indicates whether the response came from cache. |
X-Request-Id | Unique request id. Echoes incoming if you set one. |
X-RateLimit-Limit | Max requests per minute for this key. |
X-RateLimit-Remaining | Requests remaining in current minute. |
X-RateLimit-Reset | Unix epoch seconds when the window resets. |
Caching
Cached for 1 hour per (domain_id, days). 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:
- Walk the response's citation array.
- Group entries by normalized
url. - Per URL, count the responses that cited it (
frequency), sum the per-response mention counts (mentions), count distinct prompts (prompt_count), union the AI channels (models), and record the first / last response timestamps (first_seen/last_seen). - Annotate each URL with its cached
page_typeandpage_title. - Sort by
frequencydescending.
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.
Errors
domain_id query parameter missing. Body points you at /api/v1/domains.
Missing, malformed, or revoked API key.
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.
domain_id is malformed, does not exist, or your user is not a member of its team.
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"
}