Visibility over time
Visibility as a series rather than a single number. This is the endpoint a warehouse sync appends from nightly, and the one behind any trend chart.
For why a number moved rather than that it moved, compare periods with Get visibility and drill into Prompt metrics, which names the prompts that drove the change.
GET /v1/brands/{brand_id}/visibility/timeseriesAuthorization
| Header | Type | Required |
|---|---|---|
Authorization | Bearer asky_sk_… | required |
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
brand_id | uuid | required | From List brands |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | optional | Rows per page, 1 to 100. Defaults to 20. |
cursor | string | optional | Opaque cursor from the previous response. Treat it as a token, never construct one. |
start_date | YYYY-MM-DD | optional | Inclusive lower bound, YYYY-MM-DD in UTC. Narrows the population before metrics are computed. |
end_date | YYYY-MM-DD | optional | Inclusive upper bound, YYYY-MM-DD in UTC. Narrows the population before metrics are computed. |
include_citation_rate | boolean | optional | Add citation rate to each point. Requires the read:citations scope. |
topic_id | uuid, repeatable | optional | Restrict to these topics. From List topics. Includes the topic and its subtopics. A root topic returns everything beneath it; a subtopic returns only itself. Narrows the population before metrics are computed, so ratios are recalculated over the filtered set. |
engine | string, repeatable | optional | Restrict to these AI search engines, by slug. From List engines. Narrows the population before metrics are computed. |
country | string, repeatable | optional | Restrict to these execution country codes. Narrows the population before metrics are computed. |
tag_id | uuid, repeatable | optional | Restrict to prompts carrying these tags. From List tags. Narrows the population before metrics are computed. |
Request
cURL
curl --request GET \
--url 'https://api.askylabs.com/v1/brands/242546de-4cee-4a7d-952e-f6aa60c63ef8/visibility/timeseries?start_date=2026-07-14&end_date=2026-08-13' \
--header 'Authorization: Bearer <api-key>'Response
200
{
"data": [
{
"date": "2026-07-14",
"visibility_pct": 28.1,
"executions": 61
},
{
"date": "2026-07-15",
"visibility_pct": 31.9,
"executions": 60
}
],
"pagination": {
"limit": 20,
"has_more": true,
"next_cursor": "eyJ2IjoxLCJvIjoyMCwibCI6MjB9"
},
"meta": {
"period": {
"start_date": "2026-07-14",
"end_date": "2026-08-13"
},
"granularity": "daily",
"brand_id": "242546de-4cee-4a7d-952e-f6aa60c63ef8"
},
"request_id": "req_74be0c2af1834d9e6a51"
}Response fields
| Field | Type | Description |
|---|---|---|
data.brand_id | uuid | required |
data.period | object | required. { start_date, end_date } actually used. Read it before comparing endpoints — their defaults differ |
data.granularity | string | required. daily or weekly. Derived from the span, not chosen |
data.points | object[] | required. { date, visibility_pct, executions } |
data.points[].citation_rate | number | null | Only with include_citation_rate |
data.omitted_for_scope | string[] | Present when a requested series was dropped for lack of scope |
data.partial_errors | string[] | Present when a series failed to load. The rest of the response is still valid |
request_id | string | required. Also returned as the x-request-id header. Quote it when contacting support |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | A parameter is unknown or malformed. The message names it |
| 401 | invalid_token | The key is missing, malformed, or does not exist |
| 401 | token_expired | The key passed its expiry date |
| 401 | token_revoked | The key was revoked |
| 404 | not_found | The brand does not exist, or this key is not allowed to reach it. The two are deliberately indistinguishable |
| 429 | rate_limited | Over 300 requests per minute. See the Retry-After header |
Notes
granularity is derived from the range, not requested. Short spans return daily points, longer
spans roll up to ISO weeks. Read the field rather than assuming.
A missing scope degrades rather than fails. Asking for include_citation_rate without
read:citations returns the series without it and names it in omitted_for_scope, so a
partially-scoped key still gets useful data instead of a 403.
Last updated on