API

Concurrent Polymarket requests with asyncio

Fetching a hundred markets one at a time wastes most of the time waiting on the network. Concurrency fixes that, but unbounded concurrency trips the rate limit. A bounded semaphore gets both: fast and polite.

What this guide covers

  • The SDK is synchronous; for concurrency use httpx.AsyncClient against the REST endpoints
  • The API enforces per-minute and per-day request limits
  • An asyncio.Semaphore caps in-flight requests so you do not exceed the rate
  • Each request needs the X-API-Key header

Why concurrency, and why bound it

Fetching a hundred markets serially means a hundred round trips back to back. Concurrently, the latency overlaps, so the wall-clock time approaches the slowest single request rather than their sum. That is the entire benefit.

The catch is that the API meters requests per minute and per day. Fire a thousand at once and most come back rejected. A semaphore of a modest size keeps you under the limit while still overlapping latency.

Choosing a concurrency level

Set the semaphore from your plan's per-minute limit and your observed latency. If you can make N requests per minute and each takes a fraction of a second, a small concurrency count saturates the budget without bursting past it.

Start conservative - five or ten concurrent requests - and raise it while watching for 429 responses. The right number is plan- and latency-dependent, so measure rather than assume.

The async client

Because the SDK is synchronous, concurrent work uses httpx.AsyncClient directly against api.polyorderbooks.com with the same X-API-Key header. The endpoints and shapes are identical; only the transport changes.

Wrap each request in a task, gather them, and handle errors per task so one failure does not cancel the batch. Retry the failures separately, ideally with backoff.

Memory when gathering many pages

Gathering every response into memory works for a handful of markets and fails for thousands. Process each result as it completes and write it out, rather than holding the whole result set until the end.

A bounded queue or an as_completed loop lets you stream results: parse, flatten and append to a local file or database as each request finishes. Peak memory then depends on the concurrency level, not the total number of markets.

Combine the streamed writes with the local cache so an interrupted run resumes without re-fetching. Concurrency gets you speed; streaming and caching keep a large crawl from turning into a memory or quota problem.

Code examples

import asyncio
import os

import httpx

BASE = "https://api.polyorderbooks.com"
HEADERS = {"X-API-Key": os.environ["POLYORDERBOOKS_API_KEY"]}


async def fetch(client, sem, slug, start, end):
    async with sem:
        r = await client.get(
            f"{BASE}/v1/markets/{slug}/books",
            params={"start_ts": start, "end_ts": end, "resolution": "60s"},
            headers=HEADERS,
        )
        r.raise_for_status()
        return slug, r.json()["data"]


async def main(slugs):
    sem = asyncio.Semaphore(6)  # cap in-flight requests
    async with httpx.AsyncClient(timeout=30) as client:
        tasks = [
            fetch(client, sem, s,
                  "2026-10-17T07:00:00Z", "2026-10-17T08:00:00Z")
            for s in slugs
        ]
        return await asyncio.gather(*tasks)

# results = asyncio.run(main(slugs))

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

Is the SDK async?

No, it is synchronous. For concurrency, use httpx.AsyncClient against the same REST endpoints with the X-API-Key header.

How many concurrent requests should I use?

Start small (around six) and raise it while watching for 429s. The right level depends on your plan's per-minute limit and your latency.

Will concurrency break rate limits?

It can. Cap in-flight requests with a semaphore so the concurrency level stays under your per-minute allowance.

How do I handle a slow request among many?

Set a client timeout and treat timeouts as retryable. gather() will not hang forever if each request has its own deadline.