The operating pattern
Streams are the system of record; reads are anchors. The firm-scoped Drop Copy execution and position streams drive your projection in real time. Unary reads exist for discovery, user-driven views, and reconciliation — they are not polling feeds.- Replay before new work. Persist a stream response’s
resume_tokenonly after every record in it is durably applied. Reconnect with the last committed token; deduplicate replayed events by stable exchange IDs. - Single active subscriber. Run one leader-elected consumer per firm, environment, and stream. Followers take over from the durable cursor — never one subscription per pod, and never one stream per customer.
- Reconcile after gaps. After an outage: resume and replay the streams first, then compare your projections against the reads below, and surface freshness caveats rather than presenting incomplete state as final.
- No cron pollers. Reads are triggered by onboarding, user views, bounded workflow recovery, reconnect reconciliation, or an incident — not recurring schedules.
A process restart is not a cold start. Resume from your persisted checkpoint against your existing local database — don’t re-run the unary reads below.
resume_token or resume_time, and no local projection to reconcile against. If you already persist your checkpoint, a restart just reconnects with it and keeps applying deltas to the state you already have, deduplicating anything replayed. Only a genuine no-checkpoint start (or a checkpoint too old to resume) falls back to the reads below.
Reconciliation reads
The anchors are the standard gRPC read APIs — the samepolymarket.v1 services documented under Institutional API, called with your partner credentials over TLS with a Bearer token. Full message definitions are in the proto download; the tables below map each reconciliation concern to its RPC.
The correct architecture is stream-first: build your local database from Drop Copy and Order Stream, serve reads from that database, and call these RPCs only for cold-start hydration, point-in-time queries, and gap recovery — never as a substitute for a subscription. See Rate Limits for the full list; SearchOrders, SearchExecutions, and SearchTrades, for example, are capped at 12 requests/minute, and sustained call volume against any of these endpoints will get throttled.
Cash balances — polymarket.v1.PositionAPI
An account balance is the authoritative cash figure for one account and currency, plus the risk context used to size orders — see Balance Data for the full field reference.
This balance read is the authoritative confirmation to take before initiating a
WITHDRAWAL transfer — confirm the participant’s free cash covers the amount (net of accrued vendor fees) before creating the transfer.
Stream it instead
- Apply
CreateBalanceLedgerSubscriptionentries (before_balance/after_balance) to your projection as they arrive. - Reserve
GetAccountBalance/ListAccountBalancesfor a true first-time cold start and the pre-withdrawal confirmation above. - On an ordinary restart, resume from the last
update_timeyou persisted and keep applying deltas to the balance you already have — don’t re-fetch it.
Positions — polymarket.v1.PositionAPI
A position tracks net quantity, cost basis, and realized P&L for one account and symbol — see Position Data for the full field reference.
Stream it instead
- Call
ListAccountPositionsto hydrate a brand-new projection (true cold start) or to answer point-in-time (as_of_time/as_of_date) queries a stream can’t answer — not on every process restart. - A restarting service persists its position projection alongside the stream’s
resume_tokenand reconnects with that token; a routine restart is never a reason to re-fetch positions.
Orders — polymarket.v1.ReportAPI
An order carries its full lifecycle state — quantity, price, fills, and status — see Order Message Structure for the full field reference (the same fields these reads return).
Stream it instead
- Open with the default
snapshot_only: false; the first message carries asnapshotof every currently-open order, thenupdatemessages push executions continuously. - This request carries no
resume_token, so a restart simply reconnects and gets a fresh snapshot for free — there’s no checkpoint to persist for this one. - Reserve
SearchOrders/GetOrderfor one-off lookups the live snapshot doesn’t cover (e.g. a specific historical/closed order by ID). - Drop Copy’s execution and Trade Capture Report subscriptions, by contrast, carry a
resume_tokenand no snapshot — hydrate firm-wide historical fills once viaSearchExecutions/SearchTradeson true cold start, then resume the stream with the persisted token on every subsequent restart instead of re-querying.
Cash ledger — polymarket.v1.FundingAPI
A ledger entry records a single cash-balance change with its before/after amounts — see BalanceLedgerEntry Fields for the full field reference.
The cash ledger is where every transfer (deposits, withdrawals, vendor fee collections), fill, exchange fee, and settlement credit lands with before/after balances — it is the platform-side record your funding entity’s books reconcile against, per account.
Stream it instead
- Use
GetAccountBalanceLedgeronly for paginated historical lookups and CSV exports, not as a live feed. - Persist the
update_timeof the last applied entry and pass it back asresume_timeon reconnect — a restart resumes the stream from that point, it does not re-run history from the beginning.
Firm-wide ledger consumption
The ledger subscription is per-account — one fully-qualified account per stream — and concurrent ledger streams per firm are tightly capped. Opening one stream per customer does not scale and is not the intended pattern.COMING SOON — firm-wide balance ledger subscription. A firm-scoped subscription that delivers ledger entries for all accounts under your Firm on a single stream — with the same
resume_time replay semantics and entry.account on every row for routing — is planned. The final consumption pattern will be documented when it ships; contact institutional@polymarket.us if your rollout depends on it.- Executions and positions: the Drop Copy streams are firm-wide — one execution stream and one position stream cover every account’s trading activity.
- Cash events: drive per-account cash projections from your own transfer records (you initiate every deposit, withdrawal, and fee collection) plus firm-wide execution data, and reconcile against
GetAccountBalanceLedger/ CSV export per account on a batch cadence — not with per-account live streams.
Decoding fees on fills. Commission fields on execution reports are fixed-point notional units, scaled by both
price_scale and fractional_quantity_scale — see Fees on execution reports before reconciling fill-level fees against the fee schedule.Joining orders to fees and funding. For orders placed through the partner order API, the
clord_id on execution streams is the partner-assigned order.clord_id from CreateVendorOrder, and the execution account is the same DCM identifier supplied as order.account. SearchOrders filters by clord_id directly. Keep the exchange order_id as the canonical venue identifier: it and your clord_id are the join keys on the Vendor Fees report. Persist the create response’s correlation fields so ledger rows, stream events, fee records, and transfers all join cleanly.Related pages
Transfers
The cash-movement API these reads gate.
Drop Copy Stream
The firm-scoped execution stream that drives your projection.
Order Stream
Real-time order and execution updates for your accounts.
Rate Limits
Per-endpoint limits for the reads on this page.