Polymarket’s SDK lineage moved again. The legacy @polymarket/clob-client repo was archived on May 25, 2026 — its README now points developers to the new TypeScript SDK — and Polymarket is consolidating onto two unified beta SDKs:
py-sdk— published to PyPI aspolymarket-client(beta:pip install --pre polymarket-client)ts-sdk— published to npm as@polymarket/client(beta0.1.0-beta.2:npm install @polymarket/client@beta)
Both are explicitly beta, and in the week of May 26–28, 2026 eight bugs were filed across the two repos. They cluster around the same problems the transitional py-clob-client-v2 line hit after the CLOB V2 cutover — above all, POLY_1271 deposit-wallet authentication. This brief documents each open issue with the exact symptom and a workaround. It complements our py-clob-client-v2 bug tracker, which covers the previous SDK generation.
Every issue below was open as of June 1, 2026, with no maintainer fix merged. Both SDKs are pre-1.0 — pin a version and expect churn.
POLY_1271 deposit-wallet auth is broken in both new SDKs
This is the dominant cluster, and it is the same defect we tracked against py-clob-client-v2, now carried into the unified SDKs.
New-style accounts (Magic/email and MetaMask deposit wallets) must trade through a deposit wallet with signature_type=3 (POLY_1271, a contract-wallet signature verified via EIP-1271). The problem is in L1 authentication — the step that creates or derives your API key. Both SDKs sign the ClobAuth message with the bare EOA signer.address, ignoring the deposit-wallet shape:
- py-sdk #55 —
sign_api_key_authusessigner.addressforClobAuth, ignoring the POLY_1271 deposit-wallet shape. - ts-sdk #73 —
beginAuthenticationsignsClobAuthwith the EOA address, the same way.
Because the API key is bound to the EOA, the later order — signed as the deposit wallet — no longer matches the key. The CLOB rejects it:
HTTP 400: {"error":"the order signer address has to be the address of the API KEY"}
The root cause was first documented in clob-client-v2 #65 — createApiKey() / create_or_derive_api_key() don’t EIP-1271-wrap the L1 auth headers for POLY_1271 wallets — and reproduced in py-clob-client-v2 #64 and #71. The Rust SDK is the only client that wraps the authentication step correctly today.
Workaround: Derive your API credentials from the deposit-wallet context (not the bare EOA), and confirm the deposit wallet is deployed, funded, and approved. If you need POLY_1271 today, trade through the Rust SDK until a fix lands, or — if your account allows it — use an EOA / Proxy / Gnosis Safe signature type that doesn’t depend on the broken EIP-1271 wrapping. See the POLY_1271 smart-contract wallet guide and auth troubleshooting for the full credential-derivation flow.
Builder-fee lookups crash market-order construction
Both SDKs fetch builder-fee rates while assembling a market order, but neither handles a 404 for an unregistered builder code — so the exception propagates and takes down order construction instead of degrading gracefully.
- py-sdk #56 —
fetch_builder_fee_rateslacks 404 handling; market-order construction crashes on an unregistered builder code. - ts-sdk #74 —
fetchBuilderFeeRateslacks 404 handling;prepareMarketOrderwith an unregisteredbuilderCodecrashes instead of degrading.
The fee endpoint returns a plain 404 ({"error":"builder code not found"}) for any code you haven’t registered, and the SDK turns that into an unhandled error.
Workaround: Don’t pass a builder code you haven’t registered. Where you do use one, guard the lookup, catch the 404, and build the order without a builder fee:
# py-sdk #56: fetch_builder_fee_rates raises on a 404 for an unregistered code,
# which crashes market-order construction. Degrade instead of dying.
def safe_builder_fee(client, builder_code):
try:
return client.fetch_builder_fee_rates(builder_code)
except Exception as e: # narrow to the SDK's request-error type once exposed
msg = str(e).lower()
if "not found" in msg or "404" in msg:
return None # build the order without a builder fee
raise
Gasless wallet creation has no retry loop
- py-sdk #61 —
submit_deposit_wallet_create/_syncimportGASLESS_SUBMIT_RETRY_ATTEMPTSbut never loop on it, so a single transient relayer error aborts wallet creation on the first failure.
Workaround: Wrap the call in your own exponential-backoff retry. Wallet creation is idempotent here — there’s no nonce to burn — so retrying is safe:
# py-sdk #61: the built-in retry constant is imported but unused. Retry yourself.
import time
def create_deposit_wallet_with_retry(client, attempts=5):
for i in range(attempts):
try:
return client.submit_deposit_wallet_create()
except Exception:
if i == attempts - 1:
raise
time.sleep(2 ** i) # exponential backoff
See the Polymarket rate-limits guide for backoff patterns that also respect 429s.
Trade timestamps are parsed in the wrong unit (silent data corruption)
- ts-sdk #76 —
ClobTradeSchema.match_timeandlast_updateare typed as epoch-milliseconds, but the/data/tradesREST API returns epoch-seconds strings.
This one is dangerous precisely because it’s silent: nothing throws. Parsing a seconds value as milliseconds just produces a date in 1970. For example, the raw value 1710000000 (March 2024) parsed as milliseconds resolves to 1970-01-20T.... Any agent that keys off trade time — P&L windows, “trades since,” dedup by timestamp — will be quietly wrong.
Workaround: Normalize seconds-vs-milliseconds before you trust a trade timestamp:
// ts-sdk #76: /data/trades sends epoch-SECONDS, but the schema expects ms.
// Normalize before constructing a Date.
function toMillis(ts: string | number): number {
const n = Number(ts);
return n < 1e12 ? n * 1000 : n; // seconds -> ms; leave real ms alone
}
// new Date(toMillis("1710000000")) -> 2024-03-09T... (not 1970)
OrderBook timestamps skip the strict validator
- py-sdk #63 —
OrderBook.timestampis parsed withint()instead of the strictEpochMsTimestamptype, bypassing the whitespace-rejection guard the other models use.
Lower severity than the others, but it means a malformed or padded timestamp the rest of the SDK would reject slips through on the order book. Compare the REST /book payload against the WebSocket book event and you can see the inconsistency.
Workaround: If you depend on book timestamps, validate them strictly yourself:
# py-sdk #63: OrderBook.timestamp uses int(), skipping the strict guard.
def epoch_ms(value: str) -> int:
if not value.isdigit(): # rejects " 123", "123 ", "1.2e3", etc.
raise ValueError(f"non-epoch-ms timestamp: {value!r}")
return int(value)
See the Polymarket WebSocket guide for reconciling REST and stream payloads.
createSecureClient rejects nonce: 0
- ts-sdk #67 —
createSecureClientrejectsnonce: 0even though0is documented as the default. The nonce schema is validated as positive-only, so the documented default value is itself rejected.
Workaround: Omit the nonce field when you mean zero — the default applies:
import { createSecureClient } from "@polymarket/client";
// ts-sdk #67: nonce is validated positive-only, so nonce: 0 throws.
const client = await createSecureClient({ signer }); // nonce defaults to 0
// NOT: createSecureClient({ signer, nonce: 0 }) -> validation error
This is a developer-experience paper cut, not a fund-safety problem.
Defensive patterns for beta SDKs
Until these land fixes, treat both SDKs as pre-1.0 and code defensively:
- Verify deposit-wallet auth before you trade. Place one tiny order on a test market and confirm it isn’t rejected with
the order signer address has to be the address of the API KEYbefore running any real strategy. - Wrap the fragile paths. Guard builder-fee lookups (catch the 404), wallet creation (retry with backoff), and any timestamp you read from
/data/trades(normalize seconds → ms). - Don’t trust silent values. The timestamp bug throws nothing — add an assertion that parsed trade dates fall in a sane range.
- Pin versions.
polymarket-clientneeds--pre, and the npmlatesttag is a canary build, not the beta — install@polymarket/client@betaexplicitly. - Keep the Rust SDK in your back pocket for POLY_1271 deposit-wallet flows, which it handles correctly today.
Summary table
| Issue | SDK | Symptom | Workaround |
|---|---|---|---|
| #55 / #73 | py & ts | POLY_1271 deposit-wallet orders rejected: signer address has to be the address of the API KEY | Derive creds from the deposit-wallet context; use the Rust SDK until fixed |
| #56 / #74 | py & ts | Builder-fee 404 crashes market-order construction | Catch the 404 and degrade; don’t pass unregistered builder codes |
| #61 | py | Gasless wallet creation has no retry loop | Wrap create in your own backoff retry (idempotent) |
| #76 | ts | Trade timestamps parsed as ms but API sends seconds (1970 dates) | Normalize seconds → ms before use |
| #63 | py | OrderBook.timestamp skips the strict epoch-ms guard | Validate book timestamps yourself (digits-only) |
| #67 | ts | createSecureClient rejects nonce: 0 | Omit nonce to get the zero default |
Further reading
- py-clob-client-v2 Bug Tracker (April 2026) — the previous SDK generation’s open issues
- Top 10 Polymarket API Problems — the recurring integration failures, conceptually
- py-clob-client-v2 Errors — decode the V2 transitional client’s error messages
- Polymarket CLOB V2 Migration — what changed at the April 28, 2026 cutover
- POLY_1271 Smart-Contract Wallets — how deposit-wallet signing is supposed to work
- Polymarket Auth Troubleshooting — fix POLY header and signature errors
- Polymarket TypeScript SDK Reference — the ts-sdk method surface
- Polymarket Rust SDK Reference — the client that handles POLY_1271 auth correctly
- py-clob-client Reference — Python client methods and known issues
- Polymarket API Tutorial — full API walkthrough
- Polymarket Rate Limits Guide — handle 429s when retrying
- Polymarket WebSocket Guide — reconcile REST and stream payloads
