Rate Limiting
ShipPulse API rate limits are applied per API key using a sliding window algorithm. Limits vary by plan and endpoint group.
Limits by plan
| Plan | /api/v1/* | /api/collect | /api/subscribe |
|---|---|---|---|
| Free | No API access | 10 req / 10 min | 5 req / 15 min |
| Starter | No API access | 20 req / 10 min | 5 req / 15 min |
| Pro | 100 req / min | 50 req / 10 min | 10 req / 15 min |
| Agency | 500 req / min | 200 req / 10 min | 20 req / 15 min |
Need higher limits? Contact us for enterprise quotas.
Rate limit headers
Every API response includes headers showing your current rate limit status:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1714000860
Retry-After: 13 ← only present on 429| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed in the current window |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Unix timestamp (seconds) when the window resets |
| Retry-After | Seconds to wait before retrying (only on 429 responses) |
Handling 429 Too Many Requests
When you receive a 429, always check the Retry-After header and wait that many seconds before retrying. Never retry immediately — you will stay rate-limited.
async function callWithRateLimitHandling(
url: string,
apiKey: string,
maxRetries = 5,
): Promise<Response> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (res.status !== 429) return res;
const retryAfter = Number(res.headers.get("Retry-After") ?? 60);
console.warn(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
}
throw new Error("Max retries exceeded after rate limiting");
}Best practices
Cache responses
For read-heavy operations (listing testimonials for a public widget), cache the API response on your server or CDN for 60–300 seconds. This dramatically reduces your API usage.
Batch writes
When importing testimonials or creating changelog entries in bulk, batch them in groups of 10–50 with a small delay between batches rather than sending all requests simultaneously.
Use webhooks instead of polling
Instead of polling for new testimonials or incidents, subscribe to webhooks. This eliminates polling traffic entirely. See the Webhooks guide.
Monitor X-RateLimit-Remaining
Log the X-RateLimit-Remaining header in your application. If it consistently drops to single digits, consider upgrading your plan or reducing request frequency.
See also: Error Handling · Webhooks