import type { Metadata } from "next"; import Link from "next/link"; import { Page, PageHeader } from "@/components/ui/section"; import { SITE_URL } from "@/lib/site"; export const metadata: Metadata = { title: "Developers", description: "Market Atlas API: REST endpoints, WebSocket and SSE streams, envelopes, errors and rate limits.", alternates: { canonical: "/developers" } }; const REST: Array<[string, string]> = [ ["GET /v1/stats", "Live telemetry (instruments, connectors, observations today, events, freshness)."], ["GET /v1/markets", "Global overview: featured indices, crypto, FX, rates, commodities, equities, movers, exchanges with state, breadth."], ["GET /v1/instruments?asset_class=&exchange=&country=&q="ed=1&sort=&limit=&offset=", "Instrument master with canonical quotes. sort ∈ symbol | change | -change | volume | name."], ["GET /v1/instruments/{id|SYMBOL|VENUE:SYMBOL}", "Instrument detail: company, exchange + status, quote, aliases, observing sources, related instruments."], ["GET /v1/quotes/{id}", "One canonical quote."], ["GET /v1/quotes?ids=a,b,c", "Up to 200 canonical quotes."], ["GET /v1/quotes/{id}/provenance", "“Why this price?” — source coverage matrix: every contribution with observation_type, delta_bps vs consensus, weight, age, inclusion and reason; plus comparability, proxy/validator confirmations, coverage and shared_upstream_pairs."], ["GET /v1/coverage?asset_class=&tier=A|B|C|D&limit=", "Source Coverage Engine: summary (quoted, multiSource, byFamilies ≥5/≥3/≥2/1/0, weightedScore, tiers attainment, byAssetClass, proxiesConfirming, validatorsConfirming), targets, source-expansion queue, inferred shared upstreams (lineage)."], ["GET /v1/coverage/{id}", "Coverage of one instrument: tier, target, families, observations, proxies, validators, live/delayed/official counts, score, status, gap."], ["GET /v1/history/{id}?resolution=1m|5m|15m|1h|1d&from=&to=&limit=", "OHLCV bars (t, o, h, l, c, v, n sources, p producer)."], ["GET /v1/events?type=&instrument=&country=&asset_class=&severity=&since=&before=&limit=", "Canonical events; GET /v1/events/{id} for one."], ["GET /v1/filings?form=&cik=&q=&limit=&offset=", "Regulatory filings observed on EDGAR."], ["GET /v1/exchanges · GET /v1/exchanges/{id}", "Venues with session state, holidays, breadth, movers, events."], ["GET /v1/countries · GET /v1/countries/{code}", "Country atlas: exchanges, indices, rates, FX, equities, events."], ["GET /v1/search?q=", "Hybrid search grouped by asset class, exchange, country."], ["GET /v1/compare?ids=&resolution=", "Rebased series, return/volatility/drawdown, pairwise correlation."], ["GET /v1/changes?window=1m|5m|15m|1h|1d", "What changed: movers > 1 %, events, filings, by type."], ["GET /v1/breadth?scope=world|US|xnas", "Advancers/decliners/highs/lows for a scope."], ["GET /v1/sources · GET /v1/connectors · GET /v1/data-health · GET /v1/status · GET /v1/health · GET /v1/metrics", "Provenance directory, connector operations, health, public status, liveness, Prometheus metrics."], ]; export default function DevelopersPage() { const base = SITE_URL; const ws = base.replace(/^http/, "ws"); return (

Base URL & envelope

          {`${base}/v1

{ "data": …, "meta": { "request_id": "…", "timestamp": "2026-09-12T14:31:42.512Z", "data_status": "DELAYED" } }`}
        

Errors are structured: {`{ "error": { "code": "INSTRUMENT_NOT_FOUND", "message": "instrument not found" } }`} with the matching HTTP status. No stack traces.

Rate limits

20 requests per second sustained per IP (burst 300) on /v1 REST; 429 with Retry-After when exceeded. Streams and web pages are not counted. Higher limits and API keys will come with the developer accounts.

REST endpoints

{REST.map(([e, d]) => ( ))}
Endpoint Description
{e} {d}

Example

          {`curl ${base}/v1/quotes/BTC-USD

{
  "data": {
    "instrument_id": "crypto_btc_usd", "symbol": "BTC-USD", "price": 77326.37, "change_percent": 0.0275,
    "bid": 77326.36, "ask": 77326.37, "currency": "USD",
    "source_count": 4, "observation_count": 7, "proxy_count": 0, "validator_count": 0, "comparability": "LIVE",
    "dispersion_bps": 2.13, "confidence": 0.958, "freshness_ms": 1552,
    "data_status": "REALTIME", "market_state": null, "rights_status": "PUBLIC_ATTRIBUTED", "withheld": false,
    "updated_at": "2026-09-12T08:42:34.479Z", "source_timestamp": "2026-09-12T08:42:34.358Z"
  },
  "meta": { "request_id": "…", "timestamp": "…", "data_status": "REALTIME" }
}`}
        

Quote fields

  • data_status: REALTIME · DELAYED · AT_CLOSE · END_OF_DAY · STALE · WITHHELD. Never treat anything but REALTIME as live.
  • source_count = independent voting source families; observation_count = fresh observations of any role; proxy_count = stablecoin/derived proxies agreeing within 50 bp; validator_count = restricted-rights sources agreeing within 25 bp; comparability ∈ LIVE · FIX · EOD = class of the canonical value; dispersion_bps = spread between voting sources; confidence ∈ [0, 0.995].
  • Provenance contributions: observation_type ∈ TRADE · MID · QUOTE · INDEX_VALUE · INDICATIVE · STABLECOIN_PROXY · DERIVED · OFFICIAL_FIX · REFERENCE_RATE · SETTLEMENT · EOD_CLOSE · NAV; delta_bps = distance to the canonical value; reason ∈ stale · not_comparable · temporal_mismatch · validation_only · validator_disagrees · outlier. Validator values are null (withheld).
  • freshness_ms = age of the newest included observation at response time; source_timestamp is the source's own time when published.

WebSocket stream

          {`${ws}/v1/stream

→ { "action": "subscribe", "channels": ["quotes:BTC-USD", "quotes:AAPL", "events:*", "market:xnas"] }
← { "type": "hello", "version": 1, "server_seq": 1024, "ts": 1789202146812 }
← { "type": "subscriptions", "channels": ["quotes:crypto_btc_usd", "quotes:eq_us_xnas_aapl", "events:*", "market:xnas"] }
← { "type": "batch", "seq": 1, "ts": 1789202146921, "messages": [
      { "type": "quote", "instrument_id": "crypto_btc_usd", "symbol": "BTC-USD", "price": 77326.37, "change": 21.27, "change_pct": 0.0275,
        "bid": 77326.36, "ask": 77326.37, "volume": 6634.6, "currency": "USD", "timestamp": 1789202146563, "received": 1789202146819,
        "confidence": 0.906, "sources": 2, "status": "REALTIME" },
      { "type": "event", "event": { "id": "evt_…", "type": "TRADING_HALT", "title": "…", "severity": "WARNING", "confidence": 1, "source_count": 1, "sources": ["nasdaq-trader"], "instrument_ids": [], "data": { … } } },
      { "type": "market_state", "exchangeId": "xnas", "state": "OPEN", "at": 1789392600000 }
   ] }
← { "type": "heartbeat", "ts": …, "seq": 42 }     (every 25 s)
→ { "action": "ping", "id": 1 }  ←  { "type": "pong", "id": 1, "ts": … }
→ { "action": "unsubscribe", "channels": ["quotes:AAPL"] }`}
        

Channels:

  • quotes:<SYMBOL|id> one instrument · quotes:* everything (throttled to 2 updates/s per instrument) · quotes:class:CRYPTO one asset class · tape compact price changes.
  • events:* · events:<SYMBOL|id> · events:US (country) · events:type:TRADING_HALT.
  • market:<exchange> or market:* for session-state changes.

Frames are batched (≈ 10 per second per client) and carry a per-client seq; a gap means messages were dropped under backpressure. Up to 500 channels per connection.

Server-Sent Events

          {`curl -N "${base}/v1/sse?channels=quotes:BTC-USD,events:*"

event: hello
data: {"version":1,"channels":["quotes:crypto_btc_usd","events:*"],"ts":…}

event: batch
id: 58
data: {"type":"batch","seq":1,"ts":…,"messages":[{"type":"quote",…}]}`}
        

Identifiers

Stable public ids: eq_us_xnas_aapl, etf_us_arcx_spy, index_us_spx, crypto_btc_usd, fx_eur_usd, rate_us_us10y, cmd_xcme_gc_f. Endpoints accepting {"{id}"} also accept a bare symbol (AAPL, BTC-USD) or VENUE:SYMBOL.

Attribution & terms

Data carries the rights status of its sources; see data rights. Cboe values are 15-minute delayed and must be displayed as such; venue and official-source attributions must be preserved when redistributing. Market Atlas is an informational platform, not investment advice.

); }