Rate Limits¶
The ValuePact API enforces rate limits to ensure fair usage and platform stability. Limits vary by plan tier and authentication method.
Who this is for¶
Developer Admin
Limit tiers¶
| Plan | Requests/minute | Burst | Concurrent |
|---|---|---|---|
| Starter | 100 | 20 | 5 |
| Professional | 500 | 50 | 10 |
| Enterprise | 2,000 | 200 | 25 |
| Enterprise Plus | Custom | Custom | Custom |
Burst behavior
Burst limits allow short spikes above the sustained rate. The bucket refills at the sustained rate per minute.
Per-endpoint limits¶
Some endpoints have additional limits:
| Endpoint | Limit |
|---|---|
POST /v1/initiatives | 10/minute |
POST /v1/business-cases | 10/minute |
POST /v1/bulk/* | 5/minute |
GET /v1/analytics/* | 30/minute |
POST /v1/webhooks | 20/minute |
Rate limit headers¶
Every API response includes rate limit headers:
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 489
X-RateLimit-Reset: 1717761600
X-RateLimit-Retry-After: 45
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
X-RateLimit-Retry-After | Seconds until you can retry |
Handling rate limits¶
When you hit a limit, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"request_id": "req_abc123"
}
}
Best practices¶
- Monitor headers: Check
X-RateLimit-Remainingbefore making requests. - Exponential backoff: Start with 1 second, double on each retry, max 60 seconds.
- Respect Retry-After: Use the header value when present.
- Batch operations: Use bulk endpoints instead of individual calls.
- Cache responses: Cache read-heavy data locally.
- Queue requests: Use a job queue for high-volume operations.
Backoff example¶
import time
import requests
def api_request_with_backoff(url, headers, max_retries=3):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
retry_after = int(response.headers.get('Retry-After', 2 ** attempt))
time.sleep(retry_after)
return response
Webhook rate limits¶
Webhook delivery has separate limits:
| Metric | Limit |
|---|---|
| Delivery attempts | 3 per event |
| Retry interval | 5 minutes between attempts |
| Timeout | 30 seconds per attempt |
| Payload size | 1 MB |
See Integrations → Webhooks for webhook-specific handling.
Increasing limits¶
To request a limit increase:
- Contact your account manager or support.
- Provide your use case, expected request volume, and peak patterns.
- For Enterprise Plus, limits are negotiated per contract.
Troubleshooting¶
I'm hitting limits during normal usage
Cause: Inefficient API usage pattern or missing caching. Resolution: Review your integration for: - Unnecessary polling — use webhooks instead. - Missing caching — cache read-heavy data. - N+1 queries — use list endpoints with filtering.
429 errors after a burst of imports
Cause: Bulk import exceeded burst limit. Resolution: Use the bulk import endpoint (POST /v1/bulk/import) which has higher limits. Spread imports over time or request a temporary limit increase.