GET /v1/stream pushes events as they are recorded, as Server-Sent Events over one keyed HTTP connection. Every data event has an id; reconnect with the last id you saw and the stream replays what you missed, in order, then continues live. Authenticate as for any request. A browser EventSource cannot send the key header, so connect from a server.
Topics
topics is required: a comma-separated list of what to receive.
Filters
relations_truncated is sent to every relations stream whatever the filters. An unknown topic, a malformed market or a cursor that is not an id is 400 invalid_parameter.
The connection
The response opens with aretry line and a ready event:
Streams are not permanent. Each one ends itself after 45 to 55 minutes (the exact moment is in
ready, jittered so a deploy’s reconnects do not all return at once), and sooner when the server restarts or you fall a full buffer behind. Each ending is announced:
Reconnect after the
retry interval, 3 seconds, backing off on errors.
Ids and resuming
Every data event carries anid. An id is a decimal outbox position, or position-index for one of the many events a single graph run expands into:
Last-Event-ID header (what every SSE client library does on reconnect) or as cursor, with the same filters: events after it are replayed in order, then the stream continues live with nothing missed or repeated. Replay is served from the outbox, so a burst of 40,000 replayed quotes takes a few seconds.
A cursor from the last hour always resumes. An older cursor, or one from the future, gets a reset event after ready, and the stream continues from now:
reset, rebuild your state from the REST endpoints (/v1/opportunities, /v1/markets/{venue}/{id}/quotes, and so on), then carry on with the ids that follow.
Event shapes
Every event’sdata is one JSON object. Prices and money are decimal strings in USD; times are RFC 3339 in UTC. recorded_at on every data event is when Routeur wrote it, which is the order events are delivered in.
quote
yes_ask_usd and no_ask_usd are each omitted when that side has no ask, as here where nothing is offered on No. observed_at is when the crawl read the venue.
trade
book
GET /v1/markets/{venue}/{id}/book: yes_bid_usd/yes_bid_quantity and yes_ask_usd/yes_ask_quantity (each pair omitted when that side is empty), mid_usd, microprice_usd, spread_usd, the USD within 5¢ of each side’s best bid, imbalance, Kalshi’s seq, the venue’s venue_ts, and observed_at, when the change was received. Deeper changes that leave the top alone are not streamed.
relation
left. proof_path is where to open the proof trail. A run that finds more than 10,000 new relations, which happens when a new graph version relates everything anew, sends one relations_truncated event instead, with graph_run, new_relations, limit and a message pointing at /v1/cross-venue and /v1/markets/{venue}/{id}/relations.
lead
GET /v1/opportunities (see Leads), including depth where the run sized it against the books. Every lead of the run is sent, not only new ones, so a lead that persists arrives once an hour; key on the pair and legs to tell a repeat from a change.
The
quote, trade, book, rule_change, ready, reset and reconnect samples on this page were captured from the stream. No graph run finished during the capture, so the relation sample is an observed proof as the stream carries it, and the lead sample is the reference example from the API spec.rule_change
scope is contract, series, event, market or document; rulebook_id names the rulebook for the last four. from_hash and to_hash are rules hashes, rulebook content hashes or document hashes by scope. rules_path is where to read the versions and diffs.
error
reconnect. Reconnect after the quota resets at midnight UTC.
Limits and usage
Opening a stream counts as one request against the per-minute limit and the daily quota, and an open stream counts one more request per minute against the daily quota while it is open, so a stream held all day costs about 1,440 requests. Replayed events are not counted individually.
A key over its concurrent streams gets
429 too_many_streams with Retry-After: 60; close a stream first. When no stream capacity is free on the instance reached, or it is shutting down, the answer is 503 stream_unavailable with Retry-After: 5.
A minimal resumable client
Both clients below keep the last id, reconnect on every ending with it, resync onreset, and treat 45 seconds of silence as a dead connection. Neither depends on an SSE library.
Related
- Order books: what a
bookevent is the top of. - Rules: what a
rule_changepoints at. - Leads and Relations: what
leadandrelationevents carry. - Rate limits: the per-minute limits and daily quotas that streams count against.
- Webhooks: to be called when a lead becomes executable, without holding a connection.