Sign in Get started

Rate limits

Per-tier request budgets, the response headers, and how the SDK handles 429s.

View as Markdown

Every API key is rate-limited by its organization’s plan, over a 1-hour sliding window, and each key has its own window. The SDK auto-retries short waits; longer waits raise so your code can decide.

Source: _http/retry.py

Per-tier limits

Tier Requests / hour
Free 2,000
Core 5,000
Pro 20,000
Team 50,000
Enterprise 100,000

A key’s limit follows the plan: an upgrade applies to existing keys immediately. An admin can hold a single key below the plan with an optional per-key cap in requests per hour - rate_limit when creating or updating a key. A cap can never raise a key above its plan. Removing it (clear_rate_limit=True in the SDK, "rate_limit": null over REST) returns the key to its plan. Settings and every API-key response report the limit the key actually gets.

Response headers

A 429 response carries the state of the window. Successful responses do not include rate-limit headers - pace against the per-tier limit above, and let the SDK handle a 429 for you.

Header Meaning
Retry-After Seconds until the next call may succeed
X-RateLimit-Limit The key’s limit for the window
X-RateLimit-Remaining Calls left in the window (0 on a 429)
X-RateLimit-Reset Unix timestamp when the next call may succeed

What counts

One HTTP request → one count. Payload size doesn’t matter. Streaming downloads (image / model / export blobs) count as a single request regardless of size.

Bulk operations are designed to keep counts low - prefer client.batch.move() / client.batch.update() over per-image calls in a loop.

SDK auto-retry

RateLimitError carries a retry_after attribute. The SDK waits and retries automatically when the response includes a Retry-After header and the wait is at most 120 seconds. Anything longer raises immediately so your code can back off, queue, or fail.

from pictograph.exceptions import RateLimitError
import time

try:
    client.datasets.list(
        limit=1000,
    )
except RateLimitError as e:
    print(f"Hit cap; retry in {e.retry_after}s")
    time.sleep(e.retry_after or 5.0)
    # …then retry. The SDK won't auto-recover for waits >120s.

The pictograph CLI inherits the same behaviour. On a long wait it prints the retry estimate to stderr so you can Ctrl-C if you don’t want to wait.

Spreading bursty load

If your workload is bursty (nightly imports, large auto-annotation runs), pace it across the hour:

from time import sleep

for batch in batches:
    process(batch)
    sleep(0.8)  # at most ~4,500 req/hr - under Core's 5,000

For sustained workloads above your tier, the right move is to upgrade - retrying harder doesn’t increase your share.

See also

Copied to clipboard