API reference

Interactive reference for every MobileValidate API endpoint, generated from the OpenAPI document. Send test requests with the public sandbox key.

Base URL https://api.mobilevalidate.com/v1OpenAPI 1.0.0Download openapi.yamlQuickstart
Postman collectionBruno collection (.zip).http fileImport into Postman, Bruno, VS Code or JetBrains; the sandbox key is already set.

Test requests are live. They go straight to the API with the public sandbox key filled in. It answers the documented test numbers and addresses only and is never billed. Swap in your own key under Authentication to try anything else.

Loading the interactive reference…

All operations

Lookups

Real-time checks for up to 100 numbers and e-mail addresses per request.

POST/v1/lookup

Check up to 100 numbers and e-mail addresses

Runs every requested check (see GET /v1/services) for every identifier of the check's input type: phone checks for numbers, e-mail checks for emails. Result rows come back in order: numbers first, then e-mails. The call waits up to wait seconds (default 10) for all answers; if some are still running it answers 202 with status: pending and a next.poll_url (or use a webhook). Checks without real-time support are refused with 403 service_disabled (param checks[i]) — send them in a bulk job. E-mail checks answer only whether an account or mailbox exists — never names, photos or profiles.

ParameterInDescription
Idempotency-KeyheaderAny unique string (1–255 printable ASCII) for safe retries: a retry with the same key and body within 24 hours returns the stored response (header Idempotent-Replayed: true) instead of running the request again.
Request (sandbox key)
curl https://api.mobilevalidate.com/v1/lookup \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
  -H "Content-Type: application/json" \
  -d '{"numbers":["+447700900001"],"checks":["whatsapp","carrier"]}'

Responses: 200 All checks completed. · 202 Some checks are still running; poll `next.poll_url` (the Location header) or wait for lookup.completed. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

GET/v1/lookups/{id}

Get a lookup

Returns a lookup created by POST /v1/lookup, optionally waiting up to wait seconds for pending checks. input is masked on later reads; e164 and email are returned. Lookups expire after the account's retention period (404 not_found afterwards).

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).
waitquerySeconds (0–30) to wait for pending checks before answering.

Responses: 200 The lookup (completed or still pending). · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

Bulk jobs

Bulk checks for up to 50,000 numbers and e-mail addresses per job, with paginated results and downloads.

POST/v1/jobs/estimate

Estimate a bulk job for free

Validates and counts a list without storing it, calling a data source or charging: valid, invalid and duplicate rows, cached answers, and the maximum possible cost (max_cost). Same body as POST /v1/jobs.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/jobs/estimate \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
  -H "Content-Type: application/json" \
  -d '{"numbers":["+447700900001","+447700900002","12"],"checks":["whatsapp"]}'

Responses: 200 The estimate. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

POST/v1/jobs

Create a bulk job

Checks up to 50,000 numbers and e-mail addresses (JSON body, or a CSV upload as multipart/form-data). The maximum possible cost is reserved up front; cached, inconclusive, invalid and duplicate rows are released when the job finishes. Poll GET /v1/jobs/{id} (or subscribe to job.completed), then page through GET /v1/jobs/{id}/results or download the whole file. Test-key jobs complete immediately.

ParameterInDescription
Idempotency-KeyheaderAny unique string (1–255 printable ASCII) for safe retries: a retry with the same key and body within 24 hours returns the stored response (header Idempotent-Replayed: true) instead of running the request again.
Request (sandbox key)
curl https://api.mobilevalidate.com/v1/jobs \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
  -H "Content-Type: application/json" \
  -d '{"numbers":["+447700900001","+447700900002"],"emails":["[email protected]"],"checks":["whatsapp","email"]}'

Responses: 201 Job created. `estimate` shows how the list was counted. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

GET/v1/jobs/{id}

Get a job

Status, progress and cost of a job. wait (0–30 s) holds the request until the job reaches a final status (completed, failed or cancelled) or the time runs out. Keys with jobs:read see their own jobs; jobs:read_all sees every job of the account.

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).
waitquerySeconds (0–30) to wait for pending checks before answering.

Responses: 200 The job. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

DELETE/v1/jobs/{id}

Cancel a running job or delete a finished job's results

Running job → cancelled: rows not yet sent to a data source are released and never charged; rows already sent finish and are billed as usual. Finished job → its per-row results are deleted now instead of at the end of the retention period (purged_at is set).

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).

Responses: 200 The job after cancelling or deleting results. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

GET/v1/jobs/{id}/results

List a job's results

One item per row (number or e-mail address) in input order, with every check of the row. Filter by registered / status of one service; page with limit and after = the previous page's next_cursor.

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).
registeredqueryOnly rows whose answer for `service` is true / false / null.
statusqueryOnly rows whose `service` check has this status.
servicequeryService (code or alias) the registered/status filters apply to; default = the job's first check. With a filter, only rows of that service's input type are returned.
limitqueryRows (numbers and e-mails) per page. A row's checks are never split across pages.
afterqueryCursor from the previous page (`next_cursor`).

Responses: 200 A page of result items. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

GET/v1/jobs/{id}/download

Download a job's results as CSV or NDJSON

Streams the whole result file, one line per row (numbers, then e-mails) in input order. CSV columns: row_no, input_masked, e164, country, number_status, service, status, registered, business, checked_at, cached, billed (for the row's primary check = first requested check of its kind), then kind, email, email_status, then per service <service>.status, <service>.registered, <service>.<attribute>, <service>.billed (empty for rows of the other kind). NDJSON lines have the same shape as result items.

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).
formatrequiredqueryFile format.

Responses: 200 The result file (Content-Disposition attachment). · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

Account

Balance, limits and usage of the key's account.

GET/v1/account

Get the account balance

Balance, reserved amount and today's usage (UTC) of the key's account. Needs account:read.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/account \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"

Responses: 200 The account. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

GET/v1/limits

Get limits and remaining allowances

Request rate, request sizes, the daily identifiers cap and the key's daily spend cap. Daily counters reset at 00:00 UTC.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/limits \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"

Responses: 200 Current limits. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

GET/v1/usage

Get usage by day or by check

Checks, conclusive answers, cached answers, billed units and cost for an inclusive UTC date range (at most 366 days), for the key's mode (live or test). Rows past the retention period are no longer counted.

ParameterInDescription
fromrequiredqueryFirst day (UTC).
torequiredqueryLast day (UTC), inclusive.
group_byqueryOne row per day or per check.
Request (sandbox key)
curl https://api.mobilevalidate.com/v1/usage \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"

Responses: 200 Usage rows. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

Webhooks

Signed notifications (Standard Webhooks) when lookups and jobs finish. See the webhooks section for the event payloads and https://mobilevalidate.com/docs/webhooks for signature verification.

GET/v1/webhook_endpoints

List webhook endpoints

Every endpoint of the account that is not deleted and has the same mode as the key (test keys list test endpoints, live keys live endpoints). Needs webhooks:manage.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/webhook_endpoints \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"

Responses: 200 Endpoints. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

POST/v1/webhook_endpoints

Create a webhook endpoint

Registers a public HTTPS URL (at most 20 per account). The response contains the signing secret — shown only once. The endpoint stays pending_verification until it answers our signed webhook.verification event with a 2xx status. Not available with the public sandbox key.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/webhook_endpoints \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/mobilevalidate","events":["lookup.completed","job.completed"]}'

Responses: 201 Endpoint created; `secret` is shown only this once. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

PATCH/v1/webhook_endpoints/{id}

Update a webhook endpoint

Changes the URL and/or the subscribed events. A new URL must be verified again (pending_verification).

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).

Responses: 200 The updated endpoint. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

DELETE/v1/webhook_endpoints/{id}

Delete a webhook endpoint

Nothing is delivered to a deleted endpoint.

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).

Responses: 204 Deleted. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

POST/v1/webhook_endpoints/{id}/test

Send a test event

Queues a signed webhook.test event to the endpoint (delivered even before verification). Use it to check your signature code.

ParameterInDescription
idrequiredpathObject id (`lkp_…`, `job_…` or `we_…`).

Responses: 202 Test event queued. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

Service catalog

Service catalog (GET /v1/services is authoritative at run time).

Spam reputation (number.spam, alias spam). Whether a phone number appears in spam and nuisance-call reports: telecom-regulator actions, government nuisance-call complaint data and community spam-report sites, plus whether the number was recently offered as an unassigned number (a possible spoofed caller ID or fake lead). It is report-based: no_reports means we hold no reports for the number — it is not a statement that the number is safe. Countries: US, CA, DE; other numbers → unsupported_country (free). Attributes: risk_level (high | medium | low | no_reports), risk_score (integer 0–100, higher = more reports), booleans reason_regulator, reason_government, reason_community, reason_unassigned, hint voip_range (VoIP carrier range; adds no points), top_category (most frequent report category), first_seen / last_seen (YYYY-MM, when the number first/last appeared in our data), sources (integer, independent signal classes). Levels: high needs a regulator action or at least two independent signal classes. Billing: every risk_level including no_reports is a conclusive answer and is billed; unknown and unsupported_country are free. No report texts and no names are ever returned.

Live network status (number.hlr, alias hlr) — coming soon. Live home-network query: status (reachable | unreachable | invalid | unknown), ported, roaming (yes/no only, no location), network, mcc_mnc, country. Not listed by GET /v1/services and refused with 403 service_disabled until it is switched on.

GET/v1/services

List checks and prices

Without a key: the public catalog. With a key: every check the key's account may use, at the account's prices. Lists active phone and e-mail checks; switched-off checks (e.g. number.hlr, coming soon) are not listed.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/services \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"

Responses: 200 Service catalog with prices by mode. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

Keys

Instant personal test keys for developers (no account needed).

POST/v1/test-keys

Get a personal test key

Issues a personal test key (mv_test_…) for a work e-mail address, protected by a Cloudflare Turnstile token from the form at https://mobilevalidate.com/get-test-key. The key works in test mode with any number (never billed, never reaches a data source), including bulk jobs and test-mode webhooks. It is shown once and never returned again. Each address can receive at most 3 test keys in 30 days; after that the answer is 409 test_key_exists. At most 3 requests per network per day, whatever their result (IPv6 networks count per /64). The request also asks for live access, which we review by hand. No authentication.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/test-keys \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","name":"Ada","company":"Example Ltd","use_case":"Screen sign-up phone numbers","turnstile_token":"0.x…"}'

Responses: 201 The key (shown only once). · 409 No more test keys can be issued for this e-mail address right now (at most 3 in 30 days). Keys already issued are never shown again. · default Error. Branch on `error.code`; `suggestion` (optional) says how to fix the request.

System

Service health.

GET/v1/health

Check that the API is up

No authentication. 200 when the API can reach its database, 503 otherwise.

Request (sandbox key)
curl https://api.mobilevalidate.com/v1/health \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"

Responses: 200 Healthy. · 503 The API cannot reach its database; retry after Retry-After.

Webhook events

  • lookup.completed — A pending lookup finished
  • job.completed — A bulk job finished
  • job.failed — A bulk job failed (subscribable; delivery rolling out)
  • job.progress — Progress of a running job (opt-in; delivery rolling out)
  • balance.low — The balance fell below the low-balance threshold (delivery rolling out)
  • limits.cap_reached — A daily identifiers cap or spend cap was reached (delivery rolling out)
  • webhook.verification — Ownership challenge for a new or changed endpoint
  • webhook.test — Test event from POST /v1/webhook_endpoints/{id}/test

Signatures, retries and verification: webhooks guide.