Rate Limits
Every API endpoint has rate limits to ensure fair usage and platform stability. Rate limits are applied per IP address using a sliding window.
Endpoint Limits
| Endpoint | Method | Limit | Window |
|---|---|---|---|
| POST /api/links | Create link | 10 req | 1 min |
| GET /api/links | List links | 30 req | 1 min |
| GET /api/links/:id | Get link | 60 req | 1 min |
| PATCH /api/links/:id | Update link | 10 req | 1 min |
| DELETE /api/links/:id | Delete link | 10 req | 1 min |
| GET /api/links/:id/clicks | Click analytics | 30 req | 1 min |
| POST /api/shorten | Quick shorten (auth) | 30 req | 1 min |
| POST /api/shorten | Quick shorten (anon) | 5 req | 1 min |
| GET /api/keys | List keys | 30 req | 1 min |
| POST /api/keys | Create key | 10 req | 1 min |
| DELETE /api/keys/:id | Revoke key | 10 req | 1 min |
| GET /api/webhooks | List webhooks | 30 req | 1 min |
| POST /api/webhooks | Create webhook | 10 req | 1 min |
| PATCH /api/webhooks/:id | Update webhook | 10 req | 1 min |
| DELETE /api/webhooks/:id | Delete webhook | 10 req | 1 min |
| POST /api/webhooks/:id | Test ping | 5 req | 1 min |
| GET /api/domains | List domains | 30 req | 1 min |
| POST /api/domains/add | Add domain | 10 req | 1 min |
| POST /api/domains/:id/verify | Verify domain | 5 req | 1 min |
| DELETE /api/domains/:id | Delete domain | 10 req | 1 min |
| PATCH /api/domains/:id | Update domain | 10 req | 1 min |
Response Headers
When you hit the rate limit, the response includes a Retry-After header:
| Field | Type | Description |
|---|---|---|
| Retry-After | number | Seconds to wait before retrying |
The /api/shorten endpoint also returns these headers on all responses:
| Field | Type | Description |
|---|---|---|
| X-RateLimit-Limit | number | Maximum requests allowed in the window |
| X-RateLimit-Remaining | number | Requests remaining in the current window |
| X-RateLimit-Reset | number | Seconds until the window resets |
Handling 429 Responses
When rate limited, you receive a 429 status with this body:
429 Too Many Requests
{
"error": "Too many requests"
}
// Headers:
// Retry-After: 45Best Practices
Exponential Backoff (TypeScript)
async function apiCallWithRetry(fn: () => Promise<Response>, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fn();
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get('Retry-After') || '60');
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return res;
}
throw new Error('Max retries exceeded');
}Tips for Staying Within Limits
- Cache responses: List results and link data can be cached on your end to reduce API calls
- Use webhooks for real-time data, Instead of polling for click events, subscribe to
link.clickwebhooks - Batch operations in the dashboard, The dashboard supports bulk operations (import, export, tag) without counting against API limits
- Use pagination efficiently, Fetch larger pages (up to 100 items) instead of many small pages
- Respect Retry-After, Always wait the specified time before retrying, or you risk progressive penalties
Was this page helpful? Spotted something wrong?