Skip to content
MarketPrior

Developers · REST API

Cited company diligence, over one REST API.

Search companies, run diligence as jobs, read every report section with its freshness, and search leads and trigger events — the same data, paywall and credits as the app, behind a scoped workspace API key.

49

operations

5

resource groups

7

API-key scopes

v1.0.0

OpenAPI 3.1.0

What you can do

Everything the dashboard does, as endpoints.

Every operation below is served by the same code paths as the app, so a report section or lead row reads the same over the API.

Companies & reports

Search companies, read the report

GET /v1/companies/search spans your workspace, the tracked corpus and public registries. Read the whole report or one section, its provenance, the memo, citations and structured evidence.

Jobs

Diligence runs as jobs

POST …/diligence-runs answers 202 with a job at once; poll GET /v1/jobs/{job_id}. A second start while one is active returns the same job.

Monitoring

Watchlist and alerts

Add companies to the watchlist and page through risk / upside alerts; acknowledge them with POST /v1/alerts/{alert_id}/ack.

Leads

Lead search and trigger feed

ICP search over the company corpus (POST /v1/leads/search), the dated trigger-event feed, saved searches with alerts, and lists.

Contacts · metered

Reveal contacts, export leads

Contacts come back masked. Revealing one or exporting CSV spends lead credits — price it first with POST /v1/leads/export/quote; already-revealed contacts and exported rows are free.

Account

Credits and balances

GET /v1/credits returns remaining diligence assessments and lead-credit balances, so a client can check before it spends.

Quickstart

From API key to report in three calls.

Create a key in Settings → API keys, add a company, start a diligence run and read the report.

  • Base URL https://marketprior-diligence.fly.dev
  • Adding a company is free and idempotent by name
  • A run on a new company spends one assessment; re-runs are free
  • The free screen is readable before any unlock
quickstart · curl
export MARKETPRIOR_API_KEY=mp_...   # Settings → API keys

# 1. Add the company (free; idempotent by name) → company.id
curl -X POST "https://marketprior-diligence.fly.dev/v1/companies" \
  -H "Authorization: Bearer $MARKETPRIOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Payments", "domain": "acmepay.com"}'

# 2. Start a diligence run → 202 + a job; poll GET /v1/jobs/{job_id}
curl -X POST "https://marketprior-diligence.fly.dev/v1/companies/$COMPANY_ID/diligence-runs" \
  -H "Authorization: Bearer $MARKETPRIOR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

# 3. Read the report: every section's status + freshness
curl "https://marketprior-diligence.fly.dev/v1/companies/$COMPANY_ID/report" \
  -H "Authorization: Bearer $MARKETPRIOR_API_KEY"
quickstart · python
import os, time, requests

BASE = "https://marketprior-diligence.fly.dev"
H = {"Authorization": f"Bearer {os.environ['MARKETPRIOR_API_KEY']}"}

company = requests.post(f"{BASE}/v1/companies", headers=H,
                        json={"name": "Acme Payments", "domain": "acmepay.com"}).json()["company"]

job = requests.post(f"{BASE}/v1/companies/{company['id']}/diligence-runs", headers=H).json()
while job["status"] in ("queued", "running"):
    time.sleep(5)
    job = requests.get(f"{BASE}/v1/jobs/{job['id']}", headers=H).json()

report = requests.get(f"{BASE}/v1/companies/{company['id']}/report", headers=H).json()

Auth & scopes

One key, one workspace, least privilege.

Send the key as Authorization: Bearer mp_… or X-API-Key: mp_…. Keys are stored hashed, revocable, and run as their own workspace. Each operation names its x-required-scope; a missing scope is 403 insufficient_scope.

API key scopes
ScopeGrants
reports:readRead companies, report sections, memos, alerts, jobs and credit balances.
diligence:runAdd companies and start diligence runs (a new company spends one assessment).
monitor:writeAcknowledge monitoring alerts; add/remove watchlist subjects (uses plan monitor slots).
leads:readLead search, trigger feed, saved searches, alerts and masked contacts.
leads:writeCreate/update/delete saved searches and lists, mark lead alerts read, add registry companies to the corpus, report lead outcomes (sent / bounced / replied …).
leads:revealReveal a contact's details (metered: spends lead credits).metered
leads:exportExport leads as CSV (metered: spends lead credits).metered

Operating model

Paywall, limits, retries, pages, freshness.

The rules every endpoint follows, so a client can be written once.

402 report_locked — the paywall

The free screen (fused_scorecard, risk_score, upside_score and their citations) is always readable. Other sections answer 402 with report_locked until the company’s report is unlocked — the same decision the dashboard makes. Never render a guess in its place.

paywall · typescript
const res = await fetch(`https://marketprior-diligence.fly.dev/v1/companies/${id}/sections/hiring`, {
  headers: { Authorization: `Bearer ${process.env.MARKETPRIOR_API_KEY}` },
});
if (res.status === 402) {
  // error.code === "report_locked" — unlock the report; never guess the content
} else {
  const section = await res.json(); // payload + freshness
}

Errors — one envelope

Every error, including validation and rate limiting, is {"error": {code, message, status, details}} with a stable machine-readable code: report_locked, lead_credits_required, upgrade_required, insufficient_scope, rate_limited, not_found…

error · http
HTTP/1.1 402 Payment Required

{
  "error": {
    "code": "report_locked",
    "message": "This section is part of the paid company report. …",
    "status": 402,
    "details": { … }
  }
}

Rate limits

Per key, 120 requests/minute by default, shared by REST and MCP. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; over the limit you get 429 with Retry-After.

headers · http
HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: …

HTTP/1.1 429 Too Many Requests
Retry-After: …
{ "error": { "code": "rate_limited", "status": 429, … } }

Idempotency keys

Send Idempotency-Key: <≤255 chars> on any POST /v1/…. A retry with the same key and body replays the first response (marked Idempotent-Replayed: true) and is never charged twice. The same key with a different body is 422 idempotency_key_reused; one still in flight is 409 idempotency_request_in_progress.

retry-safe run · curl
curl -X POST "https://marketprior-diligence.fly.dev/v1/companies/$COMPANY_ID/diligence-runs" \
  -H "Authorization: Bearer $MARKETPRIOR_API_KEY" \
  -H "Idempotency-Key: run-$COMPANY_ID-2026-09"

Cursor pagination

List endpoints return {data, next_cursor, has_more}. Pass cursor=<next_cursor> back until has_more is false.

pages · http
GET /v1/companies?limit=20
{ "data": [ … ], "next_cursor": "…", "has_more": true }

GET /v1/companies?limit=20&cursor=<next_cursor>

Freshness on every section

Each section carries freshness — when it was computed, its age and status — and GET …/freshness lists them all. POST …/refresh re-fetches stale sections for free.

freshness · json
"freshness": {
  "computed_at": "…",
  "age_seconds": 5400,
  "status": "…",
  "as_of": "…"
}

Reference

49 operations, generated from the spec.

This table is built from the live OpenAPI document (OpenAPI 3.1.0, API v1.0.0) with the scope each operation needs. Machine-readable: https://marketprior-diligence.fly.dev/v1/openapi.json

Companies & reports 15

Companies & reports operations
MethodPathSummaryScope
GET/v1/companiesList your companiesreports:read
POST/v1/companiesAdd a companydiligence:run
GET/v1/companies/searchSearch companiesreports:read
GET/v1/companies/{company_id}Get a companyreports:read
GET/v1/companies/{company_id}/reportGet the full reportreports:read
GET/v1/companies/{company_id}/sections/{section}Get one report sectionreports:read
GET/v1/companies/{company_id}/sections/{section}/provenanceSection provenancereports:read
GET/v1/companies/{company_id}/memoGet the investment memoreports:read
GET/v1/companies/{company_id}/citationsSource-linked findingsreports:read
GET/v1/companies/{company_id}/dataStructured evidence rowsreports:read
GET/v1/companies/{company_id}/benchmarkLive cohort benchmarkreports:read
GET/v1/companies/{company_id}/founder-screeningFounder screening candidatesreports:read
GET/v1/companies/{company_id}/freshnessSection freshnessreports:read
POST/v1/companies/{company_id}/refreshRefresh stale sectionsdiligence:run
POST/v1/companies/{company_id}/diligence-runsStart a diligence rundiligence:run

Jobs 2

Jobs operations
MethodPathSummaryScope
GET/v1/jobsList recent jobsreports:read
GET/v1/jobs/{job_id}Get a jobreports:read

Monitoring 5

Monitoring operations
MethodPathSummaryScope
GET/v1/alertsList monitoring alertsreports:read
POST/v1/alerts/{alert_id}/ackAcknowledge an alertmonitor:write
GET/v1/watchlistList the monitoring watchlistreports:read
POST/v1/watchlistAdd to the watchlistmonitor:write
DELETE/v1/watchlist/{subject_id}Remove from the watchlistmonitor:write

Leads 26

Leads operations
MethodPathSummaryScope
GET/v1/contacts/companies/{company_id}Get Company Contactsleads:read
POST/v1/contacts/companies/{company_id}/refreshRequest Refreshleads:read
GET/v1/leads/creditsGet Creditsleads:read
POST/v1/leads/outcomesPost Outcomesleads:write
GET/v1/leads/outcomes/statsOutcome Statsleads:read
GET/v1/leads/eventsList Eventsleads:read
POST/v1/leads/searchSearchleads:read
GET/v1/leads/companies/suggestSuggest Companiesleads:read
POST/v1/leads/companies/addAdd Companyleads:write
GET/v1/leads/jobs/{job_id}Get Lead Jobleads:read
GET/v1/leads/companies/{company_id}/enrichmentGet Company Enrichmentleads:read
GET/v1/leads/saved-searchesList Saved Searchesleads:read
POST/v1/leads/saved-searchesCreate Saved Searchleads:write
PATCH/v1/leads/saved-searches/{search_id}Update Saved Searchleads:write
DELETE/v1/leads/saved-searches/{search_id}Delete Saved Searchleads:write
GET/v1/leads/saved-searches/{search_id}/previewPreview Saved Searchleads:read
GET/v1/leads/alertsList Alertsleads:read
POST/v1/leads/alerts/{alert_id}/readRead Alertleads:write
POST/v1/leads/export/quoteExport Quoteleads:read
POST/v1/leads/exportExportleads:export
POST/v1/leads/contacts/{contact_id}/revealReveal Contactleads:reveal
GET/v1/leads/listsGet Listsleads:read
POST/v1/leads/listsCreate Listleads:export
POST/v1/leads/lists/{list_id}/companiesAdd To Listleads:export
GET/v1/leads/lists/{list_id}Get Listleads:read
DELETE/v1/leads/lists/{list_id}Delete Listleads:write

Account 1

Account operations
MethodPathSummaryScope
GET/v1/creditsCredit balancesreports:read

FAQ

API questions.

Prefer an agent? The same operations are tools on the MarketPrior MCP server. Credits and plans are on pricing; what feeds each section is on data sources.

How do I authenticate with the MarketPrior API?
Create an API key in Settings → API keys and send it as "Authorization: Bearer mp_…" or as an "X-API-Key: mp_…" header. Each key is bound to one workspace and carries scopes; there is no workspace parameter.
What does a 402 report_locked response mean?
The free screen — fused_scorecard, risk_score and upside_score — is always readable. Other sections return 402 with error.code "report_locked" until that company’s report is unlocked through your plan allowance or a purchased report, exactly as in the dashboard.
What costs credits?
Starting a diligence run on a company that has never been run spends one assessment; re-runs and refreshes of stale sections are free. Revealing a contact and exporting leads spend lead credits (402 lead_credits_required when short). On the free plan, AI usage (diligence runs, refreshes, chat) is paid from the one-time starter credit; once it is used up, those calls answer 402 starter_credit_exhausted until you buy a report or choose a plan. Everything else is included.
Can a retried request charge me twice?
Not if you send an Idempotency-Key (up to 255 characters) on POSTs: a retry with the same key and body replays the first response. Reusing the key with a different body is rejected with 422 idempotency_key_reused.
What are the rate limits?
Per API key, 120 requests per minute by default. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; over the limit you get 429 rate_limited with Retry-After. REST and MCP calls share the same per-key limit.
Is there an OpenAPI spec?
Yes — the machine-readable document is served at https://marketprior-diligence.fly.dev/v1/openapi.json, and the endpoint table on this page is generated from it.

Get an API key and make your first call.

Start free, create a key in Settings → API keys, and read a company’s free screen in one request.