Limits & Quotas
WRL enforces two types of request limits: rate limits (short-term burst protection) and quotas (monthly usage caps). Both return 429 responses, but they are distinct and require different handling.
Rate Limits vs Quotas
| Rate Limits | Quotas | |
|---|---|---|
| Scope | Per IP or per tenant | Per tenant |
| Window | 60 seconds | Calendar month |
| Reset | Rolling window (seconds) | First of next month |
429 limitType |
'tenant' or absent |
'quota' |
Retry-After format |
Seconds (integer) | HTTP-date |
Monthly Quotas
Each tenant is assigned a tier that determines their monthly limits.
| Tier | Captures / month | Storage |
|---|---|---|
| Starter (free) | 200 | 1 GB |
| Pro | 5,000 | 50 GB |
Checking Usage
Every successful capture response includes X-Quota-* headers so you can monitor usage without making a separate request.
| Header | Description |
|---|---|
X-Quota-Limit |
Maximum captures allowed in the current billing period |
X-Quota-Used |
Captures consumed so far this period |
X-Quota-Remaining |
Captures left before the quota is exhausted |
To check usage programmatically without submitting a capture, use the account usage endpoint (requires session auth):
GET /v1/account/usage
Quota Exceeded Response
When a capture request would exceed the monthly quota, WRL returns a 429 before any capture work is attempted.
curl -X POST https://api.webresourceledger.com/v1/captures \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
HTTP/1.1 429 Too Many Requests
Retry-After: Mon, 01 Apr 2026 00:00:00 GMT
Content-Type: application/json
{
"type": "about:blank",
"status": 429,
"title": "Too Many Requests",
"detail": "Monthly capture quota exceeded. Your quota resets on 2026-04-01.",
"limitType": "quota",
"quota": {
"resource": "captures",
"limit": 200,
"used": 200,
"remaining": 0,
"resetsAt": "2026-04-01T00:00:00.000Z"
}
}
Key differences from rate-limit 429 responses:
limitTypeis'quota', not'tenant'or absentquota.resourceindicates which limit was hit:'captures'or'storage'Retry-Afteris an HTTP-date string (not a seconds integer) -- it points to the first second of the next billing period
Batch Captures and Quotas
Batch requests check quota upfront for the entire batch before any capture is queued. If the batch would exceed the remaining quota, the entire batch is rejected with a single 429 response -- no items are accepted.
For example, if you have 30 captures remaining and submit a batch of 50 URLs, all 50 are rejected. Resubmit a smaller batch that fits within the remaining quota, or wait until the quota resets.
See Batch Captures for full batch error handling patterns.
Rate Limits
Rate limits protect against short bursts of traffic. They apply independently of quotas -- you can hit a rate limit even when quota is plentiful, and you can exhaust quota without ever hitting a rate limit.
Per-IP Limits
Unauthenticated or low-trust requests are subject to per-IP rate limiting. The limit applies to the originating IP address.
Per-Tenant Limits
Authenticated requests are rate-limited per tenant. The window is 60 seconds, rolling.
Rate Limit Response
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json
{
"type": "about:blank",
"status": 429,
"title": "Too Many Requests",
"detail": "Rate limit exceeded. Retry after 42 seconds.",
"limitType": "tenant"
}
limitTypeis'tenant'for per-tenant limits, or absent for per-IP limitsRetry-Afteris an integer number of seconds until the window resets
When you receive a rate-limit 429, wait the number of seconds in Retry-After before retrying. For batch requests, rate limits apply per URL -- see Batch Captures for details.
Custom Quotas (Operators)
Operators can override the default tier quotas for individual tenants via the admin API. This is useful for granting higher limits to enterprise tenants or restricting a specific tenant below the tier default.
curl -X PUT https://api.webresourceledger.com/v1/admin/tenants/acme-corp/config \
-H "Authorization: Bearer YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"quotas": {
"capturesPerMonth": 25000,
"storageBytes": 107374182400
}
}'
{
"tenantId": "acme-corp",
"config": {
"quotas": {
"capturesPerMonth": 25000,
"storageBytes": 107374182400
}
},
"updatedAt": "2026-03-23T09:00:00.000Z"
}
Request fields:
| Field | Type | Description |
|---|---|---|
quotas.capturesPerMonth |
integer | Override for monthly capture limit. Replaces the tier default. |
quotas.storageBytes |
integer | Override for storage limit in bytes. Replaces the tier default. |
To remove a custom override and return the tenant to their tier default, omit the field or set it to null.