Rate Limits
All COALS API endpoints are rate-limited per API key. Limits vary by service and plan. Every response includes headers so your client always knows its current standing.
Default Limits
Unless overridden by a plan-specific limit, each API key is allowed 1,000 requests within the rolling window. The window resets at the timestamp returned in the X-RateLimit-Reset header.
1,000
requests per window
per key
limits are not shared across keys
rolling
window resets progressively
Rate Limit Headers
These headers are present on every response — success or failure — so you can proactively throttle your client before hitting the limit.
X-RateLimit-Limit
The total number of requests allowed in the current window for this key.
X-RateLimit-Remaining
The number of requests remaining before the limit is hit.
X-RateLimit-Reset
Unix timestamp (UTC) when the current window resets and the counter returns to the limit.
Example Response Headers
HTTP/1.1 200 OK X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 847 X-RateLimit-Reset: 1740960000 Content-Type: application/json
429 — Too Many Requests
When the limit is exceeded the API returns 429 Too Many Requests. The response includes a Retry-After header indicating how many seconds to wait before the next request will succeed.
Response Headers
HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1740960000 Retry-After: 42
Response Body
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Retry after 42 seconds."
}
}
SimCoal — SMS Plan Limits
In addition to the global key limit, the SimCoal POST /send and POST /send-bulk endpoints enforce per-minute throughput caps based on your plan. This prevents carrier throttling and protects deliverability.
| Plan | SMS / Minute | Notes |
|---|---|---|
| Free | 1 / min | Best for testing and low-volume personal projects. |
| Pro | 60 / min | Suitable for production applications and bulk campaigns. |
| Enterprise | 300 / min | High-throughput workloads. Contact us for custom limits. |
Best Practices
Respect Retry-After
When you receive a 429, read the Retry-After header and wait exactly that many seconds before retrying. Ignoring it and retrying immediately will continue to return 429.
Implement Exponential Backoff
For transient 500 errors or network failures, retry with exponential backoff — start at 1 second, double each attempt, and cap at ~60 seconds. Add a small random jitter to avoid thundering herd.
// Example: backoff with jitter (JavaScript) const retry = async (fn, attempts = 5) => { for (let i = 0; i < attempts; i++) { try { return await fn(); } catch (err) { if (i === attempts - 1) throw err; const delay = Math.min(1000 * 2 ** i, 60000) + Math.random() * 500; await new Promise(r => setTimeout(r, delay)); } } };
Monitor X-RateLimit-Remaining
Read X-RateLimit-Remaining on every successful response. When it falls below a threshold (e.g. 10% of the limit), slow down your request rate proactively rather than waiting for a 429.
Use Bulk Endpoints
Where available (e.g. POST /simcoal/send-bulk), batch operations into a single request rather than sending many individual requests. This is more efficient and uses fewer rate limit slots.