API reference
Read-only REST API for Peak Answer. JSON over HTTPS, bearer key, 4 endpoints.
The API returns what Peak Answer measures for one brand: your visibility in AI answers, how each tracked question is doing, and what Google shows for them. It only reads. To change what is tracked, use the app or the MCP server.
It is described as OpenAPI 3.1, so you can load it into Postman, Insomnia or any client generator.
Base URL
https://peakanswer.com/api/v1Authentication
Create a key in Settings, Account, API key and MCP. Send it as a bearer token on every request.
Authorization: Bearer asq_your_key_hereA key belongs to one brand and reads only that brand. The key is shown once, when you create it. Replacing it ends the old one straight away. Requests fail with 401 if the key is wrong or the plan is no longer active.
Keep the key on your server. The API does not allow browser requests from other sites, so a key placed in front-end code would be readable by anyone who loads the page.
Rate limits
Each endpoint allows 60 requests an hour per brand. All your keys and servers share that allowance. Past it, the API answers 429 with a Retry-After header giving the seconds to wait. Measurements are updated on a schedule rather than live, so there is little to gain from polling more often than every few minutes.
Errors
Errors use the status code that fits and a JSON body with one field, error, holding a sentence you can show to a person.
{ "error": "Missing or invalid API key. Create one in Settings and send it as a bearer token." }| Status | Meaning |
|---|---|
| 400 | The question id is empty or too long. |
| 401 | The key is missing, wrong, or its plan is no longer active. |
| 404 | No tracked question with that id belongs to the brand. |
| 429 | Too many requests. Wait the number of seconds in Retry-After. |
| 500 | The data could not be loaded. Try again. |
Every response carries Cache-Control: no-store, so nothing is kept by proxies on the way.
Endpoints
| Request | Returns |
|---|---|
GET /api/v1/visibility | Get everything measured for the brand |
GET /api/v1/questions/suggestions | Get questions worth tracking |
GET /api/v1/questions/{id}/google | Get what Google shows for a question |
GET /api/v1/questions/{id}/google-page | Get the brand's page that ranks for a question |
GET /api/v1/visibility
The visibility index and its history, how each engine does, every tracked question, strategy, framing, answers lost and the pages engines quote, in one document. Poll it on a schedule rather than calling it per question.
Example
curl https://peakanswer.com/api/v1/visibility \
-H "Authorization: Bearer $PEAK_ANSWER_KEY"Response fields
| Field | Type | Description |
|---|---|---|
brand | object | The brand the key belongs to. |
brand.name | string | The brand name. |
brand.domain | string | The brand's website domain. |
visibility | object or null | The latest visibility index. Null until the first measurement has finished. |
visibility.index | number | The visibility index from 0 to 100, smoothed over recent days. |
visibility.day | string (date) | The day the index is for. |
visibility.confidenceBand | number | How far the index could be off in either direction, in points. |
visibility.byEngine | object | How often each engine names the brand, as a percentage of its answers. The keys are engine ids. An engine that is missing was not measured, which is different from measured at zero. |
visibility.brandedPresence | number or null | How often the brand is named on questions that already contain its name. Reported on its own and never part of the index. |
visibility.categoryRank | integer or null | The brand's rank among the competitors tracked for it. |
visibility.categorySize | integer or null | How many brands that rank is out of. |
footprint | object or null | How established the brand is on the wider web. Null until measured. |
footprint.score | integer | The overall score from 0 to 100. |
footprint.publicScore | integer | The part of the score that can be compared with another company's, from 0 to 100. |
footprint.coverage | number | The share of the score's factors that had data, from 0 to 1. A low number means the score rests on little. |
footprint.factors | object | The individual factors behind the score. |
footprint.day | string (date) | The day the score is for. |
history | array of objects | Up to 90 days of the index, newest first. |
history[].day | string (date) | The day. |
history[].index | number | The smoothed index for that day. |
history[].observed | number | The unsmoothed reading for that day. |
strategy | object | How the tracked questions split by what it takes to win them. |
strategy.winnableWithContent | integer | Questions that new content can win. |
strategy.needsAuthority | integer | Questions that need mentions on other sites first. |
strategy.inconsistent | integer | Questions where the answer changes too much to say. |
strategy.losingWithContent | integer | Questions that content can win and the brand is not yet named on. |
strategy.losingWithAuthority | integer | Questions that need authority and the brand is not yet named on. |
questions | array of objects | Every tracked question with how it is doing. |
questions[].id | string | The question's id. Use it with the per-question endpoints. |
questions[].text | string | The question as it is asked of the engines. |
questions[].presence | number | How often the brand is named across all engines, from 0 to 100. |
questions[].volatility | number | How much the answer changes from run to run, from 0 to 100. High means the position is open. |
questions[].winnability | string | What it takes to win this question. One of content, authority, mixed. |
questions[].namesYou | boolean | True when the question itself contains the brand's name. Those questions are measured but left out of the index. |
questions[].byEngine | array of objects | |
questions[].byEngine[].engine | string | The engine id. One of chatgpt, claude, perplexity, gemini, google_serp, google_ai_mode, copilot. |
questions[].byEngine[].label | string | The engine's display name. |
questions[].byEngine[].presence | number | How often this engine names the brand on this question, from 0 to 100. |
questions[].byEngine[].runs | integer | How many answers that is out of. |
questions[].winners | array of string | Brands named when this brand is not. |
questions[].runs | integer | Total answers recorded for the question. |
framing | array of objects | Phrases the engines use about the brand. |
framing[].phrase | string | The phrase. |
framing[].sentiment | string | How the phrase reads. One of positive, neutral, negative. |
framing[].count | integer | How many answers used it. |
framing[].example | string | One sentence it appeared in. |
regressions | array of objects | Answers the brand used to be named in and no longer is. Up to 20. |
regressions[].question | string | The question. |
regressions[].engine | string | The engine id. One of chatgpt, claude, perplexity, gemini, google_serp, google_ai_mode, copilot. |
regressions[].scoreBefore | integer | The score when the brand was named. |
regressions[].scoreAfter | integer | The score now. |
regressions[].takenBy | string or null | The brand named in its place. |
regressions[].detectedAt | string (date-time) | When the drop was found. |
quotedPages | array of objects | The brand's own pages the engines quote. Up to 20. |
quotedPages[].url | string | The page's address. |
quotedPages[].title | string or null | The page's title. |
quotedPages[].answersSeen | integer | How many recorded answers quoted it. |
generatedAt | string (date-time) | When this document was built. |
GET /api/v1/questions/suggestions
Questions people ask on Google that fit the brand and are not tracked yet.
Example
curl https://peakanswer.com/api/v1/questions/suggestions \
-H "Authorization: Bearer $PEAK_ANSWER_KEY"Response fields
| Field | Type | Description |
|---|---|---|
suggestions | array of objects | |
suggestions[].id | string | The suggestion's id. |
suggestions[].question | string | The question. |
suggestions[].searchDemand | string | How often people ask it, compared with the brand's other questions. One of High, Medium, Low, Not enough data. |
refreshedAt | string (date-time) or null | When the list was last built. |
status | string | pending means the first list is still being built. stale means it is older than a month. One of pending, ready, stale. |
GET /api/v1/questions/{id}/google
The first 10 Google results, whether an AI Overview was shown and which pages it cites, where the brand ranks, and how many tracked competitors are on page one. Read from the last stored check; the request does not run a new search.
Path parameters
| Name | Description |
|---|---|
id | A tracked question's id, as returned in `questions[].id` by /visibility. |
Example
curl https://peakanswer.com/api/v1/questions/QUESTION_ID/google \
-H "Authorization: Bearer $PEAK_ANSWER_KEY"Response fields
| Field | Type | Description |
|---|---|---|
questionId | string | The question's id. |
google | object or null | Null when the question has not been checked on Google yet, or the last check is out of date. It is a reading, not an error. |
google.searchPhrase | string | The search that was checked. |
google.checkedAt | string (date-time) | When it was checked. |
google.searchDepth | integer | How many results were read. Always 100. One of 100. |
google.topSites | array of objects | The first 10 results. |
google.topSites[].position | integer | Place in the results, 1 to 10. |
google.topSites[].url | string | The result's address. |
google.topSites[].domain | string | Its domain. |
google.aiAnswerShown | boolean | Whether Google showed an AI Overview for the search. |
google.brandPosition | integer or null | The brand's best place in the first 100 results. Null when it is not there. |
google.competitorsOnPageOne | integer | How many tracked competitors are in the first 10 results. |
google.aiAnswer | object or null | What the AI Overview said, when one was shown. Absent on checks made before this was recorded. |
google.aiAnswer.citedPages | array of objects | The pages the AI Overview cites. Up to 30. |
google.aiAnswer.citedPages[].url | string | The page's address. |
google.aiAnswer.citedPages[].domain | string | Its domain. |
google.aiAnswer.citedPages[].title | string | Its title. |
google.aiAnswer.brandsNamed | array of objects | The brand and tracked competitors named in the AI Overview's text. |
google.aiAnswer.brandsNamed[].name | string | The brand's name. |
google.aiAnswer.brandsNamed[].isSelf | boolean | True for the brand the key belongs to. |
GET /api/v1/questions/{id}/google-page
Which page of the brand's site Google ranks for the question's search, and where.
Path parameters
| Name | Description |
|---|---|
id | A tracked question's id, as returned in `questions[].id` by /visibility. |
Example
curl https://peakanswer.com/api/v1/questions/QUESTION_ID/google-page \
-H "Authorization: Bearer $PEAK_ANSWER_KEY"Response fields
| Field | Type | Description |
|---|---|---|
questionId | string | The question's id. |
page | object or null | Null when no page of the brand's site was found for the search, or the check is out of date. |
page.url | string | The page's address. |
page.position | integer | Its place in Google's results. |
page.searchPhrase | string | The search it ranks for. |
page.checkedAt | string (date-time) | When it was checked. |
Using it with an assistant
To ask Claude or ChatGPT about the same data without writing any code, connect the MCP server. It also lets an assistant change what is tracked.