Rate Limits
WittyForm enforces rate limits on all API requests to ensure fair usage and platform stability. Limits are applied per API key and vary by plan.
Limits by plan
| Plan | Rate limit | Burst |
|---|---|---|
| Free | No API access (upgrade to Pro to use the API) | |
| Pro | 1,000 requests / minute | |
| Enterprise | 5,000 requests / minute | |
Limits are measured in fixed 60 second windows, per API key. If you exceed the limit, subsequent requests return 429 Too Many Requests until the window resets.
Rate limit headers
Every API response includes headers that tell you your current rate limit status:
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit | Your rate limit ceiling for the current window | 1000 |
X-RateLimit-Remaining | Requests remaining in the current window | 847 |
X-RateLimit-Reset | Unix timestamp when the window resets | 1726412400 |
X-Request-Id | Unique identifier for the request (handy when contacting support) | req_8f3a1c2d |
Reading headers in code
const response = await fetch('https://wittyform.com/api/v1/forms', {
headers: { 'Authorization': 'Bearer wf_live_abc123' },
});
const remaining = response.headers.get('X-RateLimit-Remaining');
const resetAt = new Date(
parseInt(response.headers.get('X-RateLimit-Reset')) * 1000
);
console.log(`${remaining} requests remaining. Resets at ${resetAt.toISOString()}`);Monitor your usage
GET /api/v1/usage
Rather than tracking the per-response headers yourself, you can ask the API for the calling key's own usage. Any active key may read its own usage (no specific scope is required). The response includes the key's per-minute rate limit plus a daily breakdown of request and error counts, so you can watch volume, spot a broken integration, and detect a leaked key.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
days | integer | 30 | How many days of history to return (1 to 90) |
Example request
curl -X GET "https://wittyform.com/api/v1/usage?days=7" \
-H "Authorization: Bearer wf_live_abc123"Example response
The daily array is ordered newest day first. total_requests and total_errors are summed over the requested window.
{
"success": true,
"data": {
"key_id": "8c1f2d3e-4a5b-4c6d-9e0f-112233445566",
"period_days": 7,
"rate_limit_per_minute": 1000,
"total_requests": 4213,
"total_errors": 17,
"daily": [
{ "day": "2025-09-15", "request_count": 642, "error_count": 3 },
{ "day": "2025-09-14", "request_count": 588, "error_count": 0 },
{ "day": "2025-09-13", "request_count": 601, "error_count": 5 }
]
}
}Handling 429 responses
When you hit the rate limit, the API returns a 429 status code with a Retry-After header indicating how many seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1726412400
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded"
}
}Retry with exponential backoff
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const retryAfter = parseInt(response.headers.get('Retry-After') || '1');
const backoff = retryAfter * 1000 * Math.pow(2, attempt);
console.log(`Rate limited. Retrying in ${backoff / 1000}s (attempt ${attempt + 1})`);
await new Promise((resolve) => setTimeout(resolve, backoff));
}
throw new Error('Max retries exceeded');
}Best practices
- Cache responses. If you are displaying form data on a dashboard, cache the API response for 30-60 seconds instead of fetching on every page load.
- Use pagination wisely. Fetch the maximum
per_page(100) to reduce the number of requests needed to retrieve all data. - Batch requests. Instead of making individual requests for each form, use the list endpoint with filters to get multiple resources in one call.
- Use webhooks for real-time data. Instead of polling the API for new submissions, set up a webhook to receive push notifications. This eliminates unnecessary API calls entirely.
- Monitor your usage. Check
X-RateLimit-Remainingin your responses and slow down proactively when you are nearing the limit. - Contact us for higher limits. If your use case requires more than 1,000 requests per minute, reach out to discuss an Enterprise plan with custom limits.