PineRouterDocs
GuidesModelsAPI referenceApps

Rate limits

How many requests you may send, what happens over the limit, and backoff code.

The limits

LimitValue
All requests, failed ones included120 a minute
Successful requests60 a minute
  • The limits are per account. All your keys share them.
  • Every call to /v1/... counts, including GET /v1/models and each poll of GET /v1/jobs/{id} or GET /v1/videos/{id}. Poll every 5 to 10 seconds, not faster.
  • A request that fails (any status of 400 or higher) counts toward the 120, not toward the 60.

Need more? Ask through the contact on the About page.

Over the limit

You get HTTP 429 with a Retry-After header in seconds (60 today) and an OpenAI-style body:

{"error": {"code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: ...)", "type": "api_error"}}

When the 120 limit is the one you hit, the message says "total request limit" and reminds you that failed attempts count.

There are no x-ratelimit-* headers. Responses do not tell you how many requests you have left, so count them yourself if you run near the limit.

A 429 on /v1/jobs can also mean the model itself is at capacity. The same backoff works.

Back off and retry

Retry 429 and 5xx answers only. Wait Retry-After seconds when it is set, otherwise 2, 4, then 8 seconds with some jitter, and give up after 3 retries. See Errors for what not to retry.

import random, time, requests

RETRY = {429, 500, 502, 503, 504}

def call(method, url, tries=4, **kw):
    for i in range(tries):
        r = requests.request(method, url, timeout=300, **kw)
        if r.status_code not in RETRY or i == tries - 1:
            return r
        wait = r.headers.get("Retry-After")
        time.sleep(float(wait) if wait else 2 ** (i + 1) + random.random())
    return r

r = call("POST", "https://pinerouter.ai/v1/chat/completions",
         headers={"Authorization": "Bearer sk-..."},
         json={"model": "gpt-6-luna", "messages": [{"role": "user", "content": "Hi"}]})
print(r.status_code, r.json())
const RETRY = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function call(url, init, tries = 4) {
  for (let i = 0; ; i++) {
    const r = await fetch(url, init);
    if (!RETRY.has(r.status) || i === tries - 1) return r;
    const wait = r.headers.get("retry-after");
    await sleep(wait ? Number(wait) * 1000 : 2 ** (i + 1) * 1000 + Math.random() * 1000);
  }
}

const r = await call("https://pinerouter.ai/v1/chat/completions", {
  method: "POST",
  headers: { Authorization: "Bearer sk-...", "Content-Type": "application/json" },
  body: JSON.stringify({ model: "gpt-6-luna", messages: [{ role: "user", content: "Hi" }] }),
});
console.log(r.status, await r.json());

The official OpenAI and Anthropic SDKs already retry 429 and 5xx with backoff (2 retries by default, max_retries to change it). Do not wrap them in a second retry loop.

Do not use this loop to resend POST /v1/jobs after a timeout or a 5xx: the job may have started. Check Task logs first. See Errors.