Docs menu

Rate limits

Limits protect every company on the platform. They are generous; a well-behaved integration rarely meets them.

Request limits

LimitValue
Requests per API key3,000 per minute
Requests per client IP address (all keys, and unauthenticated requests)6,000 per minute
Replaying one webhook delivery (…/deliveries/{delivery_id}/replay), per API key60 per minute
Replaying failed deliveries (…/deliveries/replay_failed), per API key5 per minute and 30 per hour, up to 1,000 deliveries per call

The two limits apply independently: many keys behind one IP share the IP budget. Successful responses do not carry rate-limit headers; you only see the limit when you hit it.

When you hit one

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 3000
X-RateLimit-Subject: user:apikey:0199c38d-7a8b-7c9d-98f9-142536475827
Content-Type: application/problem+json

{
  "type": "https://officehero.io/errors/rate-limited",
  "title": "Too many requests",
  "status": 429,
  "detail": "Too many requests — try again in 60 seconds",
  "retry_after_s": 60,
  "code": "rate_limited"
}

The replay limits answer differently: X-RateLimit-Bucket names the limit (integrations-replay-minute, integrations-replay-failed-minute or integrations-replay-failed-hour) instead of X-RateLimit-Subject, and Retry-After counts down to the end of that limit's window, so it can be anything up to 60 seconds (3,600 for the hourly limit).

Wait Retry-After seconds before the next request. A small helper that also backs off on 5xx:

async function withRetry(send, attempts = 5) {
  for (let i = 0; ; i++) {
    const res = await send();
    if (res.status !== 429 && res.status < 500) return res;
    if (i === attempts - 1) return res;
    const retryAfter = Number(res.headers.get('Retry-After'));
    const wait = Number.isFinite(retryAfter) && retryAfter > 0
      ? retryAfter * 1000
      : Math.min(30_000, 500 * 2 ** i) * (0.8 + Math.random() * 0.4);
    await new Promise((r) => setTimeout(r, wait));
  }
}

Other limits

WhatLimit
Page size (limit)default 50, maximum 100
Webhook endpoints per company (active + paused; resuming or pausing one switched off after failures counts)5
Idempotency window24 hours; responses up to 256 KiB are replayed
Webhook response time10 s to connect, 15 s in total
Webhook attempts8, retried after 1 min, 5 min, 15 min, 1 h, 3 h, 6 h, 12 h
Webhook signature age you should accept300 s
Automatic endpoint disabling50 failed attempts in a row, 3 × 410 Gone within 24 hours, or every delivery of the last 7 days failed — see Retries

Need more? Tell support what you are building.