The Viber check answers whether a phone number has a Viber account. It returns registered: true, false or null (unknown) with the time of the check. It runs in real time or in bulk jobs, for numbers from any country. Nothing is sent to the number, and no name, photo or online status is returned.
What does the Viber check tell you?
It tells you whether a Viber account is associated with the number. Viber identifies users by their phone number: you install the app on a phone, confirm the number, and your contacts see you under that number. There is no username layer on top, so the number is the account.
registered: true: a Viber account exists for the number.registered: false: a conclusive answer that there is no account.registered: null: no conclusive answer;statusandreasonexplain why. It is not charged.
Viber launched in 2010 and has been owned by the Japanese company Rakuten since 2014 (Rakuten press release, February 2014). Accounts can outlive the phone they were created on, and numbers get recycled by carriers, so read registered together with checked_at, and keep max_age short where freshness matters.
Who uses it, and why?
Viber's footprint is regional. In several countries of Eastern Europe, the Balkans, the Middle East and Southeast Asia it is one of the most-used messengers. Elsewhere it plays a smaller part.
- Cheaper delivery of expected messages. Viber offers paid business messaging with a branded sender, which many companies in Viber-heavy markets use for transactional messages and codes, falling back to SMS when the person has no account. Checking first keeps undeliverable Viber attempts, and the delay they cause, out of the flow. See SMS cost reduction.
- Channel order per market. Teams operating across countries check WhatsApp, Telegram and Viber in one request and choose the channel the customer opted into and actually has.
- Deliverability signal. A Viber account shows the number passed a phone-based activation at some point. Together with a carrier lookup, this helps separate live mobile numbers from typos and landlines.
What do you get back?
One result per number in checks["viber.registered"]; no extra attributes.
| Field | Type | Meaning |
|---|---|---|
registered | boolean or null | true account exists, false no account, null unknown |
status | enum | completed, pending, unknown, unsupported_country or failed |
confidence / confidence_score | enum / 0–1 | How sure the answer is |
checked_at | timestamp | When the answer was obtained |
cached / age_seconds | boolean / integer | Cache details |
billed | boolean | Whether the check was charged |
reason | string or null | Why the answer is not conclusive |
How is it billed?
Per number, only for conclusive answers. You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). Real-time and bulk checks have separate prices. See pricing.
Within the freshness window a repeat check can be answered from your account's cache, free of charge (cached: true, billed: false). Use max_age: 0 for a fresh, billed answer and max_cost to cap a request.
What are the limits?
The Viber check works in real time (POST /v1/lookup, up to 100 numbers, wait up to 30 seconds) and in bulk jobs (POST /v1/jobs, up to 50,000 numbers and e-mails). All countries are accepted.
- Up to 20 checks per request, and numbers × checks up to 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). - Each account has a daily number cap (
GET /v1/limits). - Numbers written without a country code need
default_countryto be read correctly.
How do I use it responsibly?
Use the check to decide how to reach customers who asked to hear from you, not to find new people to message. Viber's business messaging requires approved senders and opted-in recipients, and our acceptable use policy forbids unsolicited bulk messaging and scanning number ranges. Keep a lawful basis for every list you check. People whose numbers were checked can object via the opt-out form.
Example request
Test keys are free and never reach a real network. +447700900001 answers "registered". See test mode.
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900001"],"checks":["viber"]}'// 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: ["viber"],
});
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: ["viber"],
}),
});
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=["viber"])
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' => ['viber'],
]),
]);
$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":["viber"]}`)
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" => ["viber"]
})
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": "+447700900001",
"e164": "+447700900001",
"country": "GB",
"number_status": "valid",
"checks": {
"viber.registered": {
"service": "viber.registered",
"status": "completed",
"registered": true,
"attributes": null,
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:29.589Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null
}
},
"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":["viber.registered"]}'// 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: ["viber.registered"],
});
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: ["viber.registered"],
}),
});
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=["viber.registered"])
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' => ['viber.registered'],
]),
]);
$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":["viber.registered"]}`)
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" => ["viber.registered"]
})
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 viber.registered" → 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": "viber.registered",
"status": "completed",
"registered": true,
"attributes": null,
"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["viber.registered"], 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. |
Frequently asked questions
In which countries is the Viber check most useful?
The check works for numbers from any country. It is most useful where Viber is a leading messenger, which includes several countries in Eastern Europe, the Balkans, the Middle East and Southeast Asia.
Does a Viber account mean the number is a mobile number?
Usually. Viber is registered on a phone with the number activated by a code, so an account is a good sign the number was used on a smartphone. It is not proof of the current owner.
Can I use the result to send Viber business messages?
Only to people who agreed to receive them. Viber business messaging has its own rules and approval process; our check only tells you whether the number has an account.
What makes a Viber answer unknown?
Mostly a check that did not complete in time. Unknown answers have registered set to null and are never charged.
Is the Viber name or photo returned?
No. Only whether an account is associated with the number. Names, photos and online status are never returned.

