Rate limits
GoSendAPI rate-limits at multiple levels: per-API-key, per-phone-number-throughput, and per-WABA-conversations. This guide covers all three.
Per-API-key (our limit)
Each API key has a default quota across all endpoints:
| Plan | Requests/sec | Requests/day |
|---|---|---|
Sandbox (gsk_test_) | 10 | 1,000 |
Standard (gsk_live_) | 100 | 100,000 |
| High Volume | 500 | 1,000,000 |
| Enterprise | Custom | Custom |
If you exceed your key’s RPS, we return 429:
{
"statusCode": 429,
"message": "Rate limit exceeded for API key",
"retry_after_seconds": 1
}Per-phone-number (Meta’s limit)
Each phone number has a throughput tier set by Meta:
| Tier | Limit | How to upgrade |
|---|---|---|
| Standard | 80 msg/s | Default for new numbers |
| Medium | 200 msg/s | Sustained green-quality traffic |
| High | 1,000 msg/s | Apply via Meta, requires consistent volume |
| Unlimited | No cap | Enterprise customers |
If you exceed the throughput, we still accept your request but queue it locally and send to Meta as the bucket refills. If queue gets too deep (>10,000 messages waiting on the same phone), we start returning 429.
See Phone numbers concept for upgrading tiers.
Per-WABA (Meta conversation limit)
A WABA has a daily limit on unique conversations:
| Tier | Unique business-initiated conversations/24h |
|---|---|
| Tier 1 | 1,000 |
| Tier 2 | 10,000 |
| Tier 3 | 100,000 |
| Tier 4 | Unlimited |
You start at Tier 1. Meta auto-upgrades when you consistently hit the cap with quality content (low block rate, low complaint rate).
Service conversations (user-initiated) don’t count toward this limit. Only business-initiated (you sending first via template).
Response headers
Every response includes:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1716030120| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your RPS limit (requests/second) |
X-RateLimit-Remaining | Requests remaining in current 1s window |
X-RateLimit-Reset | Unix timestamp when the window resets |
On 429 responses, also includes:
Retry-After: 1The value is seconds — sleep that long before retrying.
Backoff implementation
class GoSendAPIClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.queue = [];
}
async send(payload) {
return this._withBackoff(async () => {
const res = await fetch('https://cloud.gosendapi.com/v1/messages', {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json',
'Idempotency-Key': payload.idempotencyKey,
},
body: JSON.stringify(payload),
});
if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After') ?? 1);
await sleep(retryAfter * 1000);
throw new RetryError(`Rate limited, retry after ${retryAfter}s`);
}
if (!res.ok && res.status >= 500) {
throw new RetryError(`Server error ${res.status}`);
}
if (!res.ok) {
throw new PermanentError(await res.text());
}
return res.json();
});
}
async _withBackoff(fn, attempt = 1, maxAttempts = 5) {
try {
return await fn();
} catch (err) {
if (err instanceof PermanentError) throw err;
if (attempt >= maxAttempts) throw err;
const wait = Math.min(60_000, 1000 * Math.pow(2, attempt));
await sleep(wait + Math.random() * 500); // jitter
return this._withBackoff(fn, attempt + 1, maxAttempts);
}
}
}
const sleep = ms => new Promise(r => setTimeout(r, ms));Bulk sends (e.g. marketing campaigns)
For sending to >1000 recipients:
1. Use template + media reuse
Upload media once, get an id, reuse. See Sending messages → Media.
2. Spread over time
Don’t blast 10,000 messages in 1 second. Spread over 5-10 minutes (= ~30 msg/s). Reasons:
- Stays under throughput cap with margin for organic traffic
- Improves Meta delivery rate (anti-spam heuristics)
- Lets you abort mid-campaign if quality issues surface
3. Pre-validate recipients
Before sending to 10,000 phones, run them through your own deduper:
// Check which numbers are on WhatsApp via Meta's contact validation
// (Coming soon in API. For now, send and skip the ones that fail with RECIPIENT_NOT_ON_WHATSAPP)4. Use webhooks for status
Don’t poll GET /v1/messages/{id} for 10,000 messages. Subscribe to whatsapp.message.delivered / failed and accumulate.
Quota visibility
In the dashboard at app.gosendapi.com → Usage:
- Today’s API requests (vs limit)
- Today’s messages sent
- Per-phone throughput utilization
- Conversation tier and current consumption
Upgrading limits
Contact hello@gosendapi.com if you need:
- Higher API RPS (>500)
- Custom retention period for messages
- Multi-region request routing
- Dedicated phone-number pools
We respond within 1 business day with a custom quote.
What’s next
- Sending messages — patterns for high-volume sends
- Error handling — 429 and retryable error catalog