# MENAVerify — full documentation > Validate UAE phone numbers, Emirates IDs, Tax Registration Numbers and IBANs in a single low-latency API call, served from the region. MENAVerify validates phone numbers and identity, tax and bank numbers for MENA countries. A value is checked against the rules of the country it belongs to — numbering plan, checksum, structure and check digits — so it is a structural check, not a lookup against a named person or account holder. Every endpoint answers with the same envelope: the top level is identical for each country and document type, and anything country or type specific lives under `details`. - API base URL: `https://api.menaverify.com` - Authentication: `Authorization: Bearer ` or `X-API-Key: ` - Get an API key: https://menaverify.com/signup (free, with unlimited checks) - OpenAPI 3.1.0 schema: https://api.menaverify.com/openapi.json - Docs: https://menaverify.com/docs - Index for models: https://menaverify.com/llms.txt - Full text for models: https://menaverify.com/llms-full.txt ## Guides ### Introduction Validate phone numbers, Emirates IDs, tax registration numbers and IBANs across MENA in a single call. Source: https://menaverify.com/docs MENAVerify checks an identifier against the rules of the country it belongs to and tells you whether it could exist — the numbering plan a phone number sits in, the checksum behind an Emirates ID, the structure of a TRN, the IBAN's country format and check digits. It is a structural check, not a lookup against a person or an account holder. #### One envelope for everything Every verification returns the same shape, whatever the country or document type. The top level never changes, so a client can be written once: ```json { "type": "phone", "valid": true, "input": "+971 50 123 4567", "normalized": "+971501234567", "country": { "code": "AE", "name": "United Arab Emirates" }, "formatted": { "national": "050 123 4567", "international": "+971 50 123 4567" }, "details": { "line_type": "mobile", "carrier": { "name": "Etisalat" } } } ``` Anything specific to a country or document type lives under `details`. **A rejected value is not an error** A value that fails its checks still returns `200`. Read `valid` and `reason` from the body — the status code describes the request, not the identifier. See [Reading a result](https://menaverify.com/docs/results). #### What you can verify | Type | Endpoint | Checks | | ----------------------- | ------------------------ | ------------------------------------------------------------ | | Phone | `/v1/verify/phone` | Numbering plan, line type, carrier the range is allocated to | | Emirates ID | `/v1/verify/national-id` | Structure, birth year plausibility, checksum | | Tax registration number | `/v1/verify/tax-id` | Structure and checksum | | IBAN | `/v1/verify/iban` | Country format, length and check digits | Each type also has a batch endpoint — [`/v1/verify/phone/batch`](https://menaverify.com/docs/api/verify/verifyPhoneBatch), [`/v1/verify/national-id/batch`](https://menaverify.com/docs/api/verify/verifyNationalIDBatch), [`/v1/verify/tax-id/batch`](https://menaverify.com/docs/api/verify/verifyTaxIDBatch) and [`/v1/verify/iban/batch`](https://menaverify.com/docs/api/verify/verifyIBANBatch) — that takes up to 100 values of that type in one call. The United Arab Emirates is the only country in the registry today, so a client can validate against its document formats — length, pattern and expected characters — and reject obvious mistakes locally before making a call. #### Start here - [Quickstart](https://menaverify.com/docs/quickstart): Your first verification in a few minutes. - [Authentication](https://menaverify.com/docs/authentication): API keys and session tokens. - [Reading a result](https://menaverify.com/docs/results): What valid, reason and details mean. - [API reference](https://menaverify.com/docs/api/verify/verifyPhone): Every endpoint, generated from the schema. ### Quickstart Verify your first number in a few minutes. Source: https://menaverify.com/docs/quickstart ##### Get an API key Create one in the dashboard under [API keys](https://menaverify.com/dashboard/keys). You will see the full key exactly once — store it as a secret. All API keys are live keys. Verification is free, with no limit on checks. ##### Make a call Send the key as a bearer token and the value in the body: **cURL** ```bash curl https://api.menaverify.com/v1/verify/phone \ -H "Authorization: Bearer $MENAVERIFY_KEY" \ -H "Content-Type: application/json" \ -d '{"value": "+971 50 123 4567"}' ``` **Node** ```js const res = await fetch("https://api.menaverify.com/v1/verify/phone", { method: "POST", headers: { Authorization: `Bearer ${process.env.MENAVERIFY_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ value: "+971 50 123 4567" }), }); const { valid, normalized, reason } = await res.json(); ``` **Python** ```python import os, httpx res = httpx.post( "https://api.menaverify.com/v1/verify/phone", headers={"Authorization": f"Bearer {os.environ['MENAVERIFY_KEY']}"}, json={"value": "+971 50 123 4567"}, ) result = res.json() ``` **Go** ```go body := strings.NewReader(`{"value":"+971 50 123 4567"}`) req, _ := http.NewRequest(http.MethodPost, "https://api.menaverify.com/v1/verify/phone", body) req.Header.Set("Authorization", "Bearer "+os.Getenv("MENAVERIFY_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) ``` Every verify endpoint also accepts `GET` with a `value` query parameter, which is handy for a quick check from a terminal: ```bash curl "https://api.menaverify.com/v1/verify/phone?value=%2B971501234567" \ -H "Authorization: Bearer $MENAVERIFY_KEY" ``` ##### Read the result ```json { "type": "phone", "valid": true, "input": "+971 50 123 4567", "normalized": "+971501234567", "country": { "code": "AE", "name": "United Arab Emirates" }, "details": { "line_type": "mobile", "carrier": { "name": "Etisalat" } } } ``` Branch on `valid`, store `normalized`, and show `reason` when you need to explain a rejection. The full contract is in [Reading a result](https://menaverify.com/docs/results). #### Without an account `POST /v1/playground/phone` verifies a phone number with no key at all. It gives each visitor 3 free checks, counted per IP address and per browser fingerprint, and is meant for trying the service, not for integrating — everything else needs a key, and [an account is free](https://menaverify.com/signup). #### Next * [Authentication](https://menaverify.com/docs/authentication) — keys, headers and session tokens * [Errors](https://menaverify.com/docs/errors) — what each status and code means * [Usage and rate limits](https://menaverify.com/docs/credits-and-limits) — what a call costs ### Authentication API keys for verification, session tokens for the dashboard. Source: https://menaverify.com/docs/authentication There are two kinds of credential, and they are not interchangeable. #### API keys Everything under `/v1/verify` takes an API key. Send it as a bearer token: ```bash curl https://api.menaverify.com/v1/verify/iban \ -H "Authorization: Bearer $MENAVERIFY_KEY" \ -H "Content-Type: application/json" \ -d '{"value": "AE070331234567890123456"}' ``` Or as its own header, which some HTTP clients handle more comfortably: ```bash curl https://api.menaverify.com/v1/verify/iban \ -H "X-API-Key: $MENAVERIFY_KEY" \ -H "Content-Type: application/json" \ -d '{"value": "AE070331234567890123456"}' ``` Both are equivalent — pick one. ##### API key format | Prefix | Cost | Use it for | | ---------- | ---------------------- | ------------------------------ | | `mv_live_` | Free, unlimited checks | Production and integration use | All API keys use the same live validation flow. Verification is free, and each key is held to a per-second rate limit. ##### Handling keys A key is shown in full exactly once, when you create it. Only a prefix is stored afterwards, so a lost key has to be replaced rather than recovered. **Server-side only** A key sent from a browser or a mobile app is public. Call the API from your backend and pass the result to the client. Rotating is safe to do gradually: create the replacement, deploy it, then [revoke](https://menaverify.com/docs/api/account/revokeAPIKey) the old one. Revocation takes effect immediately. #### Session tokens The dashboard endpoints under `/v1/auth` and `/v1/account` — usage and key management — take the session token returned by dashboard sign-in, not an API key: ```bash curl https://api.menaverify.com/v1/account/usage \ -H "Authorization: Bearer $MENAVERIFY_SESSION" ``` Session tokens carry the `mvs_` prefix. They exist for the dashboard; an integration should not need them. #### What a rejected credential looks like A missing, malformed or revoked credential returns `401` with the [`unauthorized`](https://menaverify.com/docs/errors#unauthorized) code. Using a session token where an API key is expected — or the reverse — returns [`forbidden`](https://menaverify.com/docs/errors#forbidden). ### Reading a result What valid, reason, normalized and details mean. Source: https://menaverify.com/docs/results Every verification returns the same envelope. The top level is identical for each country and document type; anything specific lives under `details`. #### The envelope | Field | Always present | Meaning | | ------------ | -------------- | ---------------------------------------------------------------------- | | `type` | Yes | Which document was checked: `phone`, `national_id`, `tax_id` or `iban` | | `valid` | Yes | Whether the value passed every check for its country and type | | `input` | Yes | The value exactly as you submitted it | | `normalized` | When valid | The value stripped of formatting — E.164 for phone numbers | | `country` | When resolved | Code, name and calling code | | `formatted` | Phone | National and international renderings | | `reason` | When invalid | Why the value was rejected | | `details` | Varies | Fields specific to the country and type | Store `normalized`, not `input`. It is the form that compares and deduplicates cleanly. #### valid is not an error **Status codes describe the request** A value that fails its checks is a successful request: `200`, with `valid` set to `false` and a `reason`. Only a malformed request, a bad credential, an empty balance or a throttle produce a non-2xx status. ```js const res = await fetch(url, options); // The call worked; the number may still be nonsense. if (!res.ok) throw new Error("request failed"); const result = await res.json(); if (!result.valid) { showMessage(explain(result.reason)); } ``` #### Reason codes Reasons say why one value was rejected. They are add-only and never renamed, so matching on them is safe — but treat an unrecognised code as a generic rejection rather than crashing. | Code | Meaning | | --------------------- | ----------------------------------------------------- | | `empty_input` | Nothing was provided | | `invalid_characters` | Contains characters that do not belong in this format | | `too_short` | Too short for this format | | `too_long` | Too long for this format | | `wrong_country` | Belongs to a different country than the one resolved | | `invalid_prefix` | The prefix does not exist in the numbering plan | | `unallocated_prefix` | The prefix exists but is not in use | | `unknown_area_code` | The area code is not recognised | | `invalid_year` | The encoded year is not plausible | | `checksum_failed` | The check digits do not match the rest of the value | | `unsupported_country` | That country is not supported yet | | `unsupported_type` | Unknown document type | #### Details by type `details` follows `type`: * **Phone** — `line_type` (`mobile`, `fixed_line`, `toll_free`, `premium_rate`, `shared_cost`, `unknown`) and the `carrier` the range is allocated to, including whether it is an MVNO and the network it rides on. * **Emirates ID** — the parts the number encodes, including birth year. * **Tax registration number** — structural breakdown of the TRN. * **IBAN** — bank and branch identifiers, and the account number. Carrier data reflects the range a number was allocated to. It does not follow portability, so it answers "which network was this range issued to" rather than "who serves this subscriber today". The exact fields for each type are in the [API reference](https://menaverify.com/docs/api/verify/verifyPhone), generated from the schema. #### Checking cheaply The United Arab Emirates is the only supported country today, so you can validate against its document formats — length, pattern and expected characters — client-side. Reject obviously malformed input in your own UI before making a call. ### Errors Every error code the API can return, and what to do about each one. Source: https://menaverify.com/docs/errors A failed request returns the same [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) problem document, served as `application/problem+json`, whatever went wrong: ```json { "type": "https://menaverify.com/docs/errors#rate_limited", "title": "Too Many Requests", "status": 429, "detail": "Rate limit of 100 requests per second exceeded.", "instance": "/v1/verify/phone", "code": "rate_limited" } ``` `code` is stable and safe to branch on. `detail` is written to be shown to a person. `type` links to the section below. **An invalid value is not an error** A value that fails its checks returns `200` with `valid: false`. See [Reading a result](https://menaverify.com/docs/results). #### Codes ##### invalid\_request **400** — The request itself is malformed: a missing `value`, a body that is not valid JSON, a batch over 100 items, or a country code the API cannot parse. Fix the call. Retrying unchanged will fail the same way. ##### unauthorized **401** — No credential, or one that is not recognised. The key may be mistyped, revoked, or missing its `Bearer ` prefix. Check [Authentication](https://menaverify.com/docs/authentication). ##### forbidden **403** — The credential is valid but not allowed here. Usually a session token sent to `/v1/verify`, or an API key sent to a dashboard endpoint. ##### not\_found **404** — No such route or resource — for example revoking a key id that does not exist, or asking for a country outside the registry. ##### rate\_limited **429** — You went over the per-second ceiling for the key. The allowance is on every successful response as `X-RateLimit-Limit`. Back off and retry: wait, then retry with exponential backoff and jitter. See [Usage and rate limits](https://menaverify.com/docs/credits-and-limits). ##### free\_checks\_exhausted **429** — Only from the [playground](https://menaverify.com/docs/credits-and-limits#the-playground-allowance): an anonymous visitor has used all 3 free checks. The allowance is counted per IP address and per browser fingerprint. [Create a free account](https://menaverify.com/signup) to keep going. Signed-in checks are unlimited, and API keys are never subject to this limit. ##### unsupported\_country **400** — The resolved country is not in the registry. The error carries `country` (what was resolved) and `supported` (what would have worked). Only the United Arab Emirates is supported today. ##### internal\_error **500** — Something failed on our side. Safe to retry with backoff. If it persists, the health of the service is on the [status page](https://menaverify.com/status). #### Retrying | Code | Retry | Why | | ---------------------------- | ----------------- | ------------------------------------------------- | | `rate_limited` | Yes, with backoff | The ceiling is per second | | `internal_error` | Yes, with backoff | Transient by definition | | `free_checks_exhausted` | After signing up | The playground allowance does not refill on retry | | `invalid_request` | No | Fix the request | | `unauthorized` / `forbidden` | No | Fix the credential | | `not_found` | No | Fix the route or id | Verification is a pure check with no side effects, so a retried call is safe. ### Usage and rate limits What a call costs, what the ceilings are, and how to stay inside them. Source: https://menaverify.com/docs/credits-and-limits #### 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`](https://menaverify.com/docs/errors#free_checks_exhausted). [Create a free account](https://menaverify.com/signup) 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](https://menaverify.com/docs/api/account/getUsage) endpoint breaks down requests, checks and verdicts by day. #### When you hit a limit Exceeding the per-second ceiling returns `429` with [`rate_limited`](https://menaverify.com/docs/errors#rate_limited). Retry a `429` with exponential backoff and jitter — a fixed delay across many workers just rebuilds the spike: ```js 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: ```bash 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. ## API reference ### Verify Validate a value against a country's rules. #### POST /v1/verify/iban Verify an IBAN - Operation ID: `verifyIBAN` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyIBAN Confirms the IBAN is a valid account for that country, splits out the bank code and resolves it to the bank holding it, with its legal name, SWIFT/BIC code and logo. Free for every account, and counts as one check toward usage. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "AE07 0331 2345 6789 0123 456"] Responses: - `200` — The verification result. A value that fails validation is still a 200: check the valid field. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (IBANDetails) — Bank account half of a Result. - `account_number` (string) — Domestic account number taken from the BBAN. - `bank` (Bank) — The institution a domestic bank code resolves to, reported as part of an IBAN verification. It is omitted when the code is not one the registry holds, which does not affect the IBAN's verdict. - `bank_code` (string) — Domestic bank identifier taken from the BBAN. [example "033"] - `check_digits` (string) — The two IBAN check digits. - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/iban/batch Verify an IBAN in bulk - Operation ID: `verifyIBANBatch` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyIBANBatch Validates up to 100 values of this type in one call, each counting as one check toward usage. A value that fails validation reports its reason inside the result envelope instead of failing the request. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `items` (array of IBANBatchItem, required) — The values to verify. Each one counts as one check toward usage. [min items 1; max items 100] - `country` (CountryCode, required) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "AE07 0331 2345 6789 0123 456"] Responses: - `200` — One result per item, in request order. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `results` (array of IBANBatchItemResult, required) — One entry per request item, in order. - `country` (Country) — The country a value resolved to. - `details` (IBANDetails) — Bank account half of a Result. - `formatted` (Formatted) — Display forms of a valid value. - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `summary` (BatchSummary, required) — Counts across the batch. - `invalid` (integer, required) - `total` (integer, required) - `valid` (integer, required) - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/national-id Verify a national identity number - Operation ID: `verifyNationalID` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyNationalID Confirms the number is a genuine national identity number and reports the encoded birth year. Free for every account, and counts as one check toward usage. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "784-1990-1234567-6"] Responses: - `200` — The verification result. A value that fails validation is still a 200: check the valid field. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (NationalIDDetails) — National identity document half of a Result. - `birth_year` (integer) — Year encoded in the number. - `check_digit` (string) — The trailing check digit. - `scheme` (string, required) — Name of the national scheme. [example "Emirates ID"] - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/national-id/batch Verify a national identity number in bulk - Operation ID: `verifyNationalIDBatch` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyNationalIDBatch Validates up to 100 values of this type in one call, each counting as one check toward usage. A value that fails validation reports its reason inside the result envelope instead of failing the request. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `items` (array of NationalIDBatchItem, required) — The values to verify. Each one counts as one check toward usage. [min items 1; max items 100] - `country` (CountryCode, required) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "784-1990-1234567-6"] Responses: - `200` — One result per item, in request order. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `results` (array of NationalIDBatchItemResult, required) — One entry per request item, in order. - `country` (Country) — The country a value resolved to. - `details` (NationalIDDetails) — National identity document half of a Result. - `formatted` (Formatted) — Display forms of a valid value. - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `summary` (BatchSummary, required) — Counts across the batch. - `invalid` (integer, required) - `total` (integer, required) - `valid` (integer, required) - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/phone Verify a phone number - Operation ID: `verifyPhone` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyPhone Confirms the number is a usable line in that country and returns the line type and the carrier the range is allocated to. Free for every account, and counts as one check toward usage. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "+971 50 123 4567"] Responses: - `200` — The verification result. A value that fails validation is still a 200: check the valid field. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (PhoneDetails) — Phone-specific half of a Result. - `areas` (array of string) — Administrative divisions the area code serves. - `carrier` (Carrier) — The operator a mobile prefix is allocated to. - `line_type` (LineType, required) — The service a phone number belongs to. [one of "mobile", "fixed_line", "toll_free", "premium_rate", "shared_cost", "unknown"] - `portable` (boolean, required) — Number portability applies, so the carrier is the range holder rather than the number's current network. - `prefix` (string) — The allocated range the number falls in. [example "50"] - `region` (string) — Region served by a fixed-line area code. - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/phone/batch Verify a phone number in bulk - Operation ID: `verifyPhoneBatch` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyPhoneBatch Validates up to 100 values of this type in one call, each counting as one check toward usage. A value that fails validation reports its reason inside the result envelope instead of failing the request. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `items` (array of PhoneBatchItem, required) — The values to verify. Each one counts as one check toward usage. [min items 1; max items 100] - `country` (CountryCode, required) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "+971 50 123 4567"] Responses: - `200` — One result per item, in request order. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `results` (array of PhoneBatchItemResult, required) — One entry per request item, in order. - `country` (Country) — The country a value resolved to. - `details` (PhoneDetails) — Phone-specific half of a Result. - `formatted` (Formatted) — Display forms of a valid value. - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `summary` (BatchSummary, required) — Counts across the batch. - `invalid` (integer, required) - `total` (integer, required) - `valid` (integer, required) - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/tax-id Verify a tax registration number - Operation ID: `verifyTaxID` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyTaxID Confirms the number belongs to the national tax registration scheme before it reaches an invoice. Free for every account, and counts as one check toward usage. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "100-1234-5678-9012"] Responses: - `200` — The verification result. A value that fails validation is still a 200: check the valid field. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (TaxIDDetails) — Tax registration half of a Result. - `checksum` (boolean, required) — Whether the scheme carries a verifiable check digit of its own, which the response reports for transparency. - `scheme` (string, required) — Name of the tax scheme. [example "UAE TRN"] - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. #### POST /v1/verify/tax-id/batch Verify a tax registration number in bulk - Operation ID: `verifyTaxIDBatch` - Auth: `Authorization: Bearer ` (apiKeyBearer) or `X-API-Key: ` (apiKeyHeader) - Docs: https://menaverify.com/docs/api/verify/verifyTaxIDBatch Validates up to 100 values of this type in one call, each counting as one check toward usage. A value that fails validation reports its reason inside the result envelope instead of failing the request. The country is taken from the country field, then from the value itself when it carries a marker, then from the service default. Request body (`application/json`, required): - `items` (array of TaxIDBatchItem, required) — The values to verify. Each one counts as one check toward usage. [min items 1; max items 100] - `country` (CountryCode, required) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "100-1234-5678-9012"] Responses: - `200` — One result per item, in request order. - `meta` (Meta, required) — Usage counters for the call. Verification is free, so nothing is deducted. - `credits_used` (integer (int64), required) — Checks this request counted toward usage: one, or one per batch item. - `results` (array of TaxIDBatchItemResult, required) — One entry per request item, in order. - `country` (Country) — The country a value resolved to. - `details` (TaxIDDetails) — Tax registration half of a Result. - `formatted` (Formatted) — Display forms of a valid value. - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `summary` (BatchSummary, required) — Counts across the batch. - `invalid` (integer, required) - `total` (integer, required) - `valid` (integer, required) - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `429` — The caller exceeded its per-second ceiling. - `500` — Something failed on our side. ### Account Usage and API keys. #### GET /v1/account Get the current account - Operation ID: `getAccount` - Auth: `Authorization: Bearer ` (sessionBearer) - Docs: https://menaverify.com/docs/api/account/getAccount Responses: - `200` — The account behind the session. - `created_at` (string (date-time), required) - `credit_balance` (integer (int64), required) — Legacy credit balance. Verification no longer draws on it. - `email` (string (email), required) - `id` (string (uuid), required) - `name` (string, required) - `plan` (string, required) — Subscription plan. [example "free"] - `401` — The credential is missing, malformed or revoked. #### GET /v1/account/keys List API keys - Operation ID: `listAPIKeys` - Auth: `Authorization: Bearer ` (sessionBearer) - Docs: https://menaverify.com/docs/api/account/listAPIKeys Responses: - `200` — The account's active keys. - `keys` (array of APIKey, required) — Active keys, newest first. - `created_at` (string (date-time), required) - `environment` ("live", required) — Every API key is a live credential. - `id` (string (uuid), required) - `last_used_at` (string (date-time)) — Null until the key authenticates a request. - `name` (string, required) — Label you gave the key. - `prefix` (string, required) — Leading segment of the key, safe to display. [example "mv_live_a1b2c3d4"] - `rate_limit` (integer, required) — Requests per second allowed for this key. - `401` — The credential is missing, malformed or revoked. - `500` — Something failed on our side. #### POST /v1/account/keys Create an API key - Operation ID: `createAPIKey` - Auth: `Authorization: Bearer ` (sessionBearer) - Docs: https://menaverify.com/docs/api/account/createAPIKey Returns the secret exactly once. Only its digest is stored, so a lost key has to be revoked and replaced. Request body (`application/json`, required): - `environment` ("live") [default "live"] - `name` (string, required) — Label so the key stays identifiable. [max length 60] Responses: - `201` — The new key and its plaintext secret. - `key` (APIKey, required) — An API key. The secret itself is never returned after creation. - `created_at` (string (date-time), required) - `environment` ("live", required) — Every API key is a live credential. - `id` (string (uuid), required) - `last_used_at` (string (date-time)) — Null until the key authenticates a request. - `name` (string, required) — Label you gave the key. - `prefix` (string, required) — Leading segment of the key, safe to display. [example "mv_live_a1b2c3d4"] - `rate_limit` (integer, required) — Requests per second allowed for this key. - `plaintext` (string, required) — The secret. Returned exactly once and never stored. [example "mv_live_..."] - `warning` (string) - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `409` — The request collides with the current state. - `500` — Something failed on our side. #### DELETE /v1/account/keys/{keyID} Revoke an API key - Operation ID: `revokeAPIKey` - Auth: `Authorization: Bearer ` (sessionBearer) - Docs: https://menaverify.com/docs/api/account/revokeAPIKey Takes effect on the next request the key makes. Parameters: - `keyID` (path, required) — Identifier of the key to revoke. Responses: - `204` — The key is revoked. - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `404` — No such resource. - `500` — Something failed on our side. #### GET /v1/account/ledger Credit ledger - Operation ID: `getLedger` - Auth: `Authorization: Bearer ` (sessionBearer) - Docs: https://menaverify.com/docs/api/account/getLedger The append-only record of credit movements, most recent first. Parameters: - `limit` (query) — How many entries to return. Responses: - `200` — The recent movements. - `balance` (integer (int64), required) — Current balance. - `entries` (array of LedgerEntry, required) — Most recent movements first. - `balance_after` (integer (int64), required) — Balance immediately after the movement. - `created_at` (string (date-time), required) - `delta` (integer (int64), required) — Signed change. Negative for a debit. - `reason` (string, required) — What caused the movement. [example "phone_verify"] - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `500` — Something failed on our side. #### GET /v1/account/usage Daily usage - Operation ID: `getUsage` - Auth: `Authorization: Bearer ` (sessionBearer) - Docs: https://menaverify.com/docs/api/account/getUsage Aggregates requests, checks and verdicts over a trailing window. Parameters: - `days` (query) — Size of the window. Responses: - `200` — The aggregated window. - `credit_balance` (integer (int64), required) - `days` (integer, required) — Size of the window that was aggregated. - `points` (array of UsagePoint, required) — One entry per day with traffic. - `credits` (integer (int64), required) - `day` (string (date-time), required) — Start of the day, UTC. - `invalid` (integer (int64), required) — Requests that returned valid: false. - `requests` (integer (int64), required) - `valid` (integer (int64), required) — Requests that returned valid: true. - `total_credits` (integer (int64), required) - `total_requests` (integer (int64), required) - `400` — The request was rejected before any work was done. - `401` — The credential is missing, malformed or revoked. - `500` — Something failed on our side. ### Playground Try a verification without an account. A few free checks per visitor. #### POST /v1/playground/iban Try an IBAN without an account - Operation ID: `playgroundIBAN` - Auth: none (public) - Docs: https://menaverify.com/docs/api/playground/playgroundIBAN The trial behind the landing page. An anonymous visitor gets a few free checks, counted against both their IP address and their browser fingerprint; once they are spent the call answers 429 with code free_checks_exhausted until the visitor signs up. A request that is rejected before verification, such as an empty value, does not spend a check. Not meant for integration: use an API key for that. Parameters: - `X-Client-Fingerprint` (header) — A stable hash of the visitor's browser, sent by the landing page. Free checks are counted against it as well as the IP address, and whichever has used more decides. Omit it and only the IP address is counted. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "AE07 0331 2345 6789 0123 456"] Responses: - `200` — The verification result. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (IBANDetails) — Bank account half of a Result. - `account_number` (string) — Domestic account number taken from the BBAN. - `bank` (Bank) — The institution a domestic bank code resolves to, reported as part of an IBAN verification. It is omitted when the code is not one the registry holds, which does not affect the IBAN's verdict. - `bank_code` (string) — Domestic bank identifier taken from the BBAN. [example "033"] - `check_digits` (string) — The two IBAN check digits. - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `demo` (boolean, required) — Always true. - `quota` (PlaygroundQuota) — An anonymous visitor's free playground allowance. - `limit` (integer (int64), required) — Free checks a visitor gets before being asked to sign up. - `remaining` (integer (int64), required) — Free checks left. At zero, the visitor needs an account. - `used` (integer (int64), required) — Free checks already spent. - `400` — The request was rejected before any work was done. - `429` — Either the caller exceeded its per-second ceiling (code rate_limited) or an anonymous visitor has used every free check (code free_checks_exhausted) and needs an account to continue. #### POST /v1/playground/national-id Try a national identity number without an account - Operation ID: `playgroundNationalID` - Auth: none (public) - Docs: https://menaverify.com/docs/api/playground/playgroundNationalID The trial behind the landing page. An anonymous visitor gets a few free checks, counted against both their IP address and their browser fingerprint; once they are spent the call answers 429 with code free_checks_exhausted until the visitor signs up. A request that is rejected before verification, such as an empty value, does not spend a check. Not meant for integration: use an API key for that. Parameters: - `X-Client-Fingerprint` (header) — A stable hash of the visitor's browser, sent by the landing page. Free checks are counted against it as well as the IP address, and whichever has used more decides. Omit it and only the IP address is counted. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "784-1990-1234567-6"] Responses: - `200` — The verification result. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (NationalIDDetails) — National identity document half of a Result. - `birth_year` (integer) — Year encoded in the number. - `check_digit` (string) — The trailing check digit. - `scheme` (string, required) — Name of the national scheme. [example "Emirates ID"] - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `demo` (boolean, required) — Always true. - `quota` (PlaygroundQuota) — An anonymous visitor's free playground allowance. - `limit` (integer (int64), required) — Free checks a visitor gets before being asked to sign up. - `remaining` (integer (int64), required) — Free checks left. At zero, the visitor needs an account. - `used` (integer (int64), required) — Free checks already spent. - `400` — The request was rejected before any work was done. - `429` — Either the caller exceeded its per-second ceiling (code rate_limited) or an anonymous visitor has used every free check (code free_checks_exhausted) and needs an account to continue. #### POST /v1/playground/phone Try a phone number without an account - Operation ID: `playgroundPhone` - Auth: none (public) - Docs: https://menaverify.com/docs/api/playground/playgroundPhone The trial behind the landing page. An anonymous visitor gets a few free checks, counted against both their IP address and their browser fingerprint; once they are spent the call answers 429 with code free_checks_exhausted until the visitor signs up. A request that is rejected before verification, such as an empty value, does not spend a check. Not meant for integration: use an API key for that. Parameters: - `X-Client-Fingerprint` (header) — A stable hash of the visitor's browser, sent by the landing page. Free checks are counted against it as well as the IP address, and whichever has used more decides. Omit it and only the IP address is counted. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "+971 50 123 4567"] Responses: - `200` — The verification result. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (PhoneDetails) — Phone-specific half of a Result. - `areas` (array of string) — Administrative divisions the area code serves. - `carrier` (Carrier) — The operator a mobile prefix is allocated to. - `line_type` (LineType, required) — The service a phone number belongs to. [one of "mobile", "fixed_line", "toll_free", "premium_rate", "shared_cost", "unknown"] - `portable` (boolean, required) — Number portability applies, so the carrier is the range holder rather than the number's current network. - `prefix` (string) — The allocated range the number falls in. [example "50"] - `region` (string) — Region served by a fixed-line area code. - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `demo` (boolean, required) — Always true. - `quota` (PlaygroundQuota) — An anonymous visitor's free playground allowance. - `limit` (integer (int64), required) — Free checks a visitor gets before being asked to sign up. - `remaining` (integer (int64), required) — Free checks left. At zero, the visitor needs an account. - `used` (integer (int64), required) — Free checks already spent. - `400` — The request was rejected before any work was done. - `429` — Either the caller exceeded its per-second ceiling (code rate_limited) or an anonymous visitor has used every free check (code free_checks_exhausted) and needs an account to continue. #### GET /v1/playground/quota Check the free allowance - Operation ID: `getPlaygroundQuota` - Auth: none (public) - Docs: https://menaverify.com/docs/api/playground/getPlaygroundQuota How many free playground checks the calling visitor has left, counted the same way as the playground itself. Spends nothing. Parameters: - `X-Client-Fingerprint` (header) — A stable hash of the visitor's browser, sent by the landing page. Free checks are counted against it as well as the IP address, and whichever has used more decides. Omit it and only the IP address is counted. Responses: - `200` — The visitor's allowance. - `limit` (integer (int64), required) — Free checks a visitor gets before being asked to sign up. - `remaining` (integer (int64), required) — Free checks left. At zero, the visitor needs an account. - `used` (integer (int64), required) — Free checks already spent. - `429` — The caller exceeded its per-second ceiling. #### POST /v1/playground/tax-id Try a tax registration number without an account - Operation ID: `playgroundTaxID` - Auth: none (public) - Docs: https://menaverify.com/docs/api/playground/playgroundTaxID The trial behind the landing page. An anonymous visitor gets a few free checks, counted against both their IP address and their browser fingerprint; once they are spent the call answers 429 with code free_checks_exhausted until the visitor signs up. A request that is rejected before verification, such as an empty value, does not spend a check. Not meant for integration: use an API key for that. Parameters: - `X-Client-Fingerprint` (header) — A stable hash of the visitor's browser, sent by the landing page. Free checks are counted against it as well as the IP address, and whichever has used more decides. Omit it and only the IP address is counted. Request body (`application/json`, required): - `country` (CountryCode) — ISO 3166-1 alpha-2 country code. [min length 2; max length 2; one of "AE"] - `value` (string, required) — The value to verify. [example "100-1234-5678-9012"] Responses: - `200` — The verification result. - `country` (Country) — The country a value resolved to. - `calling_code` (string) — International calling code, digits only. [example "971"] - `code` (string, required) — ISO 3166-1 alpha-2 code. [example "AE"] - `name` (string, required) — English country name. [example "United Arab Emirates"] - `details` (TaxIDDetails) — Tax registration half of a Result. - `checksum` (boolean, required) — Whether the scheme carries a verifiable check digit of its own, which the response reports for transparency. - `scheme` (string, required) — Name of the tax scheme. [example "UAE TRN"] - `formatted` (Formatted) — Display forms of a valid value. - `international` (string) — Internationally formatted value, phone only. [example "+971 50 123 4567"] - `national` (string) — Nationally formatted value. [example "050 123 4567"] - `input` (string, required) — The value exactly as it was submitted. - `normalized` (string) — The value stripped of formatting. E.164 for phone numbers. - `reason` (Reason) — Why a value was rejected. Codes are add-only and never renamed. [one of "empty_input", "invalid_characters", "too_short", "too_long", "wrong_country", "invalid_prefix", "unallocated_prefix", "unknown_area_code", "invalid_year", "checksum_failed", "unsupported_country", "unsupported_type"] - `type` (DocumentType, required) — The kind of value being validated. [one of "phone", "national_id", "tax_id", "iban"] - `valid` (boolean, required) — Whether the value passed every check for its country and type. - `demo` (boolean, required) — Always true. - `quota` (PlaygroundQuota) — An anonymous visitor's free playground allowance. - `limit` (integer (int64), required) — Free checks a visitor gets before being asked to sign up. - `remaining` (integer (int64), required) — Free checks left. At zero, the visitor needs an account. - `used` (integer (int64), required) — Free checks already spent. - `400` — The request was rejected before any work was done. - `429` — Either the caller exceeded its per-second ceiling (code rate_limited) or an anonymous visitor has used every free check (code free_checks_exhausted) and needs an account to continue.