import type { Metadata } from 'next'; import Link from 'next/link'; import { Container, Note, PageHeader, Section } from '@/components/ui/section'; import { CONTACT_EMAIL, PUBLIC_API_BASE, routes } from '@/lib/site'; export const metadata: Metadata = { title: 'API & data', description: 'Public JSON API for Company Atlas: companies, events, timelines, metrics, rankings, industries, countries, search, SSE streaming and exports.' }; const GROUPS: { title: string; rows: [string, string, string][] }[] = [ { title: 'Platform', rows: [ ['GET', '/stats', 'Global counters: companies, sensors, observations, changes, events, dataset age (cached 60 s)'], ['GET', '/stats/history?days=90', 'Daily global aggregates and the activity index'], ['GET', '/system', 'Public aggregate health'], ['GET', '/pulse', 'Homepage aggregate (live, movers, hiring, launches, pricing, AI, industries, countries, trending, index, map)'], ['GET', '/live?limit=50&since=&event_type=&min_importance=', 'Latest active events (no cache)'], ['GET', '/live/stream', 'Server-sent events: `event: event` with an Event JSON, `event: heartbeat` every 20 s; `?since=` supported'], ], }, { title: 'Companies', rows: [ ['GET', '/companies?q=&country=&industry=&tier=&public=&sort=&sparkline=1', 'Paginated CompanyCard rows (`page`, `per_page` ≤ 200)'], ['GET', '/companies/{slug}', 'Detail: aliases, domains, relationships, metrics with confidence, coverage, signals, sparklines'], ['GET', '/companies/{slug}/events', 'Events with filters `event_type, event_subtype, since, until, min_importance, surface`'], ['GET', '/companies/{slug}/timeline?filter=all|products|jobs|pricing|leadership|locations|legal|news|developer', 'Grouped by day'], ['GET', '/companies/{slug}/metrics?days=90', 'Current metrics + series'], ['GET', '/companies/{slug}/jobs?status=open|removed|all&ai=1', 'Listings + summary (`meta.summary`)'], ['GET', '/companies/{slug}/people · /products · /pricing · /locations · /news', 'Reconciled entities with first/last seen'], ['GET', '/companies/{slug}/sensors · /history · /similar', 'Sensors, historical page viewer index, similar companies'], ['GET', '/companies/compare?companies=stripe,adyen,block', 'Side-by-side metrics, series, events, jobs, locations (2–6)'], ], }, { title: 'Provenance', rows: [ ['GET', '/sensors/{id} · /snapshots · /changes', 'Sensor with company and latest snapshot; its versions and changes'], ['GET', '/snapshots/{id}', 'Normalised text, semantic blocks, extracted fields (≤ 200 kB)'], ['GET', '/snapshots/{id}/diff/{other_id}', 'Block-level diff computed on demand'], ['GET', '/changes/{id}', 'Change with diff, structured delta and derived events'], ['GET', '/events/{id}', 'Event with every corroborating source and detection time'], ], }, { title: 'Events, rankings, atlases', rows: [ ['GET', '/events?event_type=&country=&industry=&since=&min_importance=&min_confidence=&q=&origin=&sort=', 'Paginated events'], ['GET', '/events/types · /events/summary?days=7&group=type|industry|country', 'Taxonomy counts and deltas'], ['GET', '/rankings?kind=most_active|hiring_growth|…&window=24h|7d|30d|90d|1y&country=&industry=', 'Ranked CompanyCards with value and delta'], ['GET', '/industries · /industries/{slug} · /countries · /countries/{code}', 'Living indices per industry and country'], ['GET', '/signals?scope=company|industry|country|global · /trends?window=7d · /map?metric=events_30d · /index', 'Signals, trending terms, map buckets, Global Corporate Activity Index'], ], }, { title: 'Search, watchlists, exports', rows: [ ['GET', '/search?q=&types=companies,events,… · /search/suggest?q= · /ask?q=', 'Grouped search, fast suggestions, natural-language routing'], ['GET/POST/DELETE', '/watchlist · /watchlist/{slug} · /alerts · /alerts/{id} · /alerts/deliveries', 'Owner-token endpoints (`X-CA-Owner-Token`, ≥ 24 chars, stored hashed)'], ['GET', '/export/events.{json,ndjson,csv}?since=&event_type=&country=&limit=10000', 'Streamed export'], ['GET', '/export/companies.{json,ndjson,csv}?country=&industry= · /export/jobs.ndjson?company=&since=', 'Streamed exports'], ['GET', '/sitemap?kind=companies|industries|countries&page= · /methodology', 'Indexable slugs; metric definitions'], ], }, ]; export default function ApiPage() { const base = PUBLIC_API_BASE; return (

Base URL: {base}. Pagination: ?page=1&per_page=25 (max 200) → {'{ items, page, per_page, total, pages }'}. Unknown parameters are ignored. Interactive OpenAPI docs: {base.replace(/^https?:\/\//, '')}/docs.

Quick start

          {`# latest structured events
curl -s "${base}/live?limit=5" | jq '.items[] | {company: .company.display_name, type: .event_type, title, confidence_label, source_url}'

# a company with its metrics, coverage and signals
curl -s "${base}/companies/stripe" | jq '{display_name, metrics, counts, coverage}'

# pricing events in Canada since September, most important first
curl -s "${base}/events?event_type=PRICING&country=CA&since=2026-09-01T00:00:00Z&sort=importance"

# rankings: fastest hiring growth over 30 days in fintech
curl -s "${base}/rankings?kind=hiring_growth&window=30d&industry=fintech"

# stream (SSE)
curl -N "${base}/live/stream"

# export as NDJSON
curl -s "${base}/export/events.ndjson?event_type=LEADERSHIP&limit=1000" > leadership.ndjson`}
        

Watchlists without an account

          {`TOKEN=$(uuidgen | tr -d -)$(uuidgen | tr -d -)
curl -s -X POST "${base}/watchlist" -H "X-CA-Owner-Token: $TOKEN" -H "content-type: application/json" -d '{"company":"stripe"}'
curl -s "${base}/watchlist" -H "X-CA-Owner-Token: $TOKEN"
curl -s -X POST "${base}/alerts" -H "X-CA-Owner-Token: $TOKEN" -H "content-type: application/json" \\
  -d '{"name":"Stripe pricing","company":"stripe","condition":{"event_types":["PRICING"],"min_importance":0.5},"channel":"webhook","target":"https://example.com/hook"}'`}
        
{GROUPS.map((g) => (
{g.rows.map(([m, p, n]) => ( ))}
Method Path Notes
{m} {p} {n}
))}
  • Anonymous: 120 requests / minute per IP; bursts above that return 429 with a Retry-After header. Higher limits, webhooks and bulk datasets on request by email to {CONTACT_EMAIL} (free for research).
  • Public aggregates (/pulse, /stats, /rankings, /industries, /countries) are cached 60 s and served with Cache-Control: public, max-age=60; /live*, owner and admin routes are no-store.
  • Metrics are 0–100 floats rounded to one decimal, except hiring momentum (percentage, may be negative) and open jobs (integer). A metric without inputs is omitted or null — never zero.
  • Company lookups accept slug or id; slugs may change, ids never do. Unknown → 404 {'{ "detail": "company not found" }'}.
  • Redistribution: derived data (events, metrics, entity facts, metadata) may be reused with attribution to Company Atlas and a link to the event page; raw page content is not redistributed.
  • Careful language is part of the contract: status: no_longer_listed, confidence_label and origin (deterministic · llm · hybrid · backfill) ship with every event — see methodology.
Shapes are documented in the repository (`docs/API.md`) and mirrored by the web client’s TypeScript types.
); }