What would you like the SDK to support?
Package: polymarket-client 0.2.0
Area: transport configuration / connection lifecycle
Summary
TransportOptions (in polymarket/clients/_transport.py) already supports configuring http2, limits, timeout, and event_hooks, and both SyncTransport / AsyncTransport accept an injected httpx client. However, none of this is reachable from the public constructors — AsyncSecureClient.create() builds its transports with hardcoded defaults. As a result there is no supported way to:
- disable HTTP/2 or tune connection limits / keepalive,
- attach
event_hooks for request/response observability,
- inject a pre-configured
httpx.AsyncClient (custom transport, proxies, instrumentation).
Why this matters: HTTP/2 GOAWAY vs. non-idempotent requests
The CLOB edge recycles every HTTP/2 connection after ~10,000 requests with a graceful GOAWAY. A long-running trading service (SDK client kept alive across cycles, as recommended for connection reuse) hits this cap regularly. Requests racing with the GOAWAY surface as:
polymarket.errors.TransportError: <ConnectionTerminated error_code:0, last_stream_id:19999, additional_data:None>
(last_stream_id: 19999 = the 10,000th client-initiated stream, so this is deterministic, not network flakiness.)
For idempotent calls this is retryable. For post_orders it is not safely retryable from application code: httpcore raises the identical exception both for streams the server never processed (opened after GOAWAY was seen) and for in-flight streams at or below last_stream_id, which the server will still process. A blind retry can therefore double-place orders, and the information needed to distinguish the two cases is lost by the time the exception reaches SDK users.
With TransportOptions exposed, users could resolve this cleanly on their side, e.g.:
http2=False: HTTP/1.1 keep-alive connections are recycled via Connection: close, which httpx handles transparently — this failure mode disappears entirely; or
- inject an httpx client with a custom connection pool that proactively expires connections before the ~10k cap (Envoy-style
max_requests_per_connection).
Current workaround
We reach through private attributes (client._ctx.secure_clob._client._transport._pool) to close idle connections whose _request_count approaches the cap before each trading cycle. It works, but it is coupled to httpcore internals and will silently break on refactors.
Proposed API
Any of these would work (first is probably minimal):
client = await AsyncSecureClient.create(
private_key=...,
credentials=...,
transport_options=TransportOptions(http2=False, limits=...), # applied to all transports
)
or accept per-service overrides / a pre-built httpx.AsyncClient, mirroring what AsyncTransport.__init__ already supports.
Why do you need this?
We run a long-lived trading service that keeps one AsyncSecureClient alive across decision cycles for connection reuse. The CLOB edge (Cloudflare) recycles every HTTP/2 connection after ~10,000 requests with a graceful GOAWAY, so a few times a day in-flight requests fail with:
polymarket.errors.TransportError: <ConnectionTerminated error_code:0, last_stream_id:19999, additional_data:None>
For reads this is retryable, but for post_orders it is not safely retryable from application code: httpcore raises the identical exception both for streams the server never processed and for in-flight streams it will still process, so a blind retry risks double-placing orders. The clean fixes are all transport-level — disable HTTP/2 (HTTP/1.1 recycling via Connection: close is handled transparently by httpx), or inject an httpx client whose pool expires connections before the cap. TransportOptions and client injection already exist in _transport.py; they're just not reachable from AsyncSecureClient.create(). Today we work around it by reaching through private attributes (client._ctx.secure_clob._client._transport._pool) to proactively close near-cap connections, which is coupled to httpcore internals and will break silently on refactors.
Example API or additional context
What would you like the SDK to support?
Package:
polymarket-client0.2.0Area: transport configuration / connection lifecycle
Summary
TransportOptions(inpolymarket/clients/_transport.py) already supports configuringhttp2,limits,timeout, andevent_hooks, and bothSyncTransport/AsyncTransportaccept an injectedhttpxclient. However, none of this is reachable from the public constructors —AsyncSecureClient.create()builds its transports with hardcoded defaults. As a result there is no supported way to:event_hooksfor request/response observability,httpx.AsyncClient(custom transport, proxies, instrumentation).Why this matters: HTTP/2 GOAWAY vs. non-idempotent requests
The CLOB edge recycles every HTTP/2 connection after ~10,000 requests with a graceful GOAWAY. A long-running trading service (SDK client kept alive across cycles, as recommended for connection reuse) hits this cap regularly. Requests racing with the GOAWAY surface as:
(
last_stream_id: 19999= the 10,000th client-initiated stream, so this is deterministic, not network flakiness.)For idempotent calls this is retryable. For
post_ordersit is not safely retryable from application code: httpcore raises the identical exception both for streams the server never processed (opened after GOAWAY was seen) and for in-flight streams at or belowlast_stream_id, which the server will still process. A blind retry can therefore double-place orders, and the information needed to distinguish the two cases is lost by the time the exception reaches SDK users.With
TransportOptionsexposed, users could resolve this cleanly on their side, e.g.:http2=False: HTTP/1.1 keep-alive connections are recycled viaConnection: close, which httpx handles transparently — this failure mode disappears entirely; ormax_requests_per_connection).Current workaround
We reach through private attributes (
client._ctx.secure_clob._client._transport._pool) to close idle connections whose_request_countapproaches the cap before each trading cycle. It works, but it is coupled to httpcore internals and will silently break on refactors.Proposed API
Any of these would work (first is probably minimal):
or accept per-service overrides / a pre-built
httpx.AsyncClient, mirroring whatAsyncTransport.__init__already supports.Why do you need this?
We run a long-lived trading service that keeps one AsyncSecureClient alive across decision cycles for connection reuse. The CLOB edge (Cloudflare) recycles every HTTP/2 connection after ~10,000 requests with a graceful GOAWAY, so a few times a day in-flight requests fail with:
polymarket.errors.TransportError: <ConnectionTerminated error_code:0, last_stream_id:19999, additional_data:None>
For reads this is retryable, but for post_orders it is not safely retryable from application code: httpcore raises the identical exception both for streams the server never processed and for in-flight streams it will still process, so a blind retry risks double-placing orders. The clean fixes are all transport-level — disable HTTP/2 (HTTP/1.1 recycling via Connection: close is handled transparently by httpx), or inject an httpx client whose pool expires connections before the cap. TransportOptions and client injection already exist in _transport.py; they're just not reachable from AsyncSecureClient.create(). Today we work around it by reaching through private attributes (client._ctx.secure_clob._client._transport._pool) to proactively close near-cap connections, which is coupled to httpcore internals and will break silently on refactors.
Example API or additional context