API

Retries and backoff for Polymarket API calls

Networks flake and quotas bite. A robust client retries a bounded number of times with growing delays, and when the API says how long to wait, it waits exactly that long.

What this guide covers

  • RateLimitError (429) carries retry_after, the seconds the API asked you to wait
  • AuthenticationError is 401, NotFoundError is 404, APIError covers other HTTP failures
  • Exponential backoff with jitter avoids synchronized retry storms
  • Retry only idempotent reads - all SDK methods are GETs

Which errors to retry

Rate limits and transient server errors are worth retrying; authentication and not-found errors are not - retrying a bad key or a wrong slug just burns quota. Classify the error first, then decide.

Every SDK method is a GET, so retrying is safe: there is no risk of duplicating a side effect. That is not true of arbitrary APIs, and it is a reason to keep write operations out of a retry loop.

Honoring retry_after

When the API returns a rate-limit error, the RateLimitError exposes retry_after - the number of seconds it wants you to wait. Sleeping exactly that long is better than a fixed backoff, because the server told you the correct answer.

If retry_after is absent, fall back to exponential backoff starting at a fraction of a second and doubling each attempt, capped at a sane maximum so a persistent failure does not hang forever.

Jitter and the retry budget

Add jitter to backoff delays so many clients failing together do not retry in lockstep and re-flood the server. A random fraction of the delay is enough to break synchronization.

Also bound the number of attempts. Three or four is usually right: enough to ride out a blip, few enough that a real outage surfaces as an error rather than an endless loop. Log the final failure rather than swallowing it.

Retrying inside a crawl

In a long crawl, retries should not lose progress. Wrap each page request in the retry helper and keep the cursor in a variable outside the loop, so a retried page does not restart the whole run.

Log the number of retries per run. A crawl that suddenly needs many retries is telling you that you are near a limit, which is a signal to lower concurrency or slow down before the whole job fails.

If a page exhausts its retries, decide deliberately whether to abort or to skip. Skipping keeps the run going but leaves a hole you must record, so the analysis knows the data is incomplete rather than assuming it is whole.

Code examples

import os
import random
import time

from polyorderbooks import (
    AuthenticationError,
    PolyOrderbooksClient,
    RateLimitError,
    APIError,
)


def with_retry(fn, *args, attempts=4, **kwargs):
    for i in range(attempts):
        try:
            return fn(*args, **kwargs)
        except RateLimitError as err:
            wait = err.retry_after or (0.5 * 2 ** i)
            time.sleep(wait + random.random() * 0.2)
        except AuthenticationError:
            raise  # bad key: do not retry
        except APIError:
            if i == attempts - 1:
                raise
            time.sleep(0.5 * 2 ** i + random.random() * 0.2)


with PolyOrderbooksClient(api_key=os.environ["POLYORDERBOOKS_API_KEY"]) as c:
    books = with_retry(
        c.get_market_books, "btc-updown-5m-1787486400",
        start_ts="2026-10-17T07:00:00Z",
        end_ts="2026-10-17T08:00:00Z", resolution="60s",
    )

Free tier

The Starter plan is free, includes order books, prices, and metrics at 250ms resolution, with 3 days of history, 60 requests/min, 1,000/day, 1 free AI backtest, and 3 strategy backtests. No credit card required.

Paid data windows from $19/mo extend history to 30–120 days at higher throughput. Backtest AI add-on is +$19/mo or standalone at $29/mo.

FAQ

What does RateLimitError provide?

It carries status_code, message, the parsed body and retry_after, the seconds the API asked you to wait before retrying.

Should I retry 404s?

No. NotFoundError means the id or slug does not exist; retrying it wastes quota. Retry only 429s and transient server errors.

Why add jitter?

Without it, clients that fail together retry together and re-flood the server. A small random fraction of the delay breaks the synchronization.

How many retries is right?

Three or four bounded attempts. Enough to ride out a blip, few enough that a real outage surfaces as an error rather than looping forever.