Visibility Time Series
Daily visibility percentage for a tracked domain across every model channel.
curl --request GET \
--url 'https://api.aiclicks.io/api/v1/visibility-time-series?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/visibility-time-series",
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"]:
print(f"{row['date']}: {row['value']}%")
const url = new URL("https://api.aiclicks.io/api/v1/visibility-time-series");
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) {
console.log(`${row.date}: ${row.value}%`);
}
{
"data": {
"items": [
{ "date": "2026-05-19", "value": 12.4 },
{ "date": "2026-05-20", "value": 13.1 },
{ "date": "2026-05-21", "value": 11.8 },
{ "date": "2026-05-22", "value": 0 }
]
},
"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."
}
Visibility is the share of LLM responses that mention your domain. Formally, for each day:
visibility = responses_with_brand_mention / total_responses * 100
Clamped at 100. A day with zero responses returns value: 0. By default, the series includes all model channels. For one channel, use the model parameter.
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. Counted as an exact window (today - days + 1), not snapped to buckets.
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.
Optional. The first day of a fixed window, in YYYY-MM-DD format. Send it with end_date. A fixed window overrides days.
Optional. The last day of a fixed window, in YYYY-MM-DD format. The window includes this day. Send it with start_date.
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.
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.
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
The list payload.
One row per day in the window, oldest first. Days with no analyses are still present with value: 0.
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. The cache key includes every query parameter. Newly completed analyses appear after the TTL expires.
How visibility is calculated
For each day in the window:
- Count every model response that ran on a tracked prompt — that's the denominator.
- Count responses where the brand appears at least once in the answer — that's the numerator.
- Divide, multiply by 100, clamp at 100.
The denominator includes channels where the brand has zero chance of being cited (e.g. AI Overviews on queries with no overview). A sudden visibility drop across every competitor in the same week usually means a new channel was added to the panel, not a real loss of visibility. It could also mean a data ingestion failure on our part.
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, 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"
}