On this page

API

Rate limits

Five rate limit tiers in 60 second windows, the RateLimit headers on every response, Retry-After on 429, and how the SDKs back off for you.

Limits protect the service from abuse and keep a busy read loop from starving payments. Each operation belongs to a tier, and the limit is a separate bucket per tier and caller: anonymous callers are counted by IP address, token callers by token, signed-in sessions by account. Windows are fixed at 60 seconds.

Rate limit tiers
TierOperationsAnonymousWith a token
read3460/min per IP600/min per token
write28not allowed120/min per token
plan15/min per IP and 40/day20/min per token and 500/day
discover15/min per IP and 40/day10/min per token and 300/day
payments8not allowed30/min per token

Daily caps apply to plan and discover because each call fans out to several outside sources: anonymous callers get 40 a day on both, token callers 500 plans and 300 discovery calls a day. Write and payments operations are not available anonymously (401 unauthorized).

Which operation is in which tier

Headers

Every response from a rate-limited operation carries:

HeaderMeaning
RateLimit-LimitThe quota of the tier for this caller.
RateLimit-RemainingRequests left in the current window.
RateLimit-ResetSeconds until the window resets (a delta, not a timestamp).
RateLimit-PolicyThe policy as <quota>;w=<seconds>, for example 60;w=60.

Real headers from an anonymous GET /api/v1/items:

http
ratelimit-limit: 60
ratelimit-policy: 60;w=60
ratelimit-remaining: 59
ratelimit-reset: 59

When you are over the limit the answer is 429 with a Retry-After header (seconds) and a rate_limited problem that repeats it as retry_after:

http
HTTP/2 429
retry-after: 17
content-type: application/problem+json
json
{
  "type": "https://reuseforge.com/docs/api/errors/rate_limited",
  "title": "Rate limited",
  "status": 429,
  "detail": "Rate limit of 60 requests per 60 seconds for the \"read\" tier exceeded. Retry in 17 seconds.",
  "code": "rate_limited",
  "request_id": "req_...",
  "retry_after": 17
}

Failed authentication also counts: repeated bad tokens spend the anonymous read budget of your address.

Checking without spending

get_rate_limits reports every tier's state for the caller without consuming allowance, so a client can look before it starts a batch.

curl

bash
curl https://reuseforge.com/api/v1/rate-limit -H "Authorization: Bearer $REUSEFORGE_TOKEN"

Python

python
import reuseforge

print(reuseforge.Client().get_rate_limits())

JavaScript

javascript
import { ReuseForge } from '@reuseforge/sdk';

console.log(await new ReuseForge().getRateLimits());

CLI

bash
rf api rate-limit

Being a good client

  • Read the headers, do not hard-code the numbers. The values are initial and may change in a minor release; GET /api/v1/ and get_rate_limits report them.
  • On 429, wait Retry-After seconds, then retry. Do not retry in a tight loop.
  • Spread bulk work out and prefer limit=100 pages to many small requests; use conditional requests so repeat reads are cheap.
  • Send a descriptive User-Agent that names your tool and a contact, for example my-tool/1.0 (+https://example.com). The SDKs send reuseforge-python/<version> and reuseforge-js/<version>.
  • Public GET responses are cacheable for a short time (Cache-Control: public, max-age=15, s-maxage=30, stale-while-revalidate=60); a shared cache in front of your own service saves quota.

The SDKs and the CLI do this for you: they retry 429 after Retry-After (up to 60 seconds, longer is raised as RateLimitError) and back off with jitter on other transient failures. Higher quotas for organizations and verified publishers are arranged by agreement and are not part of the contract.

Last updated: