Quotas and limits
Here is everything that can refuse, delay or refund a send, with the value that applies today. These are settings, not constants: an administrator can change them, and the API always tells you which one you hit through its error code.
The numbers
| Limit | Value | Code |
|---|---|---|
| Requests per second, per API key | 10 | RATE_LIMITED · 429 |
| Messages per day, per account | Unlimited by default | QUOTA_EXCEEDED · 429 |
| Text length | 1600 characters | TEXT_TOO_LONG · 400 |
| Client reference length | 64 characters | VALIDATION_ERROR · 400 |
| Page size on GET /v1/sms | 20 by default, 100 maximum | — |
| Time a message may stay QUEUED | 24 hours | FAILED · TIMEOUT |
| Webhook attempt timeout | 10 seconds | — |
| Webhook retries | Up to 48 hours | EXHAUSTED |
Ten requests per second
The limit is enforced per API key with a token bucket: the bucket holds ten tokens and refills continuously at ten per second. A short burst of ten is fine; a sustained rate above ten is not. Over the limit, the API answers immediately:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
{ "code": "RATE_LIMITED", "message": "Trop de requêtes par seconde" }Nothing is charged and nothing is queued — a rate-limited request never happened. Two consequences worth designing for: send from a queue rather than from a web request, and give each environment its own key so a batch job in staging cannot throttle your production traffic.
// Send a batch without ever tripping the limiter.
async function sendAll(messages) {
for (const message of messages) {
let attempt = 0;
while (true) {
const res = await post('/v1/sms', message);
if (res.status !== 429 && res.status !== 503) break; // done, or a real error
// Retry-After is in seconds; fall back to an exponential delay.
const wait = Number(res.headers.get('Retry-After') ?? 2 ** attempt);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
attempt += 1;
}
await new Promise((resolve) => setTimeout(resolve, 120)); // ~8 requests/second
}
}The daily quota
The daily quota is a safety net against a runaway loop, not a commercial cap. It is disabled by default; when it is set, it counts messages per account over the UTC calendar day and returns 429 QUOTA_EXCEEDED once reached — with no Retry-After header, because the reset time is midnight UTC, not a number of seconds. Treat it as a hard stop for the day, not as something to retry in a loop.
The 24-hour lifetime
A message that is still QUEUED after 24 hours is marked FAILED with the reason TIMEOUT, and refunded. An SMS that arrives a day late is worse than no SMS at all: a login code, a delivery notice or an appointment reminder has no value once its moment has passed, so the API stops trying instead of surprising your user.
Billing and refunds
- You are charged when the API accepts the message — at the 202, not at delivery. The response returns
priceXafandbalanceAfterXaf, so you always know what a send cost and what is left. - A message that ends
FAILED, for any reason, is refunded automatically and exactly once, in the same transaction as the status change. You do not open a ticket, and you cannot be refunded twice for the same message. - A duplicate
clientReferenceis never charged again. See idempotency. - A rejected request — 400, 402, 429, 503 — costs nothing. In particular
OPERATOR_DISABLEDis checked before any charge. - Every movement appears in the ledger of the console, with its message identifier.
curl 'https://api.sms.gworldship.com/v1/balance' \
-H 'Authorization: Bearer gsk_your_api_key'
{ "balanceXaf": 4275 }A new account is credited with XAF 100 once its phone number is verified — roughly twenty messages to Orange or MTN, enough to finish an integration before paying anything.
What you can send, and to whom
- Cameroonian mobile numbers only:
+2376XXXXXXXX,2376XXXXXXXXor6XXXXXXXX. - A prefix outside the ART numbering plan is not rejected: the message goes through the default route, billed XAF 10 per segment. Check any number with the carrier lookup.
- 1600 characters is the ceiling for one request, not one segment. A long text is split and billed per segment — see how segments are counted.
- No custom sender name: the recipient sees the number of the SIM. G-SMS carries transactional traffic, not marketing campaigns.
Retention
The body of a message is erased 90 days after it was sent. Its metadata — recipient, operator, segments, price, status — is kept, because it is what your invoices and your statistics are built on. Keep the text on your side if you need it longer than that.
Ready to send your first SMS?
Create an account, get 100 FCFA of credit and an API key straight away.
Create account