Menu

Rate limits

Picked limits how many calls a workspace can make per minute. This page gives the limits, the headers that show where you stand, and how to wait and retry.

The limits

Endpoints Limit
GET /api/v1/stock, GET /api/v1/stock/changes and GET /api/v1/stock/changes/feed 200 requests per minute
Every other endpoint 60 requests per minute

The limits count per workspace, not per token: every token of your workspace draws from the same budget. The stock endpoints have a budget of their own, so polling stock never uses up the budget your order import needs. A step's callback takes no token and isn't limited while it names a step that waits for it; only callbacks to an address no step waits on (a 404) count, per IP address.

Every call counts, including calls that fail and dry runs.

The headers

Every response to an authenticated call tells you where you stand:

Header Meaning
X-RateLimit-Limit The limit that applies to this call, per minute.
X-RateLimit-Remaining How many calls are left in the current minute.

Past the limit, Picked answers 429 with the code rate_limited, and two more headers:

Header Meaning
Retry-After How many seconds to wait before calling again.
X-RateLimit-Reset When the limit resets, as a Unix timestamp.

Staying under the limit

  • Read stock in bulk. GET /api/v1/stock?skus=A,B,C takes up to 100 SKUs in one call, so you don't need one call per product.
  • Spread the work. Send a batch of orders at an even pace that stays under the limit, rather than all at once. Sent all at once, most of it is refused.

Waiting and retrying

When a call answers 429, wait the number of seconds in Retry-After and try again. When it answers 503 or 500, wait a moment and double the pause on each new attempt. Give up after a few attempts and report the failure, rather than retrying forever.

A sketch in JavaScript (Node.js 18 or later):

async function callPicked(url, options = {}, attempts = 5) {
    for (let attempt = 1; ; attempt++) {
        const response = await fetch(url, {
            ...options,
            headers: {
                Authorization: `Bearer ${process.env.PICKED_TOKEN}`,
                Accept: 'application/json',
                ...options.headers,
            },
        });

        const retryable = [429, 500, 503].includes(response.status);

        if (!retryable || attempt === attempts) {
            return response;
        }

        const retryAfter = Number(response.headers.get('Retry-After'));
        const seconds = retryAfter > 0 ? retryAfter : 2 ** attempt;

        await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
    }
}

Retry a write only when Picked can recognise the repeat. See Idempotency.