BrainResearchHub
Independent research on prediction markets, perpetual futures, and trading infrastructure

Why Does the Polymarket API Return Auth Errors? Clock Skew and Stale Timestamps

Ahmed Trading· sdk · Updated October 2026

The most maddening API bug class: orders that work for hours, then randomly fail with “invalid signature” — same code, same keys. Nine times out of ten it’s clock skew or salt reuse. Signed requests carry a timestamp and a salt; the engine rejects stale timestamps and duplicated salts hard, and your system clock drifts whether you like it or not. Here’s the diagnosis and the fix patterns.

Why do my Polymarket API requests fail with signature errors?

Each signed request must include a fresh ts timestamp in milliseconds and a salt; reused or stale values are rejected. Two failure modes: clock skew — your machine’s clock drifts from the server’s, so “now” by your clock is “past” or “future” by theirs; and salt reuse — generating the same salt twice (common with bad randomness or per-second counters under concurrency). The optional expa field caps the validity window — set carelessly, it amplifies both. The signing mechanics are in authentication; the error-handling layer in the bot architecture.

The server-time sync pattern

Never trust your local clock for request signing. The pattern: sync against GET /v1/info/time (the server’s own clock) — on startup, periodically (drift accumulates), and after any long GC pause or machine sleep. Compute an offset (server_time − local_time at fetch) and sign with local_time + offset. Log the offset over time: growing offset = your clock source is drifting (NTP broken in the container? VM paused?) — fix the cause, the offset patch is a mitigation. Bots on VPS instances and containers hit this constantly; the deployment checklist includes time-sync verification for a reason.

Salt hygiene under concurrency

The salt must be unique per request. Patterns that break under load: int(time()) as salt (two requests in the same second collide), weak randomness without seeding, and parallel threads drawing from a non-thread-safe generator. The fix: a proper UUID/secure-random per request, or a monotonic counter combined with randomness. Concurrent order placement (see the execution layer) makes this a production-day-one issue, not a theoretical one.

Reading the error signatures

FAQ

Why do my Polymarket API requests randomly fail with invalid signature? Almost always clock skew or salt reuse: signed requests need fresh timestamps and unique salts. Sync against the server time endpoint with an offset, generate proper unique salts per request — the patterns are above.

How do I sync my bot’s clock with Polymarket’s server? Fetch GET /v1/info/time, compute the offset from your local clock, sign requests with local time + offset — re-sync on startup, on schedule, and on every reconnect.

What is the salt in API request signing? A uniqueness nonce per signed request — the engine rejects duplicates. Use secure random or a monotonic+random construction; never a per-second timestamp, which collides under concurrent orders.

What is the expa field in Polymarket API auth? An optional validity-window cap on signed requests — it bounds how long a signature is acceptable. Set it generously enough for network latency but tight enough to limit replay; see the auth mechanics.

Do rate-limit errors and signature errors look the same? No — rate limits return 429 with clear headers; signature/clock failures reject the request itself. If rejections cluster after idle periods or on bursts specifically, suspect skew or salt first: see rate-limit patterns for the other half.

Educational content, not financial advice. Not affiliated with Polymarket. Last updated October 2026.