API & MCP server
Everything OptimizeCamp measures about your AI visibility is available outside the app — same numbers, same access rules. There are two doors in: point your AI assistant at the MCP server and just ask, or build on the REST API.
For marketers
Connect your AI assistant
claude.ai, ChatGPT, Claude Code — ask about your visibility in plain language. Two-minute setup, no code.
For developers & agencies
Build on the REST API
Pull scores, prompts, citations and gaps into dashboards, reports, and pipelines with an API key.
Availability & quotas
| Plan | API + MCP | Requests / day |
|---|---|---|
| Starter | Not included | — |
| Pro | Included | 5,000 |
| Agency | Included | 20,000 |
Quotas are per workspace per UTC day, shared across REST and MCP. Reads never cost anything beyond quota; the one paid action is triggering an on-demand run, which uses your plan’s on-demand run credits (1 credit covers up to 50 prompts) — exactly like the in-app Run now button. Everything you read reflects the two answer engines OptimizeCamp tracks today, ChatGPT and Google AI Overviews — see Answer engines we track.
Create an API key
Open Settings → API access
On any project’s settings page. Keys belong to the workspace, not the project — one key reads every project you track. Up to 5 active keys.
Name it — and decide if it can trigger runs
Keys are read-only unless you check “Allow this key to trigger tracking runs”. Leave that off for anything that only needs to read (dashboards, most assistant setups).
Copy the secret
It looks like oc_live_… and is shown once — OptimizeCamp stores only a hash. Revoking a key in the same screen cuts it off immediately.
Connect your AI assistant (MCP)
The MCP endpoint speaks Streamable HTTP:
https://optimizecamp.com/api/mcpclaude.ai / ChatGPT — no key needed
Add a custom connector
In your assistant’s connector settings, add the endpoint URL above as a custom connector.
Sign in and approve
You’re sent to OptimizeCamp to sign in (if you aren’t already) and shown exactly what the connection can see before you approve it.
Ask
That’s it — the assistant can now answer from your workspace’s real tracked data.
Assistant connections are read-only
Claude Code, Cursor & other clients — with a key
claude mcp add --transport http optimizecamp \
https://optimizecamp.com/api/mcp \
--header "Authorization: Bearer oc_live_…"Any MCP client that can send an Authorization header works the same way.
Things worth asking
- “How’s my AI visibility this week — what moved and why?”
- “Which prompts am I losing, and who’s winning them?”
- “Which sources do AI answers cite for my category — am I on them?”
- “Pick my top content gap and draft an outline that would close it.”
| Tool | The question it answers |
|---|---|
list_projects | Which brands/sites does this workspace track? |
get_visibility_overview | How are we doing in AI search right now? |
get_visibility_trend | How has our visibility changed over time? |
get_prompt_performance | Which prompts are we winning — and losing? |
get_share_of_voice | How do we compare to competitors? |
get_citations | Where do AI answers get their information? |
get_earned_sources | Which third-party pages already cite us? |
get_content_gaps | What should we fix or write next? |
trigger_visibility_run | Measure again, right now. |
REST API
Base URL https://optimizecamp.com/api/v1, authenticated with a bearer key. Project ids are UUIDs, but every {id} also accepts the project’s domain.
curl -H "Authorization: Bearer oc_live_…" \
https://optimizecamp.com/api/v1/projects/acme.com{
"project": { "id": "…", "name": "Acme", "domain": "acme.com", … },
"overview": {
"measuredAt": "2026-07-20",
"visibilityScore": 62.4,
"mentionRate": 71.4,
"citationRate": 42.9,
"positionScore": 58.3,
"sentimentIndex": 64.0,
"shareOfVoice": 38.2,
"deltas": { "visibilityScore": 4.1, "mentionRate": 7.2, … },
"deltaBasis": "week",
"platforms": { "chatgpt": 66.1, "google_ai_overview": 58.7 },
"lockedHistory": false
}
}| Method | Path | Returns |
|---|---|---|
GET | /projects | The workspace’s tracked brands/sites. |
GET | /projects/{id} | Project facts + the latest overview (scores, deltas, per-platform). |
GET | /projects/{id}/trend?months=N | Daily Visibility Score series with the top-competitor overlay. |
GET | /projects/{id}/prompts | Per-prompt results over the last 30 measurement days. |
GET | /projects/{id}/share-of-voice | Brand vs competitors on the latest measured day. |
GET | /projects/{id}/citations | Cited domains, ranked, with brand/competitor ownership flags. |
GET | /projects/{id}/earned-sources | Third-party pages already earning you citations. |
GET | /projects/{id}/content-gaps | Ranked content opportunities with opportunity scores. |
GET | /projects/{id}/visibility.csv | The daily rollup as CSV — identical to the in-app export. |
GET | /projects/{id}/runs | The latest tracking run’s status. |
POST | /projects/{id}/runs | Starts an on-demand run (runs permission; uses run credits). |
Timeline endpoints honor your plan’s history-access window, the same as the app.
Triggering runs
POST /projects/{id}/runs starts a fresh measurement through the exact same path as the in-app button — same credit hold, same one-run-at-a-time guard. It needs a key with the runs permission, answers 202 with the new run, and you poll GET /projects/{id}/runs until the status is completed, partial or failed. A second POST while a run is live answers 409. Workspaces with an inactive subscription keep read access but can’t start runs.
Errors
Every failure is { "error": { "code", "message" } } with a matching HTTP status:
| Code | HTTP | Meaning |
|---|---|---|
unauthorized | 401 | Missing, unknown, or revoked credential. |
insufficient_scope | 403 | The key lacks the required permission. |
plan_gated | 403 | The plan doesn’t include API access — or the workspace is read-only, for writes. |
not_found | 404 | No such project in this workspace. |
invalid_request | 400 | A parameter didn’t validate. |
insufficient_credits | 402 | Out of on-demand run credits this period. |
conflict | 409 | A run is already in progress. |
rate_limited | 429 | Daily quota reached; Retry-After says when it resets (midnight UTC). |
unavailable | 503 | Runs temporarily paused — retry shortly. |