The WhatsApp Business check answers two questions about a phone number: does it have a WhatsApp account, and is that account a WhatsApp Business account? You get registered (true, false or unknown) plus a business flag, with the time we checked. It works worldwide, in real time and in bulk jobs, and never returns profile details.
What does the WhatsApp Business check tell you?
It reports whether a WhatsApp account is associated with the number and, if so, whether it was set up as a business account. WhatsApp launched the separate WhatsApp Business app for small businesses in January 2018 (WhatsApp blog). Larger companies reach customers through the WhatsApp business platform instead. Either way, a number carries one WhatsApp account at a time, so a business number is not also a personal account.
registered: trueandattributes.business: true: a WhatsApp Business account exists.registered: trueandbusiness: false: a regular WhatsApp account exists.registered: trueandbusiness: null: an account exists, but the business flag could not be determined this time.registered: false: conclusive answer, no account.registered: null: unknown, not charged.
The flag describes how the account is configured. It says nothing about who owns the number or whether the business is genuine.
Who uses it, and why?
The business flag helps where the difference between a consumer and a company matters.
- B2B lead verification. A sales lead that gives a company mobile number with a business account on WhatsApp is consistent with its claim. A lead claiming to be a company with no account at all may deserve a closer look. See lead verification.
- Marketplace and merchant onboarding. Platforms that onboard sellers, drivers or service providers can record whether the contact number is run as a business account, as one input to their review.
- Impersonation and fraud review. Scammers often pose as a company's support line. Knowing whether a number that contacted your customers is a business account helps a fraud team decide how to triage reports, together with a spam reputation check where it is available.
- Channel planning for consented messages. Messages from your own business account to a customer's business account behave like any other WhatsApp chat, but many businesses route business contacts to e-mail or account managers instead.
What do you get back?
One result per number in checks["whatsapp.business"]. The older top-level whatsapp object mirrors it and adds a business field, so v1 integrations keep working.
| Field | Type | Meaning |
|---|---|---|
registered | boolean or null | true account exists, false no account, null unknown |
attributes.business | boolean | true business account, false regular account; absent when not determined |
status | enum | completed, pending, unknown, unsupported_country or failed |
confidence / confidence_score | enum / 0–1 | How sure the registration answer is |
checked_at | timestamp | When the answer was obtained |
cached / age_seconds / billed | boolean / integer / boolean | Cache and billing details for this check |
reason | string or null | Why an answer is not conclusive |
whatsapp.business | boolean or null | Same flag in the v1 shape; null when not determined |
If you request whatsapp and whatsapp.business together, the two collapse into whatsapp.business, because it answers both.
How is it billed?
You pay per number, for conclusive answers only. You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). A result with a conclusive registered value counts as conclusive even when business is null. The business check has its own real-time and bulk prices, which differ from the plain registration check, so see pricing for current rates.
Repeat checks of the same number within the freshness window can be answered from your account's cache for free (cached: true, billed: false). Send max_age: 0 to force a fresh, billed check, and max_cost to cap what a request may cost.
What are the limits?
The check runs in real time (POST /v1/lookup, up to 100 numbers per request) and in bulk jobs (POST /v1/jobs, up to 50,000 numbers and e-mails per job) for numbers in every country.
- The business flag is filled more reliably in bulk jobs. Real-time answers may return
business: nullnext to a conclusiveregisteredvalue. If the flag is essential, run the list as a job. - Up to 20 checks per request; numbers × checks may not exceed 2,000 per lookup or 100,000 per job.
- Requests that look like sequential number ranges or generated e-mail lists are rejected (
suspected_enumeration). - A daily cap applies per account;
GET /v1/limitsshows what is left today.
How do I use it responsibly?
Check numbers you have a lawful reason to process: leads who contacted you, sellers applying to your platform, numbers reported to your fraud team. Don't use the flag to compile lists of businesses to message cold. Our acceptable use policy forbids unsolicited bulk messaging and range scanning, and WhatsApp's own business messaging rules require opt-in before a business messages someone. People whose numbers were checked can object via the opt-out form.
Example request
+447700900006 is the test number for a registered business account. The other test numbers behave as documented in test mode; for them business is false whenever the answer is conclusive.
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900006"],"checks":["whatsapp.business"]}'// 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: ["+447700900006"],
checks: ["whatsapp.business"],
});
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: ["+447700900006"],
checks: ["whatsapp.business"],
}),
});
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=["+447700900006"], checks=["whatsapp.business"])
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' => ['+447700900006'],
'checks' => ['whatsapp.business'],
]),
]);
$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":["+447700900006"],"checks":["whatsapp.business"]}`)
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" => ["+447700900006"],
"checks" => ["whatsapp.business"]
})
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.
Response (excerpt, test mode: the first item of results):
{
"kind": "phone",
"input": "+447700900006",
"e164": "+447700900006",
"country": "GB",
"number_status": "valid",
"checks": {
"whatsapp.business": {
"service": "whatsapp.business",
"status": "completed",
"registered": true,
"attributes": {
"business": true
},
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:29.519Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null
}
},
"whatsapp": {
"service": "whatsapp.business",
"status": "completed",
"registered": true,
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:29.519Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null,
"business": true
},
"test": true
}Try it now
Realtime lookup via POST /v1/lookup. Every example runs as pasted: it uses the public sandbox key, which answers the test values for free. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900001"],"checks":["whatsapp.business"]}'// 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.business"],
});
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.business"],
}),
});
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.business"])
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.business'],
]),
]);
$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.business"]}`)
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.business"]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.body// .mcp.json (Claude Code), ~/.cursor/mcp.json (Cursor) or .vscode/mcp.json ("servers" instead of "mcpServers")
{
"mcpServers": {
"mobilevalidate": {
"type": "http",
"url": "https://mcp.mobilevalidate.com/mcp",
"headers": { "Authorization": "Bearer ${MOBILEVALIDATE_API_KEY}" }
}
}
}
// Then ask your agent, e.g.:
// "Check +447700900001 with whatsapp.business" → tool: lookup_numbersThe hosted MCP server is live for customers with an agent key (mv_agent_…) or a test key. Or run it locally with npx -y @mobilevalidate/mcp.
Real test-mode result for this check
{
"service": "whatsapp.business",
"status": "completed",
"registered": true,
"attributes": {
"business": false
},
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:28.979Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null
}What you get
Fields of checks["whatsapp.business"], generated from the public service catalog.
| Field | Type | Meaning |
|---|---|---|
| registered | boolean | null | true = found, false = not found, null = unknown (not charged). |
| status | enum | completed, pending, unknown, unsupported_country or failed. |
| checked_at | timestamp | When the answer was obtained. |
| attributes.business | boolean | Account is a WhatsApp Business account. |
Frequently asked questions
What is the difference between a WhatsApp account and a WhatsApp Business account?
Both are tied to one phone number. A WhatsApp Business account is created with the separate WhatsApp Business app or through the WhatsApp business platform, and it can show a business profile. This check reports registration and, separately, whether the account is a business account.
Why is business null when registered is true?
The account check was conclusive but the business flag could not be determined for that answer. This happens more often on real-time lookups. The answer is still a conclusive registration result.
Do I need to request whatsapp and whatsapp.business together?
No. The business check already answers whether an account exists. If you request both, the API runs only whatsapp.business and you pay for one check per number.
Does a business account prove that a number belongs to a real company?
No. Anyone can install the WhatsApp Business app, so the flag says how the account was set up, not who owns it. Treat it as one signal next to your own verification steps.
Is the business name or profile returned?
No. The result contains only registered and the business flag. Names, photos, descriptions and other profile details are never returned.

