Quickstart
Make your first MobileValidate API call in 30 seconds with the public sandbox key, no signup: check a number on WhatsApp and Telegram and read the result.
Last updated
View as MarkdownThis guide takes you from nothing to a checked phone number in about 30 seconds, without signing up. You'll send one real-time lookup with the public sandbox key, read the answer, and then add more checks to the same request. You need curl, or one of the languages in the tabs below.
Step 1: Which key do I use?
Start with the public sandbox key. It is printed here on purpose: anyone can use it, it answers the documented test values only, and it is never billed.
export MOBILEVALIDATE_API_KEY="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"MOBILEVALIDATE_API_KEY is the one variable name used everywhere: the SDKs, the CLI and the MCP server read it when you don't pass a key. Every request sends the key as a bearer token: Authorization: Bearer $MOBILEVALIDATE_API_KEY.
The sandbox key is limited per IP address (30 requests per minute, 1,000 per day) and refuses any other number or address with 403 sandbox_magic_only. When you want to test with your own data, get a personal test key (mv_test_…). Live keys (mv_live_…) come with an approved access request. Your own keys are shown only once, when they are created, so keep them in a secret manager and never commit them. See authentication.
Step 2: How do I check a number?
Send the number in E.164 format (+ followed by the country code and number) to POST /v1/lookup and name the checks you want. +447700900001 is a test number that always answers "registered".
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900001"],"checks":["whatsapp"]}'// npm install mobilevalidate · Node.js 20+ · save as check.mjs and run: node check.mjs
import { MobileValidate } from "mobilevalidate";
// Omit apiKey to read MOBILEVALIDATE_API_KEY from the environment.
const mv = new MobileValidate({ apiKey: "mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" });
const { data, error } = await mv.lookup({
numbers: ["+447700900001"],
checks: ["whatsapp"],
});
if (error) console.error(error.code, error.message);
else console.dir(data.results, { depth: null });// Node.js 18+, Deno, Bun or the browser console (ES module: save as .mjs or use "type": "module").
const res = await fetch("https://api.mobilevalidate.com/v1/lookup", {
method: "POST",
headers: {
Authorization: "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym",
"Content-Type": "application/json",
},
body: JSON.stringify({
numbers: ["+447700900001"],
checks: ["whatsapp"],
}),
});
console.log(res.status, res.headers.get("x-request-id"));
console.dir(await res.json(), { depth: null });# pip install mobilevalidate-sdk
from mobilevalidate import MobileValidate
# Omit api_key to read MOBILEVALIDATE_API_KEY from the environment.
mv = MobileValidate(api_key="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
result = mv.lookup(numbers=["+447700900001"], checks=["whatsapp"])
print(result)<?php
$ch = curl_init('https://api.mobilevalidate.com/v1/lookup');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'numbers' => ['+447700900001'],
'checks' => ['whatsapp'],
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL, $response, PHP_EOL;package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"numbers":["+447700900001"],"checks":["whatsapp"]}`)
req, err := http.NewRequest("POST", "https://api.mobilevalidate.com/v1/lookup", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}require "net/http"
require "json"
uri = URI("https://api.mobilevalidate.com/v1/lookup")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
"numbers" => ["+447700900001"],
"checks" => ["whatsapp"]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyRuns as pasted with the public sandbox key, which answers the test values only. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
If you leave out checks, the API defaults to ["whatsapp"]. Numbers in national format (07700 900001) work too if you add "default_country": "GB".
Step 3: How do I read the answer?
The response has one item per number in results, in input order. Each item has a checks map with one result per service:
"checks": {
"whatsapp.registered": {
"service": "whatsapp.registered",
"status": "completed",
"registered": true,
"attributes": null,
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:29.489Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null
}
}Branch on registered: true means an account exists, false means it doesn't, and null means unknown (read status and reason). summary gives the counts, and billing shows what the request cost. It is always zero in test mode.
Step 4: How do I run several checks at once?
List more codes or aliases in checks. Each number gets one result per service:
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900001","+447700900002","+447700900003"],"checks":["whatsapp","telegram","carrier"]}'// npm install mobilevalidate · Node.js 20+ · save as check.mjs and run: node check.mjs
import { MobileValidate } from "mobilevalidate";
// Omit apiKey to read MOBILEVALIDATE_API_KEY from the environment.
const mv = new MobileValidate({ apiKey: "mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" });
const { data, error } = await mv.lookup({
numbers: ["+447700900001", "+447700900002", "+447700900003"],
checks: ["whatsapp", "telegram", "carrier"],
});
if (error) console.error(error.code, error.message);
else console.dir(data.results, { depth: null });// Node.js 18+, Deno, Bun or the browser console (ES module: save as .mjs or use "type": "module").
const res = await fetch("https://api.mobilevalidate.com/v1/lookup", {
method: "POST",
headers: {
Authorization: "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym",
"Content-Type": "application/json",
},
body: JSON.stringify({
numbers: ["+447700900001", "+447700900002", "+447700900003"],
checks: ["whatsapp", "telegram", "carrier"],
}),
});
console.log(res.status, res.headers.get("x-request-id"));
console.dir(await res.json(), { depth: null });# pip install mobilevalidate-sdk
from mobilevalidate import MobileValidate
# Omit api_key to read MOBILEVALIDATE_API_KEY from the environment.
mv = MobileValidate(api_key="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
result = mv.lookup(
numbers=["+447700900001", "+447700900002", "+447700900003"],
checks=["whatsapp", "telegram", "carrier"],
)
print(result)<?php
$ch = curl_init('https://api.mobilevalidate.com/v1/lookup');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'numbers' => ['+447700900001', '+447700900002', '+447700900003'],
'checks' => ['whatsapp', 'telegram', 'carrier'],
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL, $response, PHP_EOL;package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"numbers":["+447700900001","+447700900002","+447700900003"],"checks":["whatsapp","telegram","carrier"]}`)
req, err := http.NewRequest("POST", "https://api.mobilevalidate.com/v1/lookup", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}require "net/http"
require "json"
uri = URI("https://api.mobilevalidate.com/v1/lookup")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
"numbers" => ["+447700900001", "+447700900002", "+447700900003"],
"checks" => ["whatsapp", "telegram", "carrier"]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyRuns as pasted with the public sandbox key, which answers the test values only. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
In test mode, …001 is registered, …002 is not registered, and …003 is unknown (reason: "UPSTREAM_TIMEOUT") for every yes/no service. The carrier lookup returns data for …001 and is unknown for …002 (NO_DATA) and …003. That way you can exercise every branch of your code. summary.by_service gives counts per service.
What next?
- Try the other test numbers and addresses: pending, unsupported country, rate limited, no balance.
- Browse every endpoint in the interactive API reference and send test requests from the browser.
- Prefer an HTTP client? Import the Postman collection, the Bruno collection or the .http file. The sandbox key is already set.
- Some services are offered in bulk only. Run those through a bulk job.
- Check e-mail addresses with the
emailsfield. See e-mail checks. - Look up codes, aliases and outputs in the services reference, or call
GET /v1/servicesto get the list your key can use, with prices.
Frequently asked questions
Does the quickstart cost anything?
No. It uses the public sandbox key, a test key that anyone can use. Test keys answer from fixed test data, never reach a real network and are never billed.
Why does the sandbox key refuse my own number?
The public sandbox key answers only the documented test numbers and e-mail addresses (403 sandbox_magic_only otherwise). Get a personal test key to try any input in test mode, and a live key for real checks.
What do I change to go live?
Swap the test key for your live key (mv_live_…) and use real numbers. The request and response shapes stay the same, but test numbers are rejected with test_number_only.

