All posts
API ReferencePolymarket APIDebuggingCLOB

Polymarket API Errors: Every Code, Decoded

Polymarket's error messages are terse, and the ones that break your bot at 3am are usually the ones missing from the documentation entirely. Here is the full reference — documented errors, undocumented ones, and what to actually do about each.

12 min read

Every Polymarket CLOB API error comes back in the same shape — a JSON object with a single error field — which is admirably consistent and completely unhelpful when you are trying to work out why your order just vanished.

{
  "error": "<message>"
}

This page decodes them. It covers the documented CLOB errors, the status codes and their real causes, and — the part you will not find in the official docs — the errors people actually hit in production that Polymarket has never written down.

Status code quick reference

StatusMeaningCommon causes
400Bad RequestInvalid parameters, malformed payload, business logic violation
401UnauthorizedMissing or invalid API key, bad HMAC signature, expired timestamp
404Not FoundMarket doesn't exist, order not found, token ID not recognised
425Too EarlyMatching engine is restarting — retry with backoff
429Too Many RequestsRate limit exceeded — back off exponentially
500Internal Server ErrorUnexpected server error — retry with backoff
503Service UnavailableExchange paused, or blocked by cancel-only / post-only mode

401 — authentication errors

Unauthorized/Invalid api key

The single most-searched Polymarket error. Your API key is missing, expired, or invalid. In practice it is almost never a genuinely bad key — it is one of these:

  1. You are sending only part of the credential set. Polymarket L2 auth needs the key, the secret and the passphrase together. A common failure is populating POLY_API_KEY and POLY_PASSPHRASE but leaving the secret unset — the client sends a well-formed but unsignable request.
  2. Your system clock has drifted. The signature includes a timestamp, and a clock more than a few seconds off produces a signature the server rejects as invalid. This is the cause people find last and it is depressingly common on VMs and containers.
  3. You derived the key against a different wallet than you are trading from. Keys are bound to the signer address that created them.
  4. You are hitting an authenticated endpoint with L1 headers where L2 is expected, or vice versa.

Invalid L1 Request headers

Your L1 authentication headers — the EIP-712 signature set — are malformed, or the signature does not match the address you claim to be. Check that you are signing with the same private key as the address in the header, and that your domain separator matches the chain you are on (Polygon, chain ID 137). A signature valid for the wrong chain ID is still a valid signature — just not for this server.

Could not create api key / Could not derive api key!

These are the errors behind the search "polymarket unable to generate api credentials". Both are returned from the auth endpoints when key creation or derivation fails. Could not derive api key! on GET /auth/derive-api-key usually means no key exists yet for that signer — derive returns an existing key, it does not create one. Create the key first with POST /auth/api-key, then derive it on subsequent runs.

400 — order rejection errors

These are returned when an order passes transport validation but fails business rules. They appear in the response body of POST /order and POST /orders.

Error messageWhat it means
the order signer address has to be the address of the API KEYThe key signing the order is not the key the order says signed it. You are mixing credentials — usually a stale key from a previous wallet.
the order owner has to be the owner of the API KEYThe maker address in the order does not match the address the API key belongs to.
order {id} is invalid. Price ({price}) breaks minimum tick size rule: {tick}Your price is not on the market's tick grid. Call GET /tick-size and round to it — do not assume 0.01.
order {id} is invalid. Size ({size}) lower than the minimum: {min}Order size below the market minimum.
not enough balance / allowanceInsufficient pUSD balance, or you never granted the exchange contract an allowance. Both are required.
invalid post-only order: order crosses bookYour post-only order would take liquidity immediately. Reprice so it rests.
order couldn't be fully filled. FOK orders are fully filled or killed.Fill-or-Kill found insufficient liquidity. The whole order is rejected.
no orders found to match with FAK order.Fill-and-Kill found no match at all. At least one match is required.
invalid expirationExpiration timestamp is in the past or malformed.
order {id} is invalid. Duplicated.The same order was submitted twice.
order canceled in the CTF exchange contractThe order was canceled on-chain.
'{address}' address bannedThe address is banned from trading.
'{address}' address in closed only modeThe address may only close existing positions.

425 — Too Early

An unusual status code that confuses people because it is so rarely used elsewhere on the web. On Polymarket it means the matching engine is restarting. Your request arrived during the window where the exchange is coming back up.

This one is genuinely transient and genuinely safe to retry — it is arguably the most retry-appropriate error on the platform. Back off and try again; do not treat it as a failure and do not alert on a single occurrence.

503 — the exchange is in a restricted mode

Polymarket can put the exchange into restricted modes. These are not failures on your side and no amount of retrying the same request will help until the mode lifts.

MessageWhat you can still do
Trading is currently disabled. Check polymarket.com for updatesNothing. No orders accepted, including cancels.
Trading is currently cancel-only. New orders are not accepted, but cancels are allowed.Cancel existing orders. You cannot open new ones.
post-only mode: only post-only orders and cancels are allowedCancel, or place orders with postOnly: true. Taking orders are rejected.

Post-only mode is the one to handle properly, because it tells you exactly when to come back. The response includes code: "post_only_mode" and retry_after_seconds, and the same delay is sent in the Retry-After header.

{
  "error": "post-only mode: only post-only orders and cancels are allowed",
  "code": "post_only_mode",
  "retry_after_seconds": 79
}

Errors that are not in the documentation

This is where most real debugging time goes. The following do not appear in Polymarket's error code reference because they do not originate from the CLOB application at all — they come from the edge, the network, or your client library. Knowing which layer produced an error tells you where to look.

"The server was unable to process your request"

The most-searched Polymarket error that is not in the docs. This is an edge-layer message rather than a CLOB API error — it is what you get when the request never reached, or never cleanly returned from, the application. In practice the usual causes are:

  • A malformed request the edge rejects before routing — an oversized payload, a bad header, or an invalid HTTP method for the route.
  • A transient upstream failure while the service is redeploying or under load.
  • Sustained rate limiting. Because Cloudflare throttles by queueing rather than rejecting, a badly over-limit client can end up with requests that time out at the edge instead of returning a clean 429.

Treat it as retryable with backoff, but if it is persistent rather than occasional, check your request rate first — see the rate limits reference — and then your payload size.

403 Forbidden

A 403 from Polymarket is usually not an authentication problem, which is what makes it confusing. It is generally the edge blocking the request. The common triggers are geographic restrictions on trading endpoints, datacenter and VPN IP ranges being challenged, and missing or non-browser-like headers on requests that the edge expects to come from a client.

If a 403 appears only from your cloud VM but not from your laptop, it is an IP reputation issue, not a code issue. If it appears on trading endpoints but not on market data endpoints, it is geographic.

SSL, TLS and certificate errors

"polymarket ssl error", "polymarket certificate error" and "polymarket https error" are almost always client-side. Polymarket's certificates are fine; something between you and them is not. Check, in order: an outdated CA bundle (very common in old Docker images and frozen Python environments), a corporate TLS-inspecting proxy substituting its own certificate, and a system clock so far off that certificates appear not-yet-valid or expired.

# 1. Does TLS work at all, outside your app's HTTP client?
curl -sS -o /dev/null -w "%{http_code} %{ssl_verify_result}\n" \
  https://clob.polymarket.com/ok

# 2. Is your CA bundle current? (Python)
python3 -c "import certifi, os; print(certifi.where()); print(os.path.getmtime(certifi.where()))"

# 3. Is your clock right? Certificate validation depends on it.
date -u

"Service not ready"

A startup-state message: the service is up enough to answer but not ready to serve. Functionally treat it like 425 — back off and retry. It should clear in seconds, and it is not something you can fix from the client side.

Client library exceptions

Errors like PolyApiException[status_code=None, error_message=request exception!] come from the Python client, not the server. status_code=None is the tell: no HTTP response was ever received, so this is a connection-level failure — DNS, TLS, timeout, or a dropped socket — surfacing as a library exception. Debug it as a network problem, not an API problem. The same reasoning applies to BuilderApiKeyCreds invalid authorization 401 in the relayer client, which means the builder credentials passed to the client were not accepted; re-derive them rather than reusing a cached set.

A retry policy that handles all of this

The practical takeaway is that Polymarket errors fall into three buckets, and conflating them is what makes bots fragile: retry blindly, retry after a specific delay, and never retry.

CategoryCodes / messagesAction
Transient425, 500, Service not ready, connection errorsRetry with exponential backoff + jitter
Rate limited429Retry after Retry-After; if absent, backoff with jitter
Mode-restricted503 post-onlySleep retry_after_seconds, then retry
Mode-restricted503 cancel-only / disabledStop opening; cancel if permitted; poll slowly
Terminal400 validation, 401, 404Do not retry — fix the request or the credentials
type Decision =
  | { retry: true; delayMs: number }
  | { retry: false; reason: string };

function classify(status: number, body: { error?: string; code?: string; retry_after_seconds?: number }, attempt: number): Decision {
  const msg = (body.error ?? "").toLowerCase();
  const backoff = Math.min(2 ** attempt * 250, 8000);
  const jittered = Math.random() * backoff;

  // Never retry — the request itself is wrong.
  if (status === 401) return { retry: false, reason: "auth: check key, secret, passphrase and clock drift" };
  if (status === 404) return { retry: false, reason: "not found" };
  if (status === 400) return { retry: false, reason: `validation: ${body.error}` };

  // Mode-restricted — the server tells you when to come back.
  if (status === 503 && body.code === "post_only_mode") {
    return { retry: true, delayMs: (body.retry_after_seconds ?? 60) * 1000 };
  }
  if (status === 503) return { retry: false, reason: "exchange paused or cancel-only" };

  // Transient — safe to retry.
  if (status === 425 || status === 429 || status >= 500) return { retry: true, delayMs: jittered };
  if (msg.includes("service not ready")) return { retry: true, delayMs: jittered };

  return { retry: false, reason: `unhandled status ${status}` };
}

Errors you cannot debug your way out of

Worth naming the limits of all this. Some things that look like errors are simply not supported: there is no historical order book endpoint that returns 500s because you called it wrong — it does not exist. Resolved markets return 404 not because your token ID is malformed but because they are gone from the public feeds.

If you are hitting those walls, you are not debugging an integration — you are missing a data source. PolyTest records point-in-time snapshots of Polymarket crypto Up/Down markets as they run, with full order book depth, so the history is there after resolution. Our own error reference covers the PolyTest API's error shapes, which are deliberately boring by comparison.

Frequently asked questions

What does "Unauthorized/Invalid api key" mean on Polymarket?
It means your API key is missing, expired, or invalid — but in practice the cause is usually one of four things: you are sending an incomplete credential set (the key, secret and passphrase are all required), your system clock has drifted enough to invalidate the request signature, you derived the key against a different wallet than you are trading from, or you are sending L1 headers where L2 headers are expected.
What is Polymarket error 425?
HTTP 425 Too Early means Polymarket's matching engine is restarting and your request arrived during that window. It is transient and safe to retry with exponential backoff. A single 425 is not worth alerting on.
Why does Polymarket say "The server was unable to process your request"?
This is an edge-layer message rather than a documented CLOB API error, meaning the request never cleanly reached or returned from the application. The usual causes are a malformed request rejected before routing, a transient upstream failure, or sustained rate limiting — because Polymarket throttles by queueing rather than rejecting, a badly over-limit client can see edge timeouts instead of clean 429s.
Why am I getting a 403 from the Polymarket API?
A 403 from Polymarket is usually the edge blocking the request rather than an authentication failure. Common triggers are geographic restrictions on trading endpoints, datacenter or VPN IP ranges being challenged, and missing browser-like headers. If it happens from a cloud VM but not your laptop, it is IP reputation; if it happens on trading endpoints but not market data endpoints, it is geographic.
How do I fix "the order signer address has to be the address of the API KEY"?
The key signing your order does not match the key the order claims was used. This means you are mixing credentials — typically a stale API key cached from a previous wallet. Re-derive your API credentials for the wallet you are actually trading from and make sure the maker address in the order matches the address that owns the key.
Should I retry Polymarket API errors automatically?
Only some. Retry 425, 429, 500-series and connection errors with exponential backoff plus jitter. Retry 503 post-only mode after the retry_after_seconds value the response provides. Never retry 400 validation errors, 401 auth errors or 404s — those need a fixed request or fixed credentials, and retrying just burns rate limit budget.

Get the historical data Polymarket does not keep

PolyTest records Polymarket crypto Up/Down markets as they run — 90M+ snapshots with 8 levels of order book depth, sub-second timestamps, and resolved markets preserved. Free tier, no card.

Keep reading