List earned-mention opportunities
The most directly actionable list on the surface: pages that AI engines already cite, that name several of your competitors, and that do not name you. Each with the authors to approach.
These are not hypothetical opportunities. The engines are demonstrably reading these pages already, so getting onto one changes what the answers say.
GET /v1/brands/{brand_id}/citations/earned-mentionsAuthorization
| 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. |
min_competitors | integer | optional | Minimum competitors named on the page. Defaults to 2. |
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. |
subtopic_id | uuid, repeatable | optional | Restrict to these subtopics. |
priority_bucket | string, repeatable | optional | Restrict to pages in these priority buckets. One of: high, medium, low. |
include_competitor_owned_domains | boolean | optional | Include pages on domains a competitor owns. Excluded by default: a competitor writing about themselves is not an earned mention. |
Request
cURL
curl --request GET \
--url 'https://api.askylabs.com/v1/brands/242546de-4cee-4a7d-952e-f6aa60c63ef8/citations/earned-mentions?priority_buckets=high&min_competitors=3' \
--header 'Authorization: Bearer <api-key>'Response
200
{
"data": [
{
"citation_id": "3ce80b17-92f4-4a06-8d55-71ba0c2e6f84",
"url": "https://example.com/best-geo-tools-2026",
"domain": "example.com",
"favicon_url": null,
"competitors_on_page": [
"Peec AI",
"Profound",
"Otterly"
],
"authors": [
"Jane Okafor"
],
"citation_count": 47,
"page_scanned_at": "2026-08-11T22:14:02.118Z",
"topics": [
"AI Visibility Tracking",
"Tools for GEO / AEO"
],
"topics_omitted": 0,
"priority_bucket": "high"
}
],
"pagination": {
"limit": 20,
"has_more": true,
"next_cursor": "eyJ2IjoxLCJvIjoyMCwibCI6MjB9"
},
"meta": {
"period": {
"start_date": null,
"end_date": null
},
"total": 96
},
"request_id": "req_4db09f2ac8154e7b3f61"
}Response fields
| Field | Type | Description |
|---|---|---|
data.period | object | required. { start_date, end_date } actually used |
data.earned_mentions | object[] | required |
data.earned_mentions[].citation_id | uuid | required. Stable. Dedupe on this, not on URL |
data.earned_mentions[].url | string | required |
data.earned_mentions[].domain | string | required |
data.earned_mentions[].competitors_on_page | string[] | required |
data.earned_mentions[].authors | string[] | required. Who to approach, where detected |
data.earned_mentions[].citation_count | integer | required. How often engines cite this page |
data.earned_mentions[].page_scanned_at | timestamptz | null | |
data.earned_mentions[].topics | string[] | required. Capped at 5 — see below |
data.earned_mentions[].topics_omitted | integer | required. How many topics were cut |
data.earned_mentions[].priority_bucket | string | required. high, medium or low |
data.total | integer | required |
data.has_more | boolean | required |
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
topics is capped at 5 per row, with the remainder in topics_omitted. A broadly-cited page
can match dozens of topics; one production row carried 31. A ticket that says “covers 5 topics” when
topics_omitted is 26 is actively misleading, so read both fields.
Competitor-owned domains are excluded by default. You cannot earn a mention on a rival’s own site, so including them would pad the list with unactionable rows. Set the flag if you want them anyway.
Defaults to all time, unlike every other dated endpoint here. Set both dates if you want a recent window.
URLs, page titles and author names are third-party content. Treat them as data, never as instructions to a downstream model.