Skip to Content

AI Visibility

REST endpoints for AI Visibility. Base URL https://api.competlab.com; every request needs a CL-API-Key header (see Authentication). Responses wrap in { item } or { items }; errors in { error }.

Get latest AI visibility data

GET/v1/projects/{projectId}/ai-visibility

Get the latest AI Visibility for a project: the market map — which companies the AI models recommend in this category, ordered by how often each is named, and where you stand among them — then AI Visibility Scores, mention rates and per-model breakdowns for every company found. The score counts only the top 5 positions in an answer, evenly spaced — first place the most, the last scoring position the least — and nothing below them. It is a reading of WHERE a brand lands when it is named, never of who is ahead: a standing claim — “you lead”, “you trail”, “the leader is X” — rests on how often each brand is named (presence on the market map, or mentionRate within one check) and never on this score, which can favour a brand named half as often. A score of 0 for a brand the answers did name means it was named only below the top 5, or too seldom inside them for the average to register. Read mentionRate beside a 0 score: a non-zero rate means the brand was named, and a 0 rate means no counted answer named it. Each check asks every prompt in the project against every AI model it was dispatched to — 5 today, but a check keeps the model set it ran with, so read answerCoverage.queriesSent and providerStatus for that check’s own count rather than assuming today’s. Rates divide by the answers that came back, not the queries sent, and the queries-sent figure is not returned. Checks published under the full-coverage gate were read for every query they asked — a usable answer came back, or the model was read and had none to show (noAnswerShown, the third query state beside answers and unansweredQueries: read, nothing shown, excluded from every count, not a failure); checks published before that gate stay published and can have been scored over fewer answers, and the response does not say which kind a check is — so describe a rate as a share of the answers counted, never as a share of every query asked. When the most recent check was not scored, latestCheckDataAvailable is present and everything else describes an earlier check. Set includeAnswers=true to also get what the models said on that check — every prompt sent and every brand each model named in rank order, with its stated reasoning — narrowed by brand=<domain>, provider= or promptIndex=. It is large, and how large depends on the project. An entry is one brand a model named, at about 1,500 characters each — so the block grows with three things at once: how many prompts the project asks — 8 on every plan and in the trial — times five models, how many models answered, and how many companies each answer named. No figure quoted here can stand in for summary.totalEntries; read it and size the fetch from it. Google AI Overviews answers additionally carry the overview text and the pages Google cited, which totalEntries does not predict and which brand= keeps by design. brand= keeps every answer and empties the ones that did not name that domain, so you see where they win and where they are invisible. No filter changes a number under summary. The prose returned is the model’s wording about the brands it named, not CompetLab’s assessment of them. For ‘which prompt am I losing on’, read summary.customer.perPrompt first — it is on the plain response and costs nothing. A project with no scored check yet answers 404 no_data_available — not a missing project and not a failed measurement, just nothing measured here so far; the project itself is fine and get_project still describes it. Distinguish it from project_not_found, which means the id is wrong.

Returns · { item }

Path parameters

NameTypeDescription
projectIdstringProject ID

Query parameters

NameTypeRequiredDescription
includeAnswersbooleanSet true to include the models’ raw answers — every prompt sent and every brand each model named, with its stated reasoning. Off by default because the block is large, and how large depends on the project. An entry is one brand a model named, at about 1,500 characters each — so the block grows with three things at once: how many prompts the project asks — 8 on every plan and in the trial — times five models, how many models answered, and how many companies each answer named. No figure quoted here can stand in for summary.totalEntries; read it and size the fetch from it. Google AI Overviews answers additionally carry the overview text and the pages Google cited, which totalEntries does not predict and which the brand= filter keeps by design. The prose it returns is the model’s wording about the brands it named, not CompetLab’s assessment. Default false.
providerstringReturn only this model’s answers. Requires includeAnswers=true. Does not change any number under summary, and does not narrow providerStatus. Values: openai, claude, gemini, perplexity, google_ai_overviews.
brandstringReturn only the entries for this domain, across every answer. Requires includeAnswers=true. Matches brands[].domain, case-insensitively; brand NAMES are the model’s own wording and vary between answers, so they are never matched. EVERY answer is still returned — the ones that did not name this domain come back with an empty brands, because ‘this model answered and did not name them’ is a finding, not an absence of data. A query that produced no answer at all is in unansweredQueries instead and asserts nothing about anyone. Ranks are unaffected: an entry keeps the position it held in the full answer. This is the cheapest way to ask where a competitor wins and where they are invisible: it keeps at most one brand entry per answer instead of every brand the model named, and none at all on the answers that did not name it. Compare summary.totalEntries with the answer count to see the saving on this check. Google AI Overviews answers still carry their overview text and cited pages, which this filter keeps by design.
promptIndexnumberReturn only the answers for this prompt, across every model. Requires includeAnswers=true. Zero-based, matching the per-prompt index used elsewhere in this dimension.
viewstringHow much of the response to return. compact: summary.marketMap.brands is one page of rows — the core by default — with summary.marketMap.brandsPage saying where it sits; the customer’s own row and every tracked competitor’s are always included. Every other field is unchanged, and untrackedCoreBrands and customerStanding are still read from the whole map. full: every row of every list, as stored — large, and meant for export rather than for reading. Omitted, the API’s default view applies: compact on /v1. Values: compact, full.
mapOffsetnumberRows of summary.marketMap.brands to skip, in the order the list is stored. Default 0. Pages the compact view: sent without view it selects the compact view, and beside view=full it is refused with paging_requires_compact_view. Min 0.
mapLimitnumberRows of summary.marketMap.brands to return, 1 to 200. Default the core size (summary.marketMap.coreSize), and never fewer than 10. Pages the compact view: sent without view it selects the compact view, and beside view=full it is refused with paging_requires_compact_view. Range 1–200.

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/ai-visibility?includeAnswers=false&provider=openai&brand=competitor.com&promptIndex=0&view=compact&mapOffset=0&mapLimit=10" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "readingGuide": { "summary.pages": "Pages the engines RETRIEVED while answering, never pages they cited." }, "lastUpdatedAt": "2026-03-15T10:00:00.000Z", "summary": { "customer": { "domain": "mycompany.com", "mentionRate": 66.7, "mentionCount": 6, "aiScore": 73, "perProvider": { "openai": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "claude": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "gemini": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "perplexity": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "google_ai_overviews": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 } }, "perPrompt": [ { "promptIndex": 0, "promptLabel": "best sales forecasting platforms for mid-market teams", "mentionedBy": [ "openai", "claude" ], "score": 61 } ] }, "topCompetitor": { "domain": "competitor.com", "name": "Competitor Inc", "mentionRate": 88.9 }, "mentionRateGap": -22.2, "totalCompetitorsFound": 8, "totalQueries": 12, "totalEntries": 45, "competitorRankings": [ { "domain": "competitor.com", "name": "Competitor Inc", "isOwn": false, "mentionCount": 7, "mentionRate": 77.8, "aiScore": 68, "isTracked": false } ], "promptMarket": { "state": "rivals_named_in_most_answers", "explanation": { "code": "rivals_found_across_answers", "text": "Your prompts are describing your market well. The AI answers we collected mention companies you already track, so the numbers here are measuring the right competition." }, "answersNamingAnyRival": 34, "answersMatchingCurrentPromptText": 60, "checksAnalysed": 5, "perPrompt": [ { "promptId": "7f3c1a2e-9d44-4c8b-9f0e-2b6a5c9d7e10", "promptTextInProject": "best sales forecasting platforms for mid-market teams", "state": "answers_and_rival_list_disagree", "explanation": { "code": "rivals_found_across_answers", "text": "Your prompts are describing your market well. The AI answers we collected mention companies you already track, so the numbers here are measuring the right competition." }, "answersNamingAnyRival": 1, "answersMatchingCurrentPromptText": 20 } ] }, "marketMap": { "checksAnalysed": 5, "answersReceived": 60, "tailIsProvable": true, "coreSize": 9, "perEngine": { "openai": { "answersReceived": 15 }, "claude": { "answersReceived": 15 }, "gemini": { "answersReceived": 15 }, "perplexity": { "answersReceived": 15 }, "google_ai_overviews": { "answersReceived": 15 } }, "profileEngines": [ "openai", "claude", "gemini", "perplexity" ], "brands": [ { "answersNaming": 31, "answersReceived": 60, "presence": 52, "presenceLow": 40, "presenceHigh": 63, "zone": "named_in_a_quarter_or_more_of_answers", "domain": "sienge.com.br", "name": "Sienge", "isOwn": false, "rankByPresence": 2, "perEngine": { "openai": {}, "claude": {}, "gemini": {}, "perplexity": {}, "google_ai_overviews": {} }, "endorsement": { "value": 64, "answersRead": 9, "low": 51, "high": 77 }, "pricePerception": { "value": 64, "answersRead": 9, "low": 51, "high": 77, "tier": "mid_range", "tierAnswers": 6 } } ], "brandsPage": { "offset": 0, "limit": 10, "total": 96, "hasMore": true } } }, "untrackedCoreBrands": [ { "domain": "sienge.com.br", "name": "Sienge", "presence": 52, "presenceLow": 40, "presenceHigh": 63, "answersNaming": 31, "answersReceived": 60 } ], "customerStanding": { "endorsement": { "explanation": { "code": "described_less_warmly_than_some_core_companies", "text": "Of the 7 core companies with enough answers to compare, the AI models describe you less warmly than 3; the evidence cannot yet separate you from the other 4." }, "answersRead": 8, "coreCompared": 7, "coreAboveYou": 3, "coreBelowYou": 0, "coreNotSeparable": 4, "state": "described_less_warmly_than_some_core_companies" }, "price": { "explanation": { "code": "described_less_warmly_than_some_core_companies", "text": "Of the 7 core companies with enough answers to compare, the AI models describe you less warmly than 3; the evidence cannot yet separate you from the other 4." }, "answersRead": 8, "coreCompared": 7, "coreAboveYou": 3, "coreBelowYou": 0, "coreNotSeparable": 4, "state": "read_as_cheaper_than_every_core_company" } }, "latestCheckDataAvailable": { "available": false, "reason": "incomplete_coverage", "measuredAnswers": 8, "expectedAnswers": 12, "absentAnswers": 1 }, "answers": [ { "provider": "openai", "promptIndex": 0, "promptText": "best sales forecasting platforms for mid-market teams", "answerText": "For mid-market sales teams, forecasting platforms such as Competitor Inc, Rival Software and Example Analytics combine CRM data with AI-driven projections.", "askedIn": { "locationName": "United States", "languageName": "English" }, "sources": [ { "url": "https://www.g2.com/categories/sales-forecasting", "domain": "g2.com", "title": "Best Sales Forecasting Software" } ], "brands": [ { "rank": 1, "name": "Competitor Inc", "domain": "competitor.com", "description": "Cloud platform for pipeline forecasting aimed at mid-market sales teams.", "rankingRationale": "Listed first because it integrates directly with the major CRMs.", "sentiment": "recommended", "mentionContext": "direct_recommendation", "targetAudience": "Mid-size B2B sales teams", "pricingSignal": "mid_range", "positionConfidence": 0.9, "features": [ "pipeline forecasting", "CRM sync" ], "differentiation": { "axis": "technology", "uniqueValue": "Only platform with native two-way CRM sync" }, "messaging": { "keywords": [ "automation", "mid-market", "self-serve" ], "credibilitySignals": [ "SOC 2 Type II", "used by 400+ teams" ], "differentiationClaims": [ "fastest setup in category" ] } } ] } ], "unansweredQueries": [ { "provider": "gemini", "promptIndex": 2, "promptText": "best sales forecasting platforms for mid-market teams", "reason": "no_usable_answer" } ], "noAnswerShown": [ { "provider": "google_ai_overviews", "promptIndex": 1, "promptText": "best sales forecasting platforms for mid-market teams", "reason": "no_ai_overview_shown" } ], "providerStatus": { "openai": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "claude": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "gemini": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "perplexity": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "google_ai_overviews": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 } }, "answerCoverage": { "queriesSent": 12, "answersCounted": 8 }, "answersTruncated": false } }

Errors

StatusCodeMeaning
400invalid_parameters · invalid_run_id · invalid_check_id · bad_request · paging_requires_compact_view · paging_requires_summary · nothing_to_returnBad request — the payload failed validation.
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.
404project_not_found · no_data_available · not_foundNo project matches the id in the path.

Every error uses the shared { error: { code, message, status } } envelope; code is one of the values listed above.

{ "error": { "code": "invalid_parameters", "message": "Domain is required", "status": 400 } }

Get AI visibility check history

GET/v1/projects/{projectId}/ai-visibility/history

Get paginated history of AI visibility checks for a project. Each entry includes check timing and summary statistics with customer metrics, top competitor, and competitor rankings. The score counts only the top 5 positions in an answer, evenly spaced — first place the most, the last scoring position the least — and nothing below them. It is a reading of WHERE a brand lands when it is named, never of who is ahead: a standing claim — “you lead”, “you trail”, “the leader is X” — rests on how often each brand is named (presence on the market map, or mentionRate within one check) and never on this score, which can favour a brand named half as often. A score of 0 for a brand the answers did name means it was named only below the top 5, or too seldom inside them for the average to register. Read mentionRate beside a 0 score: a non-zero rate means the brand was named, and a 0 rate means no counted answer named it. Only scored checks are listed — under the full-coverage gate a cycle with a query it could not read is never scored and does not appear here; a query the model was read for and had no answer to show (noAnswerShown on that check’s answers) does not count against it. Checks published before that gate remain listed and can have been scored over fewer answers than they asked queries; the queries-sent figure is not returned, so a listed check cannot be shown to be fully covered. Each entry carries summary.totalEntries, which is the size preview for that check’s raw answers via the check-detail route. This page is subject to a response size cap: when truncated is true, whole entries were dropped from the end of items and pagination.hasMore does NOT account for them — lower limit to see the rest rather than paging forward, which would skip them.

Returns · { readingGuide, items, pagination, truncated }

Path parameters

NameTypeDescription
projectIdstringProject ID

Query parameters

NameTypeRequiredDescription
pagenumberPage number (1-indexed). Default 1. Min 1.
limitnumberNumber of items per page. Default 20. Range 1–100.
viewstringHow much of the response to return. compact: each row’s summary.competitorRankings carries its first 10 companies in stored order, then every tracked company and the customer’s own row that fall outside them, with summary.competitorRankingsPage counting the whole list; summary.promptMarket.perPrompt is left out — the dashboard and a check’s detail carry it. Every other field is unchanged. There is no offset: the whole list is view=full, or the check’s detail. full: every row of every list, as stored — large, and meant for export rather than for reading. Omitted, the API’s default view applies: compact on /v1. Values: compact, full.

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/ai-visibility/history?page=1&limit=20&view=compact" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "readingGuide": { "summary.pages": "Pages the engines RETRIEVED while answering, never pages they cited." }, "items": [ { "checkId": "507f1f77bcf86cd799439011", "completedAt": "2026-03-15T10:00:00.000Z", "summary": { "customer": { "domain": "mycompany.com", "mentionRate": 66.7, "mentionCount": 6, "aiScore": 73, "perProvider": { "openai": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "claude": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "gemini": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "perplexity": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "google_ai_overviews": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 } }, "perPrompt": [ { "promptIndex": 0, "promptLabel": "best sales forecasting platforms for mid-market teams", "mentionedBy": [ "openai", "claude" ], "score": 61 } ] }, "topCompetitor": { "domain": "competitor.com", "name": "Competitor Inc", "mentionRate": 88.9 }, "mentionRateGap": -22.2, "totalCompetitorsFound": 8, "totalQueries": 12, "totalEntries": 45, "competitorRankings": [ { "domain": "competitor.com", "name": "Competitor Inc", "isOwn": false, "mentionCount": 7, "mentionRate": 77.8, "aiScore": 68, "isTracked": false } ], "promptMarket": { "state": "rivals_named_in_most_answers", "explanation": { "code": "rivals_found_across_answers", "text": "Your prompts are describing your market well. The AI answers we collected mention companies you already track, so the numbers here are measuring the right competition." }, "answersNamingAnyRival": 34, "answersMatchingCurrentPromptText": 60, "checksAnalysed": 5, "perPrompt": [ { "promptId": "7f3c1a2e-9d44-4c8b-9f0e-2b6a5c9d7e10", "promptTextInProject": "best sales forecasting platforms for mid-market teams", "state": "answers_and_rival_list_disagree", "explanation": {}, "answersNamingAnyRival": 1, "answersMatchingCurrentPromptText": 20 } ] }, "competitorRankingsPage": { "offset": 0, "limit": 10, "total": 96, "hasMore": true } } } ], "pagination": { "page": 1, "limit": 20, "total": 47, "totalPages": 3, "hasMore": true }, "truncated": false }

Paginated — pass page and limit query parameters and follow pagination.hasMore to page through the full set.

Errors

StatusCodeMeaning
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.

Every error uses the shared { error: { code, message, status } } envelope; code is one of the values listed above.

{ "error": { "code": "api_key_invalid", "message": "Invalid API key", "status": 401 } }

Get AI visibility data for a specific check

GET/v1/projects/{projectId}/ai-visibility/history/{checkId}

Get full AI visibility data for a specific historical check: the market map as it stood at this check under summary.marketMap, and each competitor’s mention rate and AI Visibility Score across every AI model we query under summary.competitorRankings — in the order to render, nothing positional. The score counts only the top 5 positions in an answer, evenly spaced — first place the most, the last scoring position the least — and nothing below them. It is a reading of WHERE a brand lands when it is named, never of who is ahead: a standing claim — “you lead”, “you trail”, “the leader is X” — rests on how often each brand is named (presence on the market map, or mentionRate within one check) and never on this score, which can favour a brand named half as often. A score of 0 for a brand the answers did name means it was named only below the top 5, or too seldom inside them for the average to register. These rows name other companies, so reporting a 0 as ‘never named’ is a false claim about a third party published under CompetLab’s name. Read mentionRate beside a 0 score: a non-zero rate means the brand was named, and a 0 rate means no counted answer named it. Rates here divide by the answers that came back, not the queries sent, and the queries-sent figure is not on summary — describe a rate as a share of the answers counted, never as a share of every query asked. Set includeAnswers=true to also get what the models actually said: every prompt sent, and every brand each model named in rank order with its stated reasoning, plus providerStatus and (where the check recorded its ask) answerCoverage. That block is large, and how large depends on the project. An entry is one brand a model named, at about 1,500 characters each — so the block grows with three things at once: how many prompts the project asks — 8 on every plan and in the trial — times five models, how many models answered, and how many companies each answer named. No figure quoted here can stand in for summary.totalEntries; read it and size the fetch from it. Google AI Overviews answers additionally carry the overview text and the pages Google cited, which totalEntries does not predict and which the brand filter keeps by design. Then prefer brand=<domain> (one competitor across every answer) or provider= (one model) over fetching everything; promptIndex= narrows to a single prompt. provider and promptIndex narrow the answers array; brand does not — it reduces the brands list inside each answer, so every answer still comes back and the ones that did not name that domain arrive with an empty brands, which is a finding rather than an absence. No filter changes any number under summary: those are stored, computed over the whole check, and never recomputed for a filtered view. Ranks are stable under filtering. Queries that produced no usable answer are listed separately in unansweredQueries rather than appearing as answers naming nobody, and queries the model was read for and had no answer to show — today, prompts Google showed no AI Overview for — in noAnswerShown, excluded from every count and not a failure; those are three different facts, and an empty brands under a brand filter is none of them. summary.customer.perPrompt breaks the customer’s result down per prompt at no extra cost — use it before reaching for includeAnswers.

Returns · { item }

Path parameters

NameTypeDescription
checkIdstringCheck ID
projectIdstringProject ID

Query parameters

NameTypeRequiredDescription
includeAnswersbooleanSet true to include the models’ raw answers — every prompt sent and every brand each model named, with its stated reasoning. Off by default because the block is large, and how large depends on the project. An entry is one brand a model named, at about 1,500 characters each — so the block grows with three things at once: how many prompts the project asks — 8 on every plan and in the trial — times five models, how many models answered, and how many companies each answer named. No figure quoted here can stand in for summary.totalEntries; read it and size the fetch from it. Google AI Overviews answers additionally carry the overview text and the pages Google cited, which totalEntries does not predict and which the brand= filter keeps by design. The prose it returns is the model’s wording about the brands it named, not CompetLab’s assessment. Default false.
providerstringReturn only this model’s answers. Requires includeAnswers=true. Does not change any number under summary, and does not narrow providerStatus. Values: openai, claude, gemini, perplexity, google_ai_overviews.
brandstringReturn only the entries for this domain, across every answer. Requires includeAnswers=true. Matches brands[].domain, case-insensitively; brand NAMES are the model’s own wording and vary between answers, so they are never matched. EVERY answer is still returned — the ones that did not name this domain come back with an empty brands, because ‘this model answered and did not name them’ is a finding, not an absence of data. A query that produced no answer at all is in unansweredQueries instead and asserts nothing about anyone. Ranks are unaffected: an entry keeps the position it held in the full answer. This is the cheapest way to ask where a competitor wins and where they are invisible: it keeps at most one brand entry per answer instead of every brand the model named, and none at all on the answers that did not name it. Compare summary.totalEntries with the answer count to see the saving on this check. Google AI Overviews answers still carry their overview text and cited pages, which this filter keeps by design.
promptIndexnumberReturn only the answers for this prompt, across every model. Requires includeAnswers=true. Zero-based, matching the per-prompt index used elsewhere in this dimension.
viewstringHow much of the response to return. compact: summary.marketMap.brands is one page of rows — the core by default — with summary.marketMap.brandsPage saying where it sits; the customer’s own row and every tracked competitor’s are always included. Every other field is unchanged, and untrackedCoreBrands and customerStanding are still read from the whole map. full: every row of every list, as stored — large, and meant for export rather than for reading. Omitted, the API’s default view applies: compact on /v1. Values: compact, full.
mapOffsetnumberRows of summary.marketMap.brands to skip, in the order the list is stored. Default 0. Pages the compact view: sent without view it selects the compact view, and beside view=full it is refused with paging_requires_compact_view. Min 0.
mapLimitnumberRows of summary.marketMap.brands to return, 1 to 200. Default the core size (summary.marketMap.coreSize), and never fewer than 10. Pages the compact view: sent without view it selects the compact view, and beside view=full it is refused with paging_requires_compact_view. Range 1–200.
includeSummarybooleanWhether to return summary. Omitted, /v1 returns it unless includeAnswers=true — a read for the answers rarely needs the summary again, and the summary is most of the response — and a param that pages a summary list returns it too. Set it explicitly to override; false beside a paging param is refused with paging_requires_summary.

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/ai-visibility/history/507f1f77bcf86cd799439012?includeAnswers=false&provider=openai&brand=competitor.com&promptIndex=0&view=compact&mapOffset=0&mapLimit=10&includeSummary=true" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "readingGuide": { "summary.pages": "Pages the engines RETRIEVED while answering, never pages they cited." }, "checkId": "507f1f77bcf86cd799439011", "completedAt": "2026-03-15T10:00:00.000Z", "summary": { "customer": { "domain": "mycompany.com", "mentionRate": 66.7, "mentionCount": 6, "aiScore": 73, "perProvider": { "openai": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "claude": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "gemini": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "perplexity": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 }, "google_ai_overviews": { "mentioned": true, "mentionCount": 3, "answersCounted": 3 } }, "perPrompt": [ { "promptIndex": 0, "promptLabel": "best sales forecasting platforms for mid-market teams", "mentionedBy": [ "openai", "claude" ], "score": 61 } ] }, "topCompetitor": { "domain": "competitor.com", "name": "Competitor Inc", "mentionRate": 88.9 }, "mentionRateGap": -22.2, "totalCompetitorsFound": 8, "totalQueries": 12, "totalEntries": 45, "competitorRankings": [ { "domain": "competitor.com", "name": "Competitor Inc", "isOwn": false, "mentionCount": 7, "mentionRate": 77.8, "aiScore": 68, "isTracked": false } ], "promptMarket": { "state": "rivals_named_in_most_answers", "explanation": { "code": "rivals_found_across_answers", "text": "Your prompts are describing your market well. The AI answers we collected mention companies you already track, so the numbers here are measuring the right competition." }, "answersNamingAnyRival": 34, "answersMatchingCurrentPromptText": 60, "checksAnalysed": 5, "perPrompt": [ { "promptId": "7f3c1a2e-9d44-4c8b-9f0e-2b6a5c9d7e10", "promptTextInProject": "best sales forecasting platforms for mid-market teams", "state": "answers_and_rival_list_disagree", "explanation": { "code": "rivals_found_across_answers", "text": "Your prompts are describing your market well. The AI answers we collected mention companies you already track, so the numbers here are measuring the right competition." }, "answersNamingAnyRival": 1, "answersMatchingCurrentPromptText": 20 } ] }, "marketMap": { "checksAnalysed": 5, "answersReceived": 60, "tailIsProvable": true, "coreSize": 9, "perEngine": { "openai": { "answersReceived": 15 }, "claude": { "answersReceived": 15 }, "gemini": { "answersReceived": 15 }, "perplexity": { "answersReceived": 15 }, "google_ai_overviews": { "answersReceived": 15 } }, "profileEngines": [ "openai", "claude", "gemini", "perplexity" ], "brands": [ { "answersNaming": 31, "answersReceived": 60, "presence": 52, "presenceLow": 40, "presenceHigh": 63, "zone": "named_in_a_quarter_or_more_of_answers", "domain": "sienge.com.br", "name": "Sienge", "isOwn": false, "rankByPresence": 2, "perEngine": { "openai": {}, "claude": {}, "gemini": {}, "perplexity": {}, "google_ai_overviews": {} }, "endorsement": { "value": 64, "answersRead": 9, "low": 51, "high": 77 }, "pricePerception": { "value": 64, "answersRead": 9, "low": 51, "high": 77, "tier": "mid_range", "tierAnswers": 6 } } ], "brandsPage": { "offset": 0, "limit": 10, "total": 96, "hasMore": true } } }, "answers": [ { "provider": "openai", "promptIndex": 0, "promptText": "best sales forecasting platforms for mid-market teams", "answerText": "For mid-market sales teams, forecasting platforms such as Competitor Inc, Rival Software and Example Analytics combine CRM data with AI-driven projections.", "askedIn": { "locationName": "United States", "languageName": "English" }, "sources": [ { "url": "https://www.g2.com/categories/sales-forecasting", "domain": "g2.com", "title": "Best Sales Forecasting Software" } ], "brands": [ { "rank": 1, "name": "Competitor Inc", "domain": "competitor.com", "description": "Cloud platform for pipeline forecasting aimed at mid-market sales teams.", "rankingRationale": "Listed first because it integrates directly with the major CRMs.", "sentiment": "recommended", "mentionContext": "direct_recommendation", "targetAudience": "Mid-size B2B sales teams", "pricingSignal": "mid_range", "positionConfidence": 0.9, "features": [ "pipeline forecasting", "CRM sync" ], "differentiation": { "axis": "technology", "uniqueValue": "Only platform with native two-way CRM sync" }, "messaging": { "keywords": [ "automation", "mid-market", "self-serve" ], "credibilitySignals": [ "SOC 2 Type II", "used by 400+ teams" ], "differentiationClaims": [ "fastest setup in category" ] } } ] } ], "unansweredQueries": [ { "provider": "gemini", "promptIndex": 2, "promptText": "best sales forecasting platforms for mid-market teams", "reason": "no_usable_answer" } ], "noAnswerShown": [ { "provider": "google_ai_overviews", "promptIndex": 1, "promptText": "best sales forecasting platforms for mid-market teams", "reason": "no_ai_overview_shown" } ], "providerStatus": { "openai": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "claude": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "gemini": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "perplexity": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 }, "google_ai_overviews": { "reported": true, "completedAt": "2026-03-15T10:00:00.000Z", "answersCounted": 3, "noAnswerShown": 0, "brandsNamed": 21 } }, "answerCoverage": { "queriesSent": 12, "answersCounted": 8 }, "answersTruncated": false } }

Errors

StatusCodeMeaning
400invalid_parameters · invalid_run_id · invalid_check_id · bad_request · paging_requires_compact_view · paging_requires_summary · nothing_to_returnBad request — the payload failed validation.
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.
404check_not_foundNo check matches the id in the path.

Every error uses the shared { error: { code, message, status } } envelope; code is one of the values listed above.

{ "error": { "code": "invalid_parameters", "message": "Domain is required", "status": 400 } }

Get how the market the AI models draw has moved

GET/v1/projects/{projectId}/ai-visibility/trend

How the market the AI models draw has moved over a window: one row per company with how often it was recommended at the start of the window and now — a share of the answers pooled in each check’s window, with a 95% range and a zone — plus its rank by that share and its AI Visibility Score at both ends, the difference between the ends in each unit, and the models backing it. The project’s own company and every tracked competitor are always rows, however many there are, plus up to 3 companies the project does not track (the most recommended of the rest with a reading on the scope), all ordered by how often each is recommended on the latest map (ties are ties, so never break one). Each reading carries checksAnalysed, the checks its map pools: now is never the latest check alone. Two companies whose ranges overlap are not in a settled order whatever their shares say. The score counts only the top 5 positions in an answer, evenly spaced — first place the most, the last scoring position the least — and nothing below them. It is a reading of WHERE a brand lands when it is named, never of who is ahead: a standing claim — “you lead”, “you trail”, “the leader is X” — rests on how often each brand is named (presence on the market map, or mentionRate within one check) and never on this score, which can favour a brand named half as often. A score of 0 for a brand the answers did name means it was named only below the top 5, or too seldom inside them for the average to register. events carries what happened on the axis: the customer’s own standing changing zone and holding (the alerts the customer received — a standing is announced only once it has held for two checks), the cycles that produced no reading, and when the prompts were last edited (readings before that date answer different questions). provider reads one model’s own slice of every map; rank and score are then absent, since they exist only across every model. detail=series adds each company’s share check by check, downsampled to at most twelve points; omit it unless the shape between the ends matters. The window reads at most the newest 200 published checks — use dateFrom/dateTo for longer histories. Every figure here is a stored map’s own: a company a map does not carry is a measured zero of that map’s answers, and a model with no usable answer in a window is null, never zero.

Returns · { item }

Path parameters

NameTypeDescription
projectIdstringProject ID

Query parameters

NameTypeRequiredDescription
dateFromstringStart of the window (ISO-8601). Omit for the whole history: the newest 200 published checks, and the newest 200 cycles that produced no reading — on a longer history set both dates so the two cover one span.
dateTostringEnd of the window (ISO-8601).
providerstringRead one AI model’s own slice of every map instead of the pooled map. Omit for every model at once. Under one model rank and score are null on every reading and enginesBacking is left off the rows — they exist only across every model; never read that as ‘no model named them’. Values: openai, claude, gemini, perplexity, google_ai_overviews.
detailstringseries adds each company’s share check by check, downsampled to at most 12 points spread evenly over the window. Omit it unless the shape between the two ends matters: the rows already carry the reading now, the reading at the start and the difference. Values: series.

Request

curl "https://api.competlab.com/v1/projects/507f1f77bcf86cd799439011/ai-visibility/trend?dateFrom=2026-01-01&dateTo=2026-03-15&provider=openai&detail=series" \ -H "CL-API-Key: YOUR_COMPETLAB_API_KEY"

Response 200 OK

{ "item": { "window": { "from": "2026-08-12T09:30:00.000Z", "to": "2026-08-30T09:30:00.000Z", "checks": 16, "answersReceived": 60, "checksAnalysed": 5, "providersAsked": [ "openai", "claude", "gemini", "perplexity", "google_ai_overviews" ] }, "scope": "all", "companies": [ { "name": "Acme", "domain": "acme.com", "isOwn": false, "isTracked": true, "enginesBacking": [ "openai", "claude" ], "now": { "date": "2026-08-30T09:30:00.000Z", "checkId": "507f1f77bcf86cd799439012", "checksAnalysed": 5, "presence": { "answersNaming": 24, "answersReceived": 60, "presence": 40, "presenceLow": 28, "presenceHigh": 53, "zone": "named_in_a_quarter_or_more_of_answers" }, "rank": 3, "score": 41 }, "start": { "date": "2026-08-30T09:30:00.000Z", "checkId": "507f1f77bcf86cd799439012", "checksAnalysed": 5, "presence": { "answersNaming": 24, "answersReceived": 60, "presence": 40, "presenceLow": 28, "presenceHigh": 53, "zone": "named_in_a_quarter_or_more_of_answers" }, "rank": 3, "score": 41 }, "presenceChange": 12, "presenceChangeSeparable": true, "rankChange": 2, "scoreChange": 9, "series": [ { "date": "2026-08-20T09:30:00.000Z", "checksAnalysed": 5, "presence": 27, "presenceLow": 17, "presenceHigh": 40 } ] } ], "events": { "standingChanges": [ { "date": "2026-08-26T09:30:00.000Z", "from": "share_not_yet_separable", "to": "named_in_a_quarter_or_more_of_answers" } ], "incompleteCycles": [ { "date": "2026-03-14T02:00:00.000Z", "reason": "incomplete_coverage", "measuredAnswers": 8, "expectedAnswers": 12, "absentAnswers": 1 } ], "promptsLastChangedAt": "2026-08-16T14:00:00.000Z" } } }

Errors

StatusCodeMeaning
401api_key_missing · api_key_invalid · api_key_revoked · api_key_expired · insufficient_scopeThe CL-API-Key is missing, malformed, revoked, expired, or lacks the required scope.

Every error uses the shared { error: { code, message, status } } envelope; code is one of the values listed above.

{ "error": { "code": "api_key_invalid", "message": "Invalid API key", "status": 401 } }
Last updated on