Usage and rate limits
What a call costs, what the ceilings are, and how to stay inside them.
What a call costs
Nothing. Verification is free for every account, with no limit on the number of checks.
| Call | Cost | Limit |
|---|---|---|
Any /v1/verify/* endpoint | Free | Per-second ceiling on the key |
/v1/verify/*/batch | Free | Up to 100 items per call |
/v1/playground/* | Free | 3 checks per visitor per day without an account |
/v1/account/*, health probes | Free | — |
Each verify call still counts toward your usage — one check, or one per batch
item — so the dashboard can show what your integration is doing. The count is
reported as meta.credits_used on every response.
The playground allowance
The playground behind the landing page needs no account, so it gives each
visitor 3 free checks, which reset 24 hours after the first one. They are
counted against both the visitor's IP address and their browser fingerprint,
and whichever has used more decides, so clearing storage or switching networks
does not reset them. Once they are spent
the playground answers 429 with
free_checks_exhausted.
Create a free account to keep going: signed-in checks, in the playground and through an API key, are unlimited.
Reading your position
Every successful verification carries one header:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests per second allowed for the calling key |
For history, the dashboard's usage endpoint breaks down requests, checks and verdicts by day.
When you hit a limit
Exceeding the per-second ceiling returns 429 with
rate_limited.
Retry a 429 with exponential backoff and jitter — a fixed delay across many
workers just rebuilds the spike:
async function verify(value, attempt = 0) {
const res = await fetch(url, { ...options, body: JSON.stringify({ value }) });
if (res.status !== 429 || attempt >= 4) return res;
const wait = 2 ** attempt * 100 + Math.random() * 100;
await new Promise((resolve) => setTimeout(resolve, wait));
return verify(value, attempt + 1);
}Going faster
Batch instead of looping. One call with 100 items spends a single request against the rate limit, where 100 calls would spend 100. Each type has its own batch endpoint, so the items share one value type:
curl https://api.menaverify.com/v1/verify/phone/batch \
-H "Authorization: Bearer $MENAVERIFY_KEY" \
-H "Content-Type: application/json" \
-d '{"items": [{"country": "AE", "value": "+971501234567"}, {"country": "AE", "value": "+971502345678"}]}'An item the service cannot handle reports its own reason inside the envelope
instead of failing the whole request.
Filter locally first. Input that could never be valid — wrong length, impossible characters — can be rejected in your own code before it reaches the API.
Use a single API key for your integration and keep it in your backend service. Development and test suites should still use the same credential flow, but they should not be stored in browsers or client-side apps.