Skip to main content
Every gRPC stream you open will close, and what you do in the next second decides whether you lose events or double-process them.
Before this page: a working subscription on at least one stream — Drop copy, Position change — and the caps from Choose a stream.
API reference: Stream error handling. Opens on the public documentation site in a new tab. Where it disagrees with this page, this page is authoritative for the partner surface.

The session model

A subscription session is keyed on caller identity: the token subject plus the participant header. It is not keyed on the accounts or the symbols you requested. That single fact explains the class of failure partners spend weeks on. Two processes using the same client credentials and the same x-participant-id are, to the subscription manager, the same session — not two sessions that happen to want different symbols. Nine partners independently investigated this. Give every long-lived subscriber its own distinguishable caller identity where you can, and do not assume that requesting different symbols isolates two subscribers from each other.

13 INTERNAL: Subscription manager revoked session

Known workaround: pass an explicit list of markets on CreateOrderSubscription. There is no symbol cap on that path, so naming your markets explicitly does not trade one limit for another. This has been shared one partner at a time; it is not in the published docs.
Not yet published. What is confirmed is the keying — token subject plus participant header — and the explicit-market-list workaround. Anything beyond that, including whether a second subscription under the same caller identity always revokes the first, is unconfirmed. Ask your integration lead before you design around a specific trigger.
Treat 13 INTERNAL as terminal for that session and immediately retryable as a new subscribe. Do not treat it as a transport blip to be retried on the same call object.

The timeouts that actually apply

Whether a stream that was authorized with a token outlives that token’s expiry is not published.Until it is confirmed, keep refreshing on the expires_in minus 30-second schedule and be ready for a close at any point — the reconnect wrapper below mints a fresh token on every attempt, which is correct either way.

Reconnect cadence and backoff

1

Reconnect immediately on the first close

A 10-minute ALB close is expected operation, not a fault. Re-subscribe at once with a freshly minted token.
2

Back off exponentially on repeated failures

From 1 second, doubling to a 30-second ceiling, with full jitter. Keep the ceiling under the 10-minute connection life so a recovering stream has time to do useful work.
3

Never reconnect faster than once per second per stream

StreamRFQEvents is capped at 1 open/sec per firm — the only published open-rate number. Hold every stream to that rate so a reconnect storm cannot become a rate-limit incident.
4

Cap concurrent attempts at your stream budget

You have 20 concurrent streams per firm across all gRPC subscriptions. A reconnect loop that opens before the old stream is fully closed can spend budget you have already allocated.
5

Stop and alert on a non-transient status

PERMISSION_DENIED (missing scope grant), Aborted (a suppressed ledger entry type) and InvalidArgument will not fix themselves. Retrying them burns budget and hides the defect.
Not yet published. No reconnect budget is published, and whether opening a stream draws on the per-firm unary budget (250 requests / 60 s in cluster configuration) is an open gap. The cadence above is client-side guidance chosen to stay inside the numbers that are published — confirm it before you run hundreds of subscribers.

Gap recovery, per stream

Two rules cover all five: Snapshot then delta. Subscribe delivers current state first and changes after. Do not apply deltas to a state you built before the disconnect. At-least-once, so dedupe. Redelivery is expected on every stream. Every handler must be idempotent, keyed on the event’s own identifier, never on arrival order.

Sharding past 1000 instruments

Work the arithmetic before you write the subscriber.
You do not get all 20. A realistic ISV allocation:
Above roughly 17,000 instruments, this does not fit, and there is no published sharding pattern for it. Say so to your integration lead rather than working around it: one partner ran 7,372 subscriptions before being told the cap existed and had to re-architect in production.Do not reach for an empty symbols list. That subscribes to all instruments and collides with the same 1000-per-stream cap.
What you can do today, inside the caps:
1

Shard by symbol hash into fixed buckets

Assign each symbol to bucket hash(symbol) mod N with N ≤ 17 and ≤1000 symbols per bucket. Fixed buckets mean a reconnect re-subscribes the same symbol set, so your dedupe keys stay stable.
2

Subscribe to what you actually trade, not the universe

Market data streams are the only cap-bound-by-count subscription. Drive the tracked set from open positions and live orders, and drop symbols you have no exposure to.
3

Use position change for exposure, not market data

Position change is position-driven and needs no symbol list at all, so exposure monitoring costs you one stream regardless of instrument count.

A reconnect wrapper

Mint a fresh token per attempt, persist the resume token as you go, and let non-transient statuses out.

What can go wrong

Next

Settlement