Skip to content

DocsReference

REST API reference

The OpenReach REST API: authentication, the response envelope, and every route for lookups, creators, leads, lead searches, monitors and mentions.

The dashboard is built on this API, so anything you can do in the app you can script. The base URL is {API_URL}/api (http://localhost:4300/api on a default self-hosted install).

Authentication

Send an OpenReach API key as Authorization: Bearer oreach_…. Create keys under Settings; a key is shown once and acts as you, so treat it like a password. Keys can be revoked at any time and cannot create further keys. The dashboard itself uses a session cookie and may only make changes from its own origin.

Apps that sign in with OAuth, such as ChatGPT, get short-lived access tokens that work the same way. OpenReach is an OAuth 2.1 authorization server for its MCP endpoint, with discovery at /.well-known/oauth-protected-resource, dynamic client registration, PKCE (S256) and rotating refresh tokens.

Responses

Every response is JSON: { "ok": true, "data": … } on success, or { "ok": false, "error": { "code", "message", "details?" } } with a 4xx or 5xx status. Codes are listed in Errors and troubleshooting. IDs are UUIDs; a malformed one is a 404.

Lookups

POST /projects/:projectId/lookups/:name runs one paid lookup with its input as the JSON body. Inputs are validated before anything is spent; an omitted country or language defaults to the project’s. POST /lookups/:name runs the same lookups without a project, in the United States in English unless the input or ?country=&language= says otherwise.

Lookup query parameters
ParameterEffect
?dryRun=1Return { estimate: { min, max } } and spend nothing. max is null when the AI check is on (token-priced).
?refresh=1Skip the cache and pay for fresh data.
?maxCostUsd=0.50Refuse with cost_ceiling_exceeded if the worst-case price is higher. Must be a positive number.
Terminal
# Price a lookup (free), then run it with a ceiling
curl -X POST "$API_URL/api/projects/$PROJECT/lookups/find_leads?dryRun=1" \
  -H "Authorization: Bearer $OPENREACH_API_KEY" -H "Content-Type: application/json" \
  -d '{"query":"plumber","location":"Austin, TX","pages":2}'

curl -X POST "$API_URL/api/projects/$PROJECT/lookups/find_leads?maxCostUsd=0.20" \
  -H "Authorization: Bearer $OPENREACH_API_KEY" -H "Content-Type: application/json" \
  -d '{"query":"plumber","location":"Austin, TX","pages":2}'
Reply
{
  "ok": true,
  "data": {
    "name": "find_leads",
    "estimate": { "min": 0.07, "max": 0.14 },
    "data": { "query": "plumber", "location": "Austin, TX", "pages": 2, "leads": [ ... ] },
    "costUsd": 0.14,
    "cached": false,
    "fetchedAt": "2026-10-25T09:30:00.000Z"
  }
}

cached: true results have costUsd: 0. A result where some searches failed carries a warnings or failed list and is not cached. Names: find_creators, analyse_creator, analyse_creators, similar_creators, find_leads, lead_emails, brand_mentions, find_conversations, find_subreddits. GET /lookups lists them with descriptions and cache times.

Account

Account routes
RouteNotes
GET /meUser and settings: whether a YepAPI key is set, its hint, the monthly limit.
PUT /settings/yepapi-key{ apiKey }. Verified without spending credits. Never returned.
DELETE /settings/yepapi-keyRemove the key.
PUT /settings/budget{ monthlyBudgetUsd: number | null }.
GET /balanceLive YepAPI balance, with this month’s paid calls, spend and limit. Free.
GET /usageThis month’s spend in total, by feature and by day, and the latest calls.
GET, POST /api-keys; DELETE /api-keys/:idList, create ({ name }; the key is in the reply once) and revoke. Creating needs a signed-in session.
GET /sourcesEvery community and how it is searched (a free API or Google). Free.
GET /filter-fieldsThe fields and presets for lead and creator filter rules. Free.
GET /locations?q=&country=Location codes for project markets. Free.

Projects

GET/POST /projects, GET/PATCH/DELETE /projects/:projectId. Fields: name, domain, description, keywords (niche and problem words), competitors, locationCode, country, language, context (free-form notes for agents). Deleting a project deletes everything in it.

Creators

Creator routes
RouteNotes
GET /projects/:id/creators?platform= &status= &tag= &q= &sort=added|followers|engagement|views|name &order= &limit= &offset=. Returns { total, creators }.
POST /projects/:id/creators{ creators, status?, tags? }. Saving again refreshes numbers and keeps status and notes.
PATCH /projects/:id/creators/:creatorId{ status?, notes?, tags? }.
POST /projects/:id/creators/bulk{ ids, status?, addTags?, removeTags? }.
POST /projects/:id/creators/delete{ ids }.
POST /projects/:id/creators/analysePaid. { ids } (up to 25): fresh profile and posts. ?dryRun=1, ?maxCostUsd=.

Leads and lead searches

Lead routes
RouteNotes
GET /projects/:id/leads?status= &tag= &q= &website=yes|no &searchId= &sort=added|name|rating|reviews &order= &limit= &offset=.
POST /projects/:id/leads{ leads, status?, tags? }. One row per Google Place per project.
PATCH /projects/:id/leads/:leadId{ status?, notes?, tags?, emails? }.
POST /projects/:id/leads/bulk{ ids, status?, addTags?, removeTags? }.
POST /projects/:id/leads/delete{ ids }.
POST /projects/:id/leads/emailsPaid. { ids, method: website|directory }. ?dryRun=1, ?maxCostUsd=.
GET, POST /projects/:id/lead-searchesPOST { queries, locations?, mode: text|grid, grid?, pages, params?, filters?, autoSave? } starts a search (202). ?dryRun=1 returns its size and price.
GET /lead-searches/:searchIdProgress and spend, with the places found: ?matched=all|yes|no &limit= &offset=.
POST /lead-searches/:searchId/cancel; DELETE /lead-searches/:searchIdCancel a running search, or delete a finished one (saved leads stay).

Monitors and mentions

Monitor routes
RouteNotes
GET /projects/:id/monitors?kind=brand|topic. Each with its price per run and month, counts and last run.
POST /projects/:id/monitors{ kind, terms, excludes?, competitors?, sources?, depth?, aiScoring?, schedule?, active? }. ?dryRun=1 for the price, ?run=1 to start the first run.
GET, PATCH, DELETE /monitors/:monitorIdThe monitor and its last 20 runs; change it; delete it with what it found.
POST /monitors/:monitorId/runsPaid. Run now (202). ?maxCostUsd=.
GET /monitor-runs/:runId; POST /monitor-runs/:runId/cancelA run’s progress, spend and warnings; cancel it.
GET /projects/:id/mentions?kind= &monitorId= &status=new|read|replied|ignored|open &source= &sentiment= &minIntent= &q= &sort=newest|intent|published &limit= &offset=.
POST /projects/:id/mentions/status{ ids, status }.
GET /projects/:id/communitiesCommunities ranked by the mentions found there. ?kind= &monitorId=.

MCP

POST {API_URL}/mcp is the MCP server, stateless streamable HTTP, with the same authentication. Its tools call the routes above; see MCP tools.