API reference

Spreads

Cross-venue price-dislocation events — the same spread pipeline that powers the dashboards, as a curated event stream.

Planned /v1 event API

The /v1 endpoints documented here are the published event-API contract, but that serving layer is not deployed and these URLs return 404 today. The production machine rail is live separately at /api/v1/data, /mcp, and the authenticated family WebSocket routes. See Live APIs for endpoints that can be called now.

A spread lifecycle emits two event types per key: an `open` event (the first fresh detection above threshold) and a `close` event (the arbiter's one-shot below-threshold signal). Filter with `events=open|close` or take both.

Filter params mirror the SpreadBot per-user config 1:1 — thresholds, type allowlists, exchange allowlists with the same leg semantics, and volume/liquidity gates.

GET recent

Last ≤500 spread events held in memory, filtered.

GET/v1/spreads/recent

Tier: Free (60s delayed) · Builder (30s delayed) · Pro (live)

Query parameters

ParameterTypeDescription
min_pctfloatMinimum spread percent.
max_pctfloatMaximum spread percent.
spread_typestringOne of: futures/futures, spot/futures, spot/spot, dex/futures, spot/dex, dex/dex, internal/futures.
exchangestringMatch either leg on this exchange.
allowed_exchangescsvRestrict both legs to this allowlist.
buy_venuestringMatch the buy (cheaper) leg's venue only.
sell_venuestringMatch the sell (dearer) leg's venue only.
symbolstringExact symbol, e.g. BTC_USDT.
exclude_symbolstringBlacklist a symbol.
dexbooltrue → DEX legs only, false → exclude DEX.
min_volume_24hfloatMinimum 24h quote volume on the CEX leg.
min_dex_liquidity_usdfloatMinimum pool liquidity for DEX legs.
eventsstringopen | close — defaults to both.
delayint (s)Hide events fresher than N seconds. Your tier clamps the minimum (see Access tiers).
limitintMax events to return. Capped server-side (recent ≤ 500, history per-tier).

Response

json
{
  "meta": { "trace": "c_4f2a…", "as_of": 1781000000.42, "count": 1, "delay_seconds": 0 },
  "data": [
    {
      "received_at": 1781000000.0,
      "event": "open",
      "symbol": "ZK_USDT",
      "spread_type": "spot/futures",
      "spread_pct": 36.33,
      "legs": {
        "buy":  { "venue": "mexc",  "market": "spot",    "price": 0.01423, "mark_price": null,    "volume_24h_usd": 412000.0 },
        "sell": { "venue": "bybit", "market": "futures", "price": 0.0194,  "mark_price": 0.01939, "volume_24h_usd": 398000.0 }
      }
    }
  ]
}

GET history

Durable event log from the analytics store, keyset-paginated.

GET/v1/spreads/history

Tier: Free (1h) · Builder (1wk) · Pro (full retained)

Pages are keyset-paginated: pass the returned `meta.next_before` back as `before` to walk further back. `meta.next_before: null` (empty `data`) means you've reached the end.

Query parameters

ParameterTypeDescription
eventsstringopen | close.
allowed_exchangescsvRestrict both legs.
spread_typestringSpread type filter.
min_pctfloatMinimum spread percent.
sinceint (ts)Return events at or after this unix timestamp.
beforeint (ts)Keyset pagination cursor — return events strictly older than this unix timestamp.
limitintMax events to return. Capped server-side (recent ≤ 500, history per-tier).

Response

json
{
  "meta": { "trace": "c_4f2a…", "count": 100, "next_before": 1780990000 },
  "data": [ /* spread events, oldest-first within the page; history rows carry fired_at + is_stale */ ]
}

WS live

Filtered live event stream over WebSocket.

WS/v1/spreads/live

Tier: Builder (OPEN events min delay ≥30s) · Pro (live)

Spread OPEN frames are delayed by at least 30 seconds on Builder. Pro permits live delivery without a plan-imposed delay.

Query parameters

ParameterTypeDescription
min_pctfloatMinimum spread percent.
spread_typestringSpread type filter.
allowed_exchangescsvLeg allowlist.
dexboolDEX-leg gating.
exclude_symbolstringPer-connection blacklist.
eventsstringopen | close.
delayint (s)Hide events fresher than N seconds. Your tier clamps the minimum (see Access tiers).

Response

json
// Event frames are typed; the curated event is nested under "data":
{ "type": "event", "family": "spreads", "received_at": 1781000000.0,
  "data": { "event": "open", "symbol": "ZK_USDT", "spread_type": "spot/futures", "spread_pct": 36.33,
            "legs": { "buy": { "venue": "mexc", "market": "spot" }, "sell": { "venue": "bybit", "market": "futures" } } } }
// First frame:  { "type": "welcome", "family": "spreads", "delay_seconds": 60, "filters": { … }, "trace": "c_4f2a…" }
// Heartbeats:   { "type": "ping" }

Event fields

Payloads are curated — only the fields below are exposed. New upstream fields never leak by default (allowlist policy). Every response carries a per-consumer trace watermark.

ParameterTypeDescription
eventstringopen | close — the lifecycle discriminator (recent/live have no is_stale field).
symbolstringCanonical symbol, e.g. BTC_USDT.
spread_typestringLeg-kind pair: futures/futures · spot/futures · spot/spot · dex/futures · spot/dex · dex/dex · internal/futures.
spread_pctfloat|p_a − p_b| / min(p_a, p_b) × 100.
legs.buy / legs.sellobjectSelf-contained cheaper / dearer leg. Everything about a side lives here — no flat buy_*/sell_* fields.
legs.{buy,sell}.venuestringLowercase venue slug of the leg — e.g. binance, mexc, dex-ethereum.
legs.{buy,sell}.marketstringLeg market kind — spot | futures | dex.
legs.{buy,sell}.pricefloatLeg price at detection.
legs.{buy,sell}.mark_pricefloat?Mark price — futures legs on open events; null otherwise.
legs.{buy,sell}.venue_symbolstring?Exchange-native symbol when it differs from the canonical symbol (cross-alias).
legs.{buy,sell}.dexobject?Pool address / chain / liquidity when the leg is DEX (market === "dex").
qualityobjectCross-leg annotations: volatility_zscore, is_anomalous_move, consensus_price, buy_price_vs_consensus_pct, legs_time_skew_ms. (single_venue_suspect was removed from the wire 2026-07-04 — degenerate signal.)
stale_since_msint?Close events only — when the spread fell below threshold.
received_atfloatServer receive timestamp — on recent + live frames.
fired_atintDetection timestamp — history rows only (the keyset cursor).