Frontmost AI API
Use this API to read how AI apps answer your buyers' questions about your business, and to change what Frontmost AI tracks. It gives a coding agent the same numbers as the Frontmost AI app and the MCP server.
- Base URL:
https://app.frontmost.ai/api/v1 - OpenAPI 3.1 file: https://app.frontmost.ai/api/v1/openapi.json
- This page as Markdown: https://app.frontmost.ai/docs/api.md
Get a key
- Open https://app.frontmost.ai/team and go to API Keys.
- Name the key and choose what it can do. A Read Only key calls every GET route. A Read and Write key calls every route.
- Copy the key when it appears. Frontmost AI shows it once.
Every plan includes API keys. Every member of a workspace can create keys, see every key, and revoke any key. A revoked key stops working on the next request.
Authenticate
Send the key in the Authorization header of every request:
curl https://app.frontmost.ai/api/v1/workspace \
-H "Authorization: Bearer $FRONTMOST_API_KEY"
Keep the key in an environment variable such as FRONTMOST_API_KEY. Do not put it in code that you commit.
First call
Call GET /workspace first. It returns the plan, its limits, and what is in use. Then call GET /projects to find project IDs.
curl https://app.frontmost.ai/api/v1/projects \
-H "Authorization: Bearer $FRONTMOST_API_KEY"
Routes
| Method and path | Key | What it does |
|---|---|---|
GET /workspace | Read Only | The plan, its limits, and use. profiles.max_per_project is the most buyer profiles one project holds. |
GET /projects | Read Only | List projects. q filters by name or domain. |
GET /projects/{id} | Read Only | One project |
GET /projects/{id}/scans | Read Only | Delivered scans, newest first. delivered_after takes a date. |
GET /projects/{id}/metrics | Read Only | Visibility, Win Rate, Position, and Sentiment for one scope. group_by is app or scan. |
GET /projects/{id}/rivals | Read Only | Rivals that the answers named, most named first |
GET /projects/{id}/answers | Read Only | Answers in scope, without their text. Filters: question_id, rival_id, named, cited_domain. |
GET /answers/{id} | Read Only | One answer with its text and citations |
GET /projects/{id}/sources | Read Only | Sites that the answers cited, most cited first. Each site has a channel, such as editorial, reddit, or own_website. domain filters to one site, and channel filters to one channel. |
GET /projects/{id}/questions | Read Only | The project's questions. active filters. |
GET /projects/{id}/recommendations | Read Only | Recommendations, open first. status is open, done, or dismissed. |
GET /projects/{id}/profiles | Read Only | The project's buyer profiles, with each name and who |
POST /projects | Read and Write | Create a project from a website, or continue a setup |
PATCH /projects/{id} | Read and Write | Rename a project (name), or start or stop its scans (scanning) |
POST /projects/{id}/questions | Read and Write | Add 1 to 25 questions: {"questions": [{"text": "…", "topic": "…"}]} |
PATCH /projects/{id}/questions/{question_id} | Read and Write | Reword (text), move (topic), pause, or resume (active) a question. A paused question keeps its history and does not count toward the plan's questions. No route deletes a question. |
POST /projects/{id}/profiles | Read and Write | Add a buyer profile: name and an optional who |
PATCH /profiles/{id} | Read and Write | Rename or describe a buyer profile |
DELETE /profiles/{id} | Read and Write | Remove a buyer profile. Its questions stay. |
PATCH /recommendations/{id} | Read and Write | Set status to done, dismissed, or open |
The OpenAPI file has every parameter and response field.
Create a project
A new project starts from a website. Frontmost AI reads the site, then writes the first questions.
- Send
POST /projectswithdomainand anIdempotency-Keyheader. Use a new random key, for example a UUID, for each new project. - A ready project returns 201 with the
projectobject. - A setup that needs more time returns 202 with a
project_setupobject and aRetry-After: 30header. Itsnextfield holds the exact next call. - Wait
Retry-Afterseconds. SendPOST /projectswith{"setup_id": "…"}and noIdempotency-Key. Repeat steps 2 to 4.
A repeated call never makes a second project. A site that Frontmost AI cannot read returns 422 site_unreadable.
curl -X POST https://app.frontmost.ai/api/v1/projects \
-H "Authorization: Bearer $FRONTMOST_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'
The same Idempotency-Key with the same body returns the first reply with Idempotent-Replayed: true. The same key with another body returns 409 idempotency_conflict.
Frontmost AI keeps each key for 24 hours.
Scope
The metrics, rivals, answers, and sources routes take the same scope parameters:
| Parameter | Values | Default |
|---|---|---|
scan_id | a delivered scan of the project | the newest delivered scan |
period | scan, week, month | scan |
app | one AI app of the project | every app of the project |
questions | all, branded, unbranded | all |
compare_to | previous, none | previous |
- A
weekormonthis the calendar week or month, in US Eastern time, that holds the anchor scan. It does not end atscan_id. previousis the same kind of period that ends at the last delivered scan before this one starts.- Each rate comes from pooled counts:
{"value", "numerator", "denominator", "previous"}. Report the counts with the rate, for example "22% · 8 of 36 answers". - Every response states its scope: the anchor scan, the dates, the scans counted, the apps, the question set, and the comparison.
scope.definitions_versionchanges when a counting rule changes.- A project with no delivered scan returns
"scope": nulland empty lists.
Lists
A list returns {"object": "list", "data": [...], "has_more": true, "next_cursor": "…"}.
limit is 1 to 100, and the default is 25. To get the next page, send the same request with cursor set to next_cursor.
A cursor works only with the list and parameters that made it.
Errors
Every error has this shape. The message says what to do next. Quote request_id when you report a problem.
{
"error": {
"code": "invalid_param",
"message": "period must be scan, week, or month.",
"param": "period",
"request_id": "req_…"
}
}
| Status | Code | What to do |
|---|---|---|
| 401 | invalid_key | The key is missing or unknown. Ask the person for a key from the Team page. |
| 401 | key_revoked | The key was revoked. Ask the person for a new key. |
| 403 | plan_no_api | The workspace has no active plan. The person chooses a plan on the Billing page. |
| 403 | needs_plan | The plan is not running, so changes fail. Reads still work. |
| 403 | key_read_only | A Read Only key called a change route. Ask for a Read and Write key. |
| 404 | not_found | Nothing with this ID in this workspace, or no such route. |
| 409 | idempotency_conflict | This Idempotency-Key was used with another body. Use a new key. |
| 409 | request_in_progress | A request with this Idempotency-Key is still running. Wait Retry-After seconds. |
| 422 | invalid_param | A parameter is not valid. param names it. |
| 422 | invalid_cursor | The cursor belongs to another list. Start the list again. |
| 422 | question_limit | The change goes over the plan's questions. details has used, max, and adding. Pause a question to make room. |
| 422 | project_limit | The plan's projects are all in use. details has used and max. |
| 422 | profile_limit | The project has the most profiles it can hold. details has max. Remove a profile first. |
| 422 | site_unreadable | Frontmost AI could not read the website. Check the domain. |
| 429 | rate_limited | Too many requests. Wait Retry-After seconds, then retry. |
| 500 | internal_error | Retry with backoff. Quote request_id if it keeps failing. |
A route that exists with another method returns 404, and the message names the methods it takes.
Rate limits
Each key has one budget for reads and one for changes. A browser sign-in has the same two budgets for each person. A month scope costs more than a scan or a week. Read the headers on every response. Do not hard-code the numbers, because they can change.
HTTP/2 429
Retry-After: 3
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 3
On a 429, wait Retry-After seconds plus a short random delay, then send the request again.
Compatibility
- New fields can appear in any response. Ignore fields that you do not know.
- Lists of values, such as AI apps and codes, can grow. Treat an unknown value as "other".
- IDs are strings. They match the IDs in app URLs, for example the
16in/projects/16. - A change that removes or renames a field goes to
/v2.
Command line
The Frontmost AI CLI calls this API from a terminal. Each route on this page is a command.
brew install frontmost-ai/tap/frontmost
frontmost login
frontmost metrics --period week
Without Homebrew, run npm install -g github:frontmost-ai/cli. It needs Node.js 22.12 or later.
Add --json to any command to get the API response, unchanged. Source: https://github.com/frontmost-ai/cli
Use with a coding agent
Give the agent the key and this page:
export FRONTMOST_API_KEY=fmk_…
# Docs: https://app.frontmost.ai/docs/api.md
To connect an AI app such as Claude or ChatGPT instead, use the MCP server: https://frontmost.ai/connect