What you’ll get
A consumer forGET /v1/stream that survives everything the stream does on purpose and by accident: it saves the last event id and resumes from it, treats 45 seconds of silence as a dead connection, reopens when the server sends reconnect, backs off on errors, and waits out 429 and 503. Stopped and restarted, it picks up where it left off with nothing missed or repeated. On 2026-09-17 the trades topic delivered 6,575 fills in 12 seconds.
Prerequisites
- An API key in
ROUTEUR_API_KEY - Python 3 with
requests, or Node 18+ (nativefetchandTextDecoderStream) - A server to run it on. A browser
EventSourcecannot send the key header.
1
See the raw stream once
ready then names the cursor it resumed from:reset and the stream continues from now:2
The consumer
Both versions do the same things: parse SSE by hand (
id:, event:, data:, and : comment lines as heartbeats), write every event id to a file so a restart resumes, cut the connection after 45 seconds without bytes, reopen on reconnect with the cursor it carries, sleep out Retry-After on 429 and 503, and double the wait on connection errors up to a minute.3
Stop it and start it again
Run it, stop it after a few seconds, and run it again. The second The Node consumer, run for 90 seconds and then for 10, observed 2026-09-17 13:14 UTC. The second run replayed the 7,600 fills recorded during the three seconds it was down and the batch that landed while it ran:When the key is out of stream slots, the consumer waits rather than crashing:
ready names the cursor from the file, and the first events are the ones recorded while it was down.The Python consumer, run for 12 seconds and then for 8, observed 2026-09-17 13:11 UTC. The first run caught a trade batch and the 32 leads of graph run 75, 8,314 events in all; the second resumed from the last id and found nothing new in its 8 seconds:Read the result
Pitfalls
- Stream slots outlive the client. A key holds one stream per 60 requests a minute of its limit (10 on the pro tier). On 2026-09-17, ten streams closed from the client side (with
curl --max-time, or a killed process) between 12:41 and 12:53 UTC were all still counted at 13:09, every new connection got429 too_many_streamswithRetry-After: 60, and the first slot came back at 13:11, about 30 minutes after the stream that held it was abandoned. Do not open a fresh stream per retry; keep one consumer, let it reconnect, and expect a wait after a crash loop. - Fills come in bursts, with quiet in between. Trades are recorded in batches: 8,741 fills arrived in eight seconds at 13:14:14 UTC on 2026-09-17, and nothing for the two and a half minutes before. A consumer that sees only
readyfor a minute is not broken. Write the cursor after each event and keephandlefast, or process in batches off a queue. - Same filters on resume. Resuming with different
topics,venue,marketsormin_usdfrom the ones the cursor came from replays the wrong set. Store the filters with the cursor. cursorwins overLast-Event-IDwhen both are sent, and a cursor that is not^[0-9]+(-[0-9]+)?$is a400 invalid_parameter. Never do arithmetic on ids.- Python buffers stdout when piped. Run with
python3 -u, or nothing shows until the buffer fills. This is not the stream being silent. - Booleans and lists in the query string are plain text:
topics=trades,leads, not JSON. - Silence is not the same as no events. Heartbeats prove the connection; a stream with no trades for a minute is normal on a quiet venue. Only the 45-second timeout should reconnect.
Related
- Rule-change watch: the same consumer on the
rulestopic. - Watch for unusual flow: the five-minute scored version of the trade feed.
- Rate limits: how streams count against the minute and the day.
- Webhooks: for executable leads only, delivered to you instead of pulled.
GET /v1/streamin the API reference: every topic and event schema.