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 samex-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
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.
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.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.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.