Rate Limits
- Written for
- + Written for
- Deprecated
- + Deprecated
- Applies to
- + Applies to
Rate Limits
The Restore Hub API uses a sliding-window limiter measured in 10-second buckets. Limits are per API key (effectively per account), and they scale with your plan. Every authenticated response carries headers describing your current state.
Per-plan limits
Plan | Requests / 10s | Effective requests / minute |
|---|---|---|
Free |
| ~300 |
Premium |
| ~600 |
Business |
| ~1,200 |
Enterprise |
| ~1,200 (contact sales for higher) |
Free-plan keys are also restricted to read-only methods (GET). Write operations return
403with anupgradeUrl.
Response headers
Every authenticated response includes these headers:
Header | Meaning |
|---|---|
| Your bucket size for the current plan (e.g. |
| Requests left in the current 10-second window. |
| Unix timestamp (seconds) at which the window resets. |
| Always |
| Seconds to wait. Sent only on |
When you exceed the limit
The server responds with HTTP 429 and a JSON body:
{
"error": "Rate limit exceeded",
"limit": 100,
"window": "10s",
"retryAfter": 10,
"plan": "PREMIUM",
"upgradeUrl": "https://restorehub.net/pricing"
}Backoff strategy
Recommended algorithm for clients:
On
429, read theRetry-Afterheader.Sleep for
Retry-After + jitterseconds (10–25% jitter).Retry the same request. Cap at 3 attempts before giving up.
If you keep hitting
429, slow your concurrency or upgrade.
async function call(url, init = {}, attempt = 0) {
const res = await fetch(url, {
...init,
headers: { Authorization: `Bearer ${process.env.RH_API_KEY}`, ...init.headers },
});
if (res.status !== 429) return res;
if (attempt >= 3) throw new Error("Rate-limit backoff exhausted");
const retryAfter = Number(res.headers.get("Retry-After") ?? 10);
const jitter = Math.random() * 0.25 + 1;
await new Promise(r => setTimeout(r, retryAfter * 1000 * jitter));
return call(url, init, attempt + 1);
}import os, random, time, requests
def call(url, attempt=0, **kw):
r = requests.get(
url,
headers={"Authorization": f"Bearer {os.environ['RH_API_KEY']}"},
**kw,
)
if r.status_code != 429:
return r
if attempt >= 3:
raise RuntimeError("Rate-limit backoff exhausted")
delay = int(r.headers.get("Retry-After", "10")) * (1 + random.uniform(0, 0.25))
time.sleep(delay)
return call(url, attempt + 1, **kw)Member pulls have their own budget
members:pull is throttled separately — Discord caps how fast bots can join users to a guild. Restore Hub paces pulls at 50 members per minute per target server, regardless of your plan's API rate limit. See API Reference for the full pull endpoint contract.
Guidance for AI agents
List endpoints are paginated. Don't fetch every page in parallel — interleave.
Cache the OpenAPI spec and data-model JSON locally; they rarely change.
If you're orchestrating with the MCP server, the same limits apply (it proxies REST).
Respect
Retry-After. Don't tight-loop after a429.
Need more?
Contact [email protected] for higher limits on the Enterprise plan, or upgrade in Billing.