Skip to content

Limits & quotas

The ceilings the API enforces, and the error each one returns.

Four different ceilings can refuse the same call, and each one answers with a different status code. Reading the code tells you which one you hit before you read anything else.

CeilingApplies toRefusal
Amount for the currencyEvery payment and every transfer422 at creation
Mobile money per debitOne mobile money charge422 at processing
Account limits from KYCLive volume, in and out402 on a payment, 422 on a payout
Requests per minuteEvery call429

Plan quotas are a fifth kind, and they refuse the resource rather than the amount: payment links and invoices stop being creatable once the plan's allowance is used.

Amounts, per currency

One table governs both directions. POST /payments and POST /transfers read the same per-currency bounds, so a payout is refused by exactly the figures that would refuse a payment of the same size. Amounts are in major units.

CurrencyMinimumMaximum
XAFXAF252,000,000
XOFXOF252,000,000
NGNNGN100999,999.99
GHSGHS0.50999,999.99
KESKES0.50999,999.99
TZSTZS0.50999,999.99
UGXUGX1,000999,999,999
RWFRWF500999,999,999
GNFGNF1,000999,999,999
CDFCDF0.50999,999.99
ZARZAR0.50999,999.99
ZMWZMW0.50999,999.99
EGPEGP0.50999,999.99
EUREUR0.50999,999.99

Ninety-two currencies carry bounds, not only the ones above, and the API refuses an amount outside them.

422 on a 10 XAF payment
{
"code": 422,
"status": "Unprocessable Content",
"message": "Amount must be between 25 and 2000000 XAF.",
"errors": {
"amount": [
"The amount must be between 25 and 2000000 XAF."
]
}
}

The same refusal on POST /transfers reads Transfer amount must be between …, with the same two figures.

One mobile money debit is capped separately

The currency ceiling is what a payment may be worth. The mobile money ceiling is what a single debit on a wallet may be worth, and it is far lower.

RailCeiling per chargeWhere it comes from
Mobile MoneyMobile Money500,000 XAF or XOFOperator rules, per debit
CardsCardsThe currency ceilingNo separate cap
BankBankThe currency ceilingNo separate cap
CryptoCryptoThe currency ceilingNo separate cap

A mobile money charge above that cap is refused at processing time, not at creation. The payment stays pending and can be retried.

422 on a 900,000 XAF mobile money charge
{
"code": 422,
"status": "Unprocessable Content",
"message": "This transaction amount exceeds the per-transaction limit for cm.mtn. Use split_count or split_amounts to split the payment.",
"payer_message": "This amount is above the limit for this payment method. Use a smaller amount or another method.",
"errors": {
"request": [
"This transaction amount exceeds the per-transaction limit for cm.mtn. Use split_count or split_amounts to split the payment."
]
}
}

A ceiling per currency, the others converted

The cap is set per currency: 500,000 for XAF and XOF. A payment in another currency, such as USD or EUR, paid through a West or Central African wallet, is capped at the same figure converted at the current rate. Those wallets are offered on a USD or EUR payment by default; narrow the list with accept.

A payment above the cap can be paid in two to four charges, each under it, once you have turned on instalments: Split payments describes the calls. A single charge above it is refused, in live and in sandbox: nothing splits a payment on its own.

What your account is allowed to move

Above the per-call bounds sit limits of your own account: a single, daily and monthly ceiling for money in, and the same three for money out. They depend on your verification, your activity and Wajub's risk assessment, not on your plan. Your current limits are shown in the Dashboard; write to support to raise them.

Once your account is verified, a payment past one of its limits reads as a decline.

402 on a payment above the single-transaction ceiling
{
"code": 402,
"status": "Payment Required",
"error_code": "limit_exceeded",
"message": "Transaction amount 180 000 XAF exceeds your single-transaction limit of 150 000 XAF. Please contact support to increase your limit.",
"payer_message": "This payment exceeds the amount this merchant can currently accept."
}

Funds are held before they can be withdrawn

A credited payment lands in pending_balance and moves to available_balance when its hold elapses. The base hold follows the rail the money came in on.

RailDefault holdWhy
Mobile MoneyMobile Money24 hoursSettles and disputes fast on the operator side
BankBank48 hoursSlower reversal window
CardsCards72 hoursChargeback exposure
CryptoCrypto72 hoursChargeback exposure

The hold on your account can be shorter or longer than the default, depending on your activity and Wajub's risk assessment.

Payouts have their own brakes

Two mechanisms sit between a payout request and the money leaving, and they are independent of the amount ceilings above.

BrakeTriggerEffect
VelocityToo many payouts, or too large an amount paid out, in a short timeRefused with 422
ReviewWajub may hold a payout for review before it leavesHeld in review

These ceilings are set per account; write to support to raise them.

A held payout is not a failed one: it sits in review and moves on once approved. Treat review as a normal state in your reconciliation rather than an error.

Plan quotas

The plan governs resources and throughput, never amounts.

QuotaPay as you goGrowthScaleEnterprise
Requests per minute120360720Unlimited
Payment links25UnlimitedUnlimitedUnlimited
Invoices10UnlimitedUnlimitedUnlimited
Team members35UnlimitedUnlimited
Providers26UnlimitedUnlimited
Exports per month10UnlimitedUnlimitedUnlimited

Link and invoice allowances count live links and invoices only, and creating past them answers 403. Links and invoices exist in live only: with a sandbox key the API answers 403 (This feature is only available in live mode.).

Requests per minute

Three ceilings apply to every call made with a key, and the first one reached answers 429. All three follow your plan, and an Enterprise account has none of them. A call without a key is limited to 30 a minute.

CeilingPer minute
AccountYour plan's figure, sandbox and live apart
API keyYour plan's figure
Endpoint, per resourceSee below

Endpoint ceilings grow with the plan: three times the entry figure on Growth, six times on Scale. Every call under the resource counts, reads included, except reading a transfer.

ResourcePay as you goGrowthScaleEnterprise
/payments3090180No ceiling
/transfers2060120No ceiling
/refunds103060No ceiling
/customers50150300No ceiling

Two ceilings are fixed, the same on every plan, Enterprise included:

CallsCeiling
Payout writes: payouts, batches, beneficiaries and receivables20 a minute per account and environment
POST /beneficiaries/{id}/verify10 a minute and 200 a day per account in live, 10 a minute in sandbox

Resending an event by hand has a delay of its own, the same on every plan: see Webhook delivery. Rate limits carries the response headers, the two 429 body shapes of the rate limit, and the 429 of a resend inside that delay.

Field limits

Validation bounds you are most likely to hit, all from the request rules themselves.

FieldBound
expires.in5 to 43,200 minutes, default 1,440
description500 characters
reference128 characters
callback2,048 characters
Idempotency-Key128 characters, A-Za-z0-9._:-
per_page1 to 100, default 25
split_count2, 3 or 4
split_amounts2 to 4 amounts, each at least 0.01
Dispute evidence file10 MB, pdf jpg jpeg png doc docx txt
Dispute message5,000 characters

Webhook delivery

ParameterValue
Retry window72 hours after the first attempt
Not retried410 Gone
Request timeout10 seconds
Signature tolerance300 seconds
Resending one event to one endpoint by handOnce every 5 minutes, from Konsole or POST /events/{id}/resend

Answer inside the timeout or the delivery is retried, which is why acknowledging first and processing after is the shape to build.

A resend inside those 5 minutes skips that endpoint. When POST /events/{id}/resend finds every endpoint that has not accepted the event still inside the delay, it answers 429 with error_code: event_resend_cooldown and a Retry-After header giving the seconds left. It is not the rate limit: a resend before Retry-After gets the same answer, so do not let a client retry it on its own.

What did you think of this content?