Rate Limits & Errors
Understand the rate limits applied to API requests and how to handle error responses in your integrations.
Rate limits by plan
Rate limits are applied per-user based on the authenticated identity. Requests that exceed the limit receive a 429 Too Many Requests response.
| Endpoint | Free | Starter | Pro | Business |
|---|---|---|---|---|
| Default | 60/min | 60/min | 60/min | 60/min |
| Auth (/api/token) | 5/min | 5/min | 5/min | 5/min |
| Link Create | 20/min | 20/min | 20/min | 20/min |
| Export | 5/min | 5/min | 5/min | 5/min |
| Import | 3/min | 3/min | 3/min | 3/min |
| QR Download | 20/min | 20/min | 20/min | 20/min |
Rate limit headers
Every API response includes headers to help you track your rate limit usage.
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum number of requests allowed in the current window. |
X-RateLimit-Remaining | Number of requests remaining in the current window. |
Retry-After | Seconds to wait before retrying (only present on 429 responses). |
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
Content-Type: application/jsonHTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 45
Content-Type: application/jsonHandling rate limits
Implement exponential backoff with jitter when you receive a 429 response. Always respect the Retry-After header.
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(
response.headers.get("Retry-After") || "60",
10
);
const jitter = Math.random() * 1000;
const delay = retryAfter * 1000 + jitter;
console.log(
`Rate limited. Retrying in ${Math.round(delay / 1000)}s...`
);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
return response;
}
throw new Error("Max retries exceeded");
}import time
import random
import requests
def fetch_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries + 1):
response = requests.get(url, headers=headers)
if response.status_code == 429:
retry_after = int(
response.headers.get("Retry-After", 60)
)
jitter = random.uniform(0, 1)
delay = retry_after + jitter
print(f"Rate limited. Retrying in {delay:.0f}s...")
time.sleep(delay)
continue
return response
raise Exception("Max retries exceeded")Error codes
The API uses standard HTTP status codes. Error responses always include an error: 1 field and a human-readable message.
The request body is malformed, missing required fields, or contains invalid values.
{
"error": 1,
"message": "The 'url' field is required and must be a valid URL."
}The request is missing authentication credentials, or the provided token/API key is invalid or expired.
{
"error": 1,
"message": "Invalid or expired access token."
}The authenticated user does not have permission to perform this action. This often means a feature is gated behind a higher plan.
{
"error": 1,
"message": "Custom aliases require the Starter plan or above."
}The requested resource does not exist or the authenticated user does not have access to it.
{
"error": 1,
"message": "Link not found."
}A resource with the same identifier already exists. Common when creating a link with an alias that is already taken.
{
"error": 1,
"message": "The alias 'my-link' is already in use."
}You have exceeded the rate limit for this endpoint. Wait for the duration specified in the Retry-After header before retrying.
{
"error": 1,
"message": "Rate limit exceeded. Try again in 45 seconds."
}An unexpected error occurred on our servers. If this persists, please contact support.
{
"error": 1,
"message": "An internal error occurred. Please try again later."
}Best practices for error handling
- 1.Always check the
errorfield in the response body, not just the HTTP status code. - 2.Implement retry logic with exponential backoff for 429 and 5xx responses.
- 3.Log the full error response including the message for debugging.
- 4.Do not retry 400, 401, 403, or 404 errors -- these require changes to the request.
- 5.Monitor your
X-RateLimit-Remainingheader to proactively throttle requests before hitting the limit.