# Scope REST API documentation

> HTTP mirror of the Scope tool kernel. Same API keys, scopes, and check ledger as MCP. Reads are GET. Actions are POST.

Canonical page: https://scope.online/docs/api
MCP docs: https://scope.online/docs (markdown: https://scope.online/docs.md)
OpenAPI: https://scope.online/v1/openapi.json

## Overview

MCP: https://scope.online/mcp
REST: https://scope.online/v1
31 tools. Agent and API access is Growth-only. A `tier_locked` error means relay `upgrade_url` and stop.

## Quickstart

- Base URL: https://scope.online/v1
- Reads: `GET /v1/{tool}` with query parameters
- Actions: `POST /v1/{tool}` with a JSON body
- Authorization: `Bearer <SCOPE_API_KEY>`
- First call: `GET /v1/whoami`

```bash
curl -H "Authorization: Bearer $SCOPE_API_KEY" \
  "https://scope.online/v1/whoami"
```

## Authentication

API key: `Authorization: Bearer sk_scope_…`. Create one at https://app.scope.online/settings/api-keys. Shown once. Do not put a key in git, logs, URLs, or a shared chat.
OAuth issuer: https://scope.online/oauth.

## Scopes

| scope | allows | note |
|---|---|---|
| `read` | Visibility, citations, competitors, and recommendations. | Always granted |
| `scan:run` | Run scans and test prompts that draw on the check pool. | The only scope that spends |
| `prompts:write` | Start or stop monitoring prompts. | Changes committed burn |
| `business:create` | Add new sites to this account. | Requires an all-businesses key |
| `business:write` | Generate drafts and mark recommendations done. | Never publishes to a site |

## Connect

### Cursor

Drop Scope into .cursor/mcp.json and let Cursor pull visibility scores, citations, and draft schema fixes while you ship. One MCP endpoint, restricted API key.

```json
{
  "mcpServers": {
    "scope": {
      "url": "https://scope.online/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_SCOPE_API_KEY"
      }
    }
  }
}
```

Walkthrough: https://scope.online/agents/cursor

### Claude

Add the Scope MCP server to Claude to monitor AI visibility, run readiness checks, and draft GEO fixes — without leaving chat. Complements your existing SEO program.

```
https://scope.online/mcp
```

Walkthrough: https://scope.online/agents/claude

### Any MCP client

Any client that speaks Model Context Protocol works with Scope. The raw contract:

```
Endpoint   https://scope.online/mcp   (Streamable HTTP)
Auth       Authorization: Bearer YOUR_SCOPE_API_KEY
           (new keys: all businesses + all grantable permissions)
Tools      31 · reads free · operate/test/setup echoes the receipt after
```

## Tools

### See

| method | path | cost | scope |
|---|---|---|---|
| GET | `/v1/whoami` | Free | `read` |
| GET | `/v1/get_readiness_score` | Free | `read` |
| GET | `/v1/list_businesses` | Free | `read` |
| GET | `/v1/list_tracked_prompts` | Free | `read` |
| GET | `/v1/get_engine_status` | Free | `read` |
| GET | `/v1/get_visibility_report` | Free | `read` |
| GET | `/v1/get_ai_traffic` | Free | `read` |
| GET | `/v1/get_google_connect_url` | Free | `read` |
| GET | `/v1/get_scan` | Free | `read` |
| GET | `/v1/get_scan_diff` | Free | `read` |
| GET | `/v1/get_prompt_results` | Free | `read` |
| GET | `/v1/get_citations` | Free | `read` |
| GET | `/v1/get_citation_briefs` | Free | `read` |
| GET | `/v1/get_roundup_watchlist` | Free | `read` |
| GET | `/v1/get_money_page_gap` | Free | `read` |
| GET | `/v1/get_crawler_visits` | Free | `read` |
| GET | `/v1/get_competitors` | Free | `read` |
| GET | `/v1/get_recommendations` | Free | `read` |
| GET | `/v1/diff_prompt` | Free | `read` |

### Do

| method | path | cost | scope |
|---|---|---|---|
| POST | `/v1/update_business` | Free | `business:write` |
| POST | `/v1/set_primary_business` | Free | `business:write` |
| POST | `/v1/pin_competitors` | Free | `business:write` |
| POST | `/v1/add_business` | Free | `business:create` |
| POST | `/v1/run_scan` | 1 check per tracked prompt | `scan:run` |
| POST | `/v1/check_prompt` | Exactly 1 check | `scan:run` |
| POST | `/v1/track_prompt` | Free now — adds committed burn | `prompts:write` |
| POST | `/v1/untrack_prompt` | Free | `prompts:write` |
| POST | `/v1/update_check_interval` | Free | `prompts:write` |
| POST | `/v1/update_roundup_status` | Free | `business:write` |

### Fix

| method | path | cost | scope |
|---|---|---|---|
| POST | `/v1/generate_fix` | Free | `business:write` |
| POST | `/v1/complete_recommendation` | Free | `business:write` |


## Tool schemas

### `whoami`

GET `/v1/whoami` · Free · `read` · read-only

Returns the authenticated Scope plan. Default view=compact is plan, remaining, committed burn, this_business, weekly_brief, and next[]. Pass view=full for upgrade_policy, skip_engines_down, recent_spend, and rate limits. Use when the user asks what plan they are on, how many checks they have left, or whether the agent is connected.

When to use: when the user asks what plan they are on, how many checks they have left, or whether the agent is connected.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | no | — | Optional business to label the weekly brief for |
| `view` | compact | full | no | compact | compact is the default card; full returns the kitchen-sink payload. |

### `get_readiness_score`

GET `/v1/get_readiness_score` · Free · `read` · read-only

Scores any website for AI-readiness (schema, entity, FAQ, crawlability). A limited anonymous check works with no login. Use when the user pastes a URL and asks if the site is ready for AI search.

When to use: when the user pastes a URL and asks if the site is ready for AI search.

| name | type | required | default | description |
|---|---|---|---|---|
| `url` | string (uri) | yes | — | Website URL |

### `list_businesses`

GET `/v1/list_businesses` · Free · `read` · read-only

Lists every business on the account with the latest visibility snapshot and scheduled check burn. Use when the user asks which sites or brands are in Scope, or before choosing a business_id.

When to use: when the user asks which sites or brands are in Scope, or before choosing a business_id.

No arguments.

### `list_tracked_prompts`

GET `/v1/list_tracked_prompts` · Free · `read` · read-only

Lists every active tracked prompt for a business with id, text (user_text, not stuffed compiled_text), cadence, and burn_per_prompt so you can audit committed burn.

When to use: when burn jumped and you need the full set, or before untrack_prompt.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |

### `get_engine_status`

GET `/v1/get_engine_status` · Free · `read` · read-only

Returns whether ChatGPT, Claude, Gemini, and Perplexity are green, degraded, down, unknown, or stale. Down only after a recent failed attempt or live probe. Skipped engines are unknown, not down. grok is not_in_product. Free.

When to use: before run_scan, so you do not treat a down engine as an omit.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | no | — | Scope business ID |

### `update_business`

POST `/v1/update_business` · Free · `business:write` · write

Updates name, industry, city, state, disambiguation, index_status, webhook_url, or primary flag. Free. Requires business:write.

When to use: when list_businesses left description empty or recs need existing URLs.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `name` | string | no | — | — |
| `industry` | string | no | — | — |
| `city` | string | no | — | — |
| `state` | string | no | — | — |
| `disambiguation` | string | no | — | — |
| `index_status` | unknown | indexed | not_indexed | no | — | — |
| `webhook_url` | string (uri) | no | — | — |
| `is_primary` | boolean | no | — | — |

### `set_primary_business`

POST `/v1/set_primary_business` · Free · `business:write` · write

Sets the account primary business. Free. Requires business:write.

When to use: on a multi-business account before reading whoami.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |

### `get_visibility_report`

GET `/v1/get_visibility_report` · Free · `read` · read-only

Returns the current AI visibility score. Default view=compact is score, scores_by_engine (0–100 recommendation share per engine, null when not_measured), scorer_version, engines attempted/measured, mention-kind counts, cite_kind_split (ours/other), derived issues, and trend. Pass view=full for the per-engine dump. Do not assume Grok/Copilot/Google AI ran. Use when the user asks how visible they are in AI search.

When to use: when the user asks how visible they are in AI search.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `view` | compact | full | no | compact | compact is the default; full returns the per-engine dump. |

### `get_ai_traffic`

GET `/v1/get_ai_traffic` · Free · `read` · read-only

Returns AI-referral sessions from a connected GA4 property and latest Google Search Console queries. connected=false when Google is not linked. Use when the user asks whether AI visibility is turning into visits.

When to use: when the user asks whether AI visibility is turning into visits.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `days` | integer | no | 28 | — |

### `get_google_connect_url`

GET `/v1/get_google_connect_url` · Free · `read` · read-only

Returns the dashboard URL a human uses to connect Google Analytics and Search Console.

When to use: when get_ai_traffic is not_connected.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | no | — | Scope business ID |

### `get_scan`

GET `/v1/get_scan` · Free · `read` · read-only

Returns one scan by id: queued, running, completed, completed_partial, or failed, plus engines_attempted vs engines_measured, prompts_billed/prompts_scored, progress_percent, engine outages, scores_by_engine when completed, and enrichment readiness. Default view=compact hides vs_previous.cells. Poll this after run_scan.

When to use: to poll a scan started with run_scan.

| name | type | required | default | description |
|---|---|---|---|---|
| `scan_id` | string | yes | — | Scan ID returned by run_scan |
| `view` | compact | full | no | compact | compact hides vs_previous.cells; full returns the cell dump. |

### `get_scan_diff`

GET `/v1/get_scan_diff` · Free · `read` · read-only

Compares two scans: prompts added/removed, engines measured, scorer_version, and score delta.

When to use: when a trend from 47 to 0 is uninterpretable.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `scan_a` | string | yes | — | Older scan ID |
| `scan_b` | string | yes | — | Newer scan ID |

### `get_prompt_results`

GET `/v1/get_prompt_results` · Free · `read` · read-only

Returns paginated results for the tracked prompt set from the headline scan. Default view=rollup with include_snippets false. view=cells or view=matrix also accepted. Optional snippet_chars when snippets are on.

When to use: when the user asks which prompts they win or lose.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `page` | integer | no | 1 | — |
| `page_size` | integer | no | 25 | — |
| `view` | cells | rollup | matrix | no | rollup | rollup/matrix is the default compact grid; cells is the opt-in dump. |
| `include_snippets` | boolean | no | false | Default false. Set true only when you need response_snippet. |
| `snippet_chars` | integer | no | 240 | — |

### `get_citations`

GET `/v1/get_citations` · Free · `read` · read-only

Returns sources AI engines cite. Default view=compact is counts, top domains, cite_kind split, and top URLs. Pass view=full for the full citation dump. Use when the user asks who ChatGPT cites or which sources they are missing.

When to use: when the user asks who ChatGPT cites or which sources they are missing.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `view` | compact | full | no | compact | compact is the default; full returns the citation dump. |

### `get_citation_briefs`

GET `/v1/get_citation_briefs` · Free · `read` · read-only

Returns citation-backed content briefs for measured losses, including urls_to_earn. Engine errors are not treated as losses.

When to use: when the user asks what page to write next.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |

### `get_roundup_watchlist`

GET `/v1/get_roundup_watchlist` · Free · `read` · read-only

Independent roundup URLs already cited in the headline scan. Excludes competitor-owned and vendor docs. Use this instead of pitching Housecall Pro or Google Support.

When to use: when deciding which off-site page to pitch for a citation.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |

### `get_money_page_gap`

GET `/v1/get_money_page_gap` · Free · `read` · read-only

Per tracked prompt: registered money page vs the URL ChatGPT actually cited. Use when the model knows you from /about instead of the compare or product URL.

When to use: when the model knows the brand from the wrong owned URL.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `engine` | string | no | chatgpt | Engine to inspect. Default chatgpt. |

### `get_crawler_visits`

GET `/v1/get_crawler_visits` · Free · `read` · read-only

Returns first-party AI crawler hits from the WordPress snippet or plugin. Empty until bots are reported.

When to use: when the user asks whether GPTBot or ClaudeBot crawled a page.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `limit` | integer | no | 25 | — |

### `get_competitors`

GET `/v1/get_competitors` · Free · `read` · read-only

Returns the measured competitor set and share-of-voice across AI engines. Use when the user asks who AI recommends instead of them.

When to use: when the user asks who AI recommends instead of them.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |

### `pin_competitors`

POST `/v1/pin_competitors` · Free · `business:write` · write

Replaces the pinned competitor list for a business.

When to use: when get_competitors returned invented names.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `names` | string[] | yes | — | Competitor names to pin |

### `get_recommendations`

GET `/v1/get_recommendations` · Free · `read` · read-only

Returns a prioritized fix list with id, rec_type, winner, cited_url, and blocks_prompt_ids. Never empty when the scan already knows who won. Pass recommendation id to generate_fix.

When to use: when the user asks what to do next to improve AI visibility.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |

### `add_business`

POST `/v1/add_business` · Free · `business:create` · write

Idempotently adds a business by normalized domain, scrapes the site, and seeds ~20 tracked plus ~20 proposed prompts. Free (no check debit). Requires business:create and an all-businesses key. Use when the user wants to monitor a new site.

When to use: when the user wants to monitor a new site.

| name | type | required | default | description |
|---|---|---|---|---|
| `url` | string (uri) | yes | — | Business website |
| `name` | string | yes | — | Business name |
| `city` | string | no | — | City |
| `state` | string | no | — | State or region |
| `industry` | string | no | — | Industry |

### `run_scan`

POST `/v1/run_scan` · 1 check per tracked prompt · `scan:run` · open world

Runs every tracked prompt across currently green engines. Optional engines allowlist. force retries not_measured cells only. check_prompt quotes 1 check the same way. confirm defaults false.

When to use: when the user asks to refresh visibility or run all tracked prompts.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `confirm` | boolean | no | false | If false, return a quote only |
| `use_defaults` | boolean | no | false | Allow 4 generated prompts when none are tracked |
| `force` | boolean | no | false | Retry not_measured cells only. Quotes those cells, not the full tracked set. |
| `engines` | chatgpt | claude | perplexity | gemini[] | no | — | Optional allowlist. Omit Gemini while it is down. |
| `idempotency_key` | string | no | — | Stable caller-provided idempotency key |

### `check_prompt`

POST `/v1/check_prompt` · Exactly 1 check · `scan:run` · open world

Tests one prompt across engines without tracking it. confirm defaults false (quote only). Costs exactly 1 check when confirm is true. On confirm, returns by_engine (this prompt's mention_kind per engine) and scores_by_engine (updated 0–100 recommendation share).

When to use: to try a query before adding it to monitoring.

| name | type | required | default | description |
|---|---|---|---|---|
| `prompt` | string | yes | — | — |
| `business_id` | string | yes | — | Scope business ID |
| `confirm` | boolean | no | false | If false, return a quote only |
| `idempotency_key` | string | no | — | Stable caller-provided idempotency key |

### `diff_prompt`

GET `/v1/diff_prompt` · Free · `read` · read-only

Returns the last two measured results for one compiled prompt. Free.

When to use: after publishing a fix, to verify the next answer.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `prompt` | string | yes | — | — |

### `track_prompt`

POST `/v1/track_prompt` · Free now — adds committed burn · `prompts:write` · write

Starts monitoring a prompt. Free now. Pass cadence or check_interval_days to change the account interval (monthly→30). Omitting both leaves the current interval.

When to use: when the user wants to start monitoring a query over time.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `prompt` | string | yes | — | — |
| `cadence` | weekly | monthly | no | — | When present, sets the account interval: monthly=30, weekly=7. |
| `check_interval_days` | integer | no | — | Account-wide check interval. Overrides cadence when both are sent. |

### `untrack_prompt`

POST `/v1/untrack_prompt` · Free · `prompts:write` · destructive

Stops monitoring a prompt and reduces committed burn.

When to use: when the user wants to stop monitoring a query.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `prompt_id` | string | yes | — | Tracked prompt ID |

### `update_check_interval`

POST `/v1/update_check_interval` · Free · `prompts:write` · write

Sets the account-wide check_interval_days (1, 3, 5, 7, 14, or 30). Monthly is 30.

When to use: when burn is too high because the account is on daily checks, or after track_prompt cadence was ignored.

| name | type | required | default | description |
|---|---|---|---|---|
| `check_interval_days` | integer | yes | — | Account-wide check interval in days |

### `update_roundup_status`

POST `/v1/update_roundup_status` · Free · `business:write` · write

Sets not_pitched | pitched | listed | cited_us on an independent roundup URL. Requires business:write.

When to use: after pitching or confirming a listing on an independent roundup.

| name | type | required | default | description |
|---|---|---|---|---|
| `business_id` | string | yes | — | Scope business ID |
| `url` | string (uri) | yes | — | Independent roundup URL |
| `status` | not_pitched | pitched | listed | cited_us | yes | — | — |

### `generate_fix`

POST `/v1/generate_fix` · Free · `business:write` · read-only

Drafts schema, FAQ, or content from a recommendation. Never writes to the website or CMS.

When to use: when the user asks to draft a schema, FAQ, or content fix.

| name | type | required | default | description |
|---|---|---|---|---|
| `recommendation_id` | string | yes | — | — |

### `complete_recommendation`

POST `/v1/complete_recommendation` · Free · `business:write` · write

Marks a recommendation done or dismissed. Pass status=dismissed with a reason when the rec is wrong. Requires business:write.

When to use: when the user has applied a fix and wants Scope to attribute the before/after.

| name | type | required | default | description |
|---|---|---|---|---|
| `recommendation_id` | string | yes | — | — |
| `note` | string | no | — | — |
| `fix_url` | string (uri) | no | — | Link to the applied fix |
| `status` | completed | dismissed | no | — | dismissed = won't fix / bad rec |
| `reason` | string | no | — | Why this rec was dismissed |

## Checks and burn

A **check** is one prompt evaluated across all supported engines. Reads spend **no** checks.

`check_prompt` spends exactly one check. `run_scan` spends one check per currently tracked prompt.

Adding a prompt with `track_prompt` is free at that moment — its schedule creates **committed burn** from `check_interval_days` (default 5 days = +6 / prompt / month; monthly +1 only at interval 30).

For operate/test/setup, enroll, scan, then echo the receipt. Stop before spend only for auto-upgrade, 80% burn, or a hard cap.

There is no `publish_fix` tool. Scope agents draft fixes; they never publish directly to a website or CMS.

Always pass `business_id` when the account has more than one business. `whoami.weekly_brief` is the **primary** business, not necessarily the one the user is talking about.

A prompt is billed only if at least one engine returns a body. Timeout / 429 / 400 cells are error, never omit.

A billed cell can still be unmeasured (timeout, 429, or ChatGPT gpt-4o fallback). Do not force-retry a down engine.

`track_prompt` is free now and expensive later. Default 5-day interval is +6 checks per month per prompt.

`quota_exceeded` is terminal. Do not retry or split the action.

## Errors

| code | status | meaning | fix |
|---|---|---|---|
| `invalid_request` | 400 | Malformed or missing required arguments. | Fix the payload. Retrying the same body will fail identically. |
| `unauthorized` | 401 | Missing, expired, or invalid API key or OAuth token. | Check the Authorization header. Keys are shown once at creation. |
| `insufficient_scope` | 403 | The key is missing a required permission. | Grant the scope in Settings, or use a key that has it. No rotation needed. |
| `tier_locked` | 403 | The account is not on Growth. | Relay the returned upgrade_url and stop. Do not retry. |
| `not_found` | 404 | The business, prompt, or recommendation ID does not exist on this account. | Re-read list_businesses — IDs are per account. |
| `rate_limited` | 429 | Too many requests. | Respect retry_after in seconds before retrying. |
| `quota_exceeded` | 402 | The account's check pool is exhausted. | Do not retry and do not split the action into smaller calls. |
| `over_committed` | 402 | Tracked prompts already exceed the plan pool. | Offer to reduce cadence or untrack prompts. |
| `internal_error` | 500 | Unexpected server error. | Safe to retry once. |

## Rate limits

- **Read tools:** 60 requests / 60 seconds (per authenticated user)
- **Action tools:** 10 requests / 60 seconds, 500 / day (per authenticated user)
- **Anonymous readiness check:** 5 requests / 24 hours (per source IP)

A `rate_limited` response includes `retry_after` in seconds.

## Recipes

### Weekly visibility brief

`whoami` (read) → `get_visibility_report` (read) → `get_prompt_results` (read) → `get_recommendations` (read)

Every Monday, pull my Scope visibility report, compare it to last week, list engines that moved, and name the top three losing prompts.

### Citation audit

`get_citations` (read) → `get_citation_briefs` (read) → `get_competitors` (read)

List the sources AI cites about my business, the gaps competitors have, and three citation briefs for prompts I am losing.

### Quote, then scan

`get_engine_status` (read) → `run_scan` (spend) → `get_scan` (read)

Quote a run_scan for this business. If engines_measured would be less than engines_attempted, say so and stop. If I confirm, run it and poll get_scan until it leaves queued or running.

### Did AI send traffic

`get_ai_traffic` (read) → `get_google_connect_url` (read)

Is Google connected? If yes, how many AI-referral sessions landed in the last 28 days, by source? If not, tell me to connect GA4 in Settings → Business and stop. Do not invent traffic.

## Security

There is no `publish_fix` tool. Agents draft; humans publish. No scope grants write access to a website or CMS.

- Skill: https://scope.online/agents/skill.md
- Product hub: https://scope.online/agents
