Skip to main content
Every API key on BlockForecast is subject to a default rate limit of 60 requests per minute. The limit is enforced per key on a rolling 60-second window — it is not a fixed-clock reset. If you exceed the limit, the API returns 429 Too Many Requests and you must wait before retrying. This page explains how to read the rate-limit headers included on every response, how to implement exponential backoff, and how to stay within the default quota for common workloads.

Rate-limit headers

Every API response includes three headers that tell you your current quota status:
HeaderTypeDescription
X-RateLimit-LimitintegerYour total allowed requests per 60-second window (default: 60).
X-RateLimit-RemainingintegerRequests remaining in the current window.
X-RateLimit-ResetUnix timestampThe time (in seconds since epoch) when the window resets and X-RateLimit-Remaining returns to X-RateLimit-Limit.
Example response headers:
HTTP/2 200
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1745001060
Content-Type: application/json

When you exceed the limit

If X-RateLimit-Remaining reaches 0, the next request returns:
HTTP/2 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1745001060
Retry-After: 23
Content-Type: application/json
{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "You have exceeded the rate limit of 60 requests/min. Retry after the Retry-After interval.",
  "retryAfter": 23
}
The Retry-After header tells you exactly how many seconds to wait before the window resets.

Handling 429s: exponential backoff

Immediately retrying after a 429 will only trigger another 429. Use exponential backoff with jitter:
import os, time, random, requests

API_KEY = os.environ["BLOCKFORECAST_API_KEY"]
BASE_URL = "https://blockforecast.io/api/v2"

def get_with_backoff(path: str, params: dict = None, max_retries: int = 5):
    url = f"{BASE_URL}{path}"
    headers = {"X-API-Key": API_KEY}

    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers, params=params)

        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 1))
            # Add jitter: wait retry_after seconds + up to 1s random
            wait = retry_after + random.random()
            print(f"Rate limited. Waiting {wait:.1f}s (attempt {attempt + 1}/{max_retries})")
            time.sleep(wait)
            continue

        resp.raise_for_status()
        return resp.json()

    raise Exception("Max retries exceeded after repeated 429 responses")

markets = get_with_backoff("/public/markets", params={"status": "active"})
Read X-RateLimit-Remaining proactively on every response and slow down when it approaches zero — don’t wait for a 429 to trigger your backoff logic.

Tips for staying within the limit

Cache responses locally. Market state does not change on every tick. For most read workloads, caching GET /markets for 5–10 seconds and individual market details for 1–2 seconds dramatically reduces API calls without meaningfully staling your data. Batch reads with pagination. Rather than making one request per market, use the limit parameter on GET /markets to fetch up to 100 markets in a single call. Then page through with offset as needed. Use the real-time feed for high-frequency data. If you need market prices at sub-second latency, the x402 feed endpoint (GET /api/v2/x402/feed, $0.001/call) is designed for that use case and does not count against your REST rate limit. Deduplicate inflight requests. In async or multi-threaded code, use a simple in-memory cache or request-deduplication layer to prevent the same resource from being fetched concurrently by multiple coroutines or threads. Avoid polling loops with short intervals. A loop that calls GET /markets/:id every second will exhaust your quota in one minute. Use a minimum 5-second poll interval for market state, or switch to the WebSocket broadcaster for real-time updates.

Request a higher rate limit

If your use case requires more than 60 requests per minute — for example, a trading desk pulling tick-level data or an arbitrage bot running across hundreds of markets simultaneously — you can request a higher quota. Email support@blockforecast.io with:
  • Your wallet address (the one attached to your API key)
  • A brief description of your use case
  • The approximate request rate you need
Higher-limit keys are provisioned manually and are typically turned around within one business day.
x402 pay-per-request endpoints (/api/v2/x402/*) are not subject to the same per-minute quota. Each call is independently metered by payment, so there is no burst limit — you can make as many calls as you can pay for.