• Home
  • Blog
  • How to Check if a Phone Number Is on WhatsApp (5 Methods)
Guides
12 min read Jun 9, 2026

How to Check if a Phone Number Is on WhatsApp (5 Methods)

Five practical ways to verify whether a phone number is on WhatsApp — from the wa.me link to open-source libraries and a hosted REST API — with code, accuracy, and ban-risk notes.

Key takeaways
  • The wa.me click-to-chat link is the simplest manual check, but it's one-at-a-time and gets throttled.
  • Open-source libraries Baileys (onWhatsApp) and whatsapp-web.js (getNumberId) work programmatically but need an authenticated session and risk WhatsApp bans.
  • The official WhatsApp Business Cloud API has NO endpoint to check existence before messaging — the old On-Premises /contacts endpoint is gone.
  • A hosted Profile API returns existence, profile picture, name, and business flag as JSON with no session, browser, or personal-number ban exposure.
  • Phone numbers are personal data under GDPR — only check numbers you have a lawful basis for and honor opt-out.

Why verifying WhatsApp presence is harder than it looks

Checking whether a phone number is registered on WhatsApp sounds trivial — surely there's an endpoint for that? In practice, WhatsApp deliberately makes bulk existence checks difficult, because the same capability powers spam, scraping, and stalkerware. There is no single blessed, frictionless API from Meta for the simple question "is this number on WhatsApp?"

That leaves developers and growth teams choosing between a handful of approaches, each with a different trade-off between effort, accuracy, scale, and risk. A quick manual check on the web costs nothing but doesn't scale. Open-source libraries automate it but tie you to an authenticated WhatsApp session — and to the ban risk that comes with reverse-engineered clients. The official Cloud API is fully supported but, surprisingly, offers no pre-send verification at all. A hosted API abstracts the mess away but adds a dependency and a per-request cost.

Below we walk through all five methods with working code where it applies, then summarize accuracy, rate limits, and the legal angle so you can pick the right tool for your use case — whether that's validating a single lead or cleaning a list of 100,000 numbers. We also dedicate a section to the bulk workflow specifically, because that is where most teams get stuck.

Method 1 — Manual check with the wa.me link

Meta's official click-to-chat URL is the fastest zero-code way to test a single number. Open this in any browser, replacing the placeholder with the full international number:

BASH
# Canonical form: country code + number, digits only.
# No '+', no leading zeros, no spaces, parentheses, or hyphens.
https://wa.me/15551234567

If the number is registered, the page opens (or offers to open) a chat. If it isn't, WhatsApp shows a message like "The phone number isn't on WhatsApp." That's your answer. A second manual option: save the number to your phone's contacts, open WhatsApp, tap New Chat, and look for a profile — it only appears for registered numbers.

The catch: this is strictly one-at-a-time, there's no JSON to parse, and WhatsApp may temporarily rate-limit or refuse checks after several rapid attempts from the same IP or device. It's perfect for an occasional manual lookup and useless for automation or bulk validation.

Method 2 — Baileys onWhatsApp (Node.js, lightweight)

Baileys is a popular MIT-licensed library that speaks WhatsApp's multi-device WebSocket protocol directly — no browser required. The maintained package is baileys (also published as @whiskeysockets/baileys) from the WhiskeySockets organization. Avoid the old @adiwajshing/baileys package; it's abandoned.

Once you have an authenticated socket (via QR or pairing code), the onWhatsApp method tells you whether a number exists. It accepts either the bare digits-only number or a full JID — both resolve correctly, and the result always returns the canonical JID:

JS
import makeWASocket, { useMultiFileAuthState } from 'baileys'

const { state, saveCreds } = await useMultiFileAuthState('auth')
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)

// Both forms work — pass the bare number...
const [result] = await sock.onWhatsApp('15551234567')
// ...or the full JID: sock.onWhatsApp('[email protected]')
if (result?.exists) {
  console.log(`Registered. JID: ${result.jid}`)
} else {
  console.log('Not on WhatsApp')
}

Baileys is actively maintained, with the v7.x line current. v7 introduced major breaking changes around WhatsApp's new LID (Linked ID) identity format: onWhatsApp no longer returns LIDs, which are now fetched separately via getLIDForPN / getLIDsForPNs. If you're upgrading, read the official v7 migration guide before relying on the returned shape.

Method 3 — whatsapp-web.js getNumberId (browser-driven)

whatsapp-web.js takes a different approach: it drives the real WhatsApp Web client inside a headless Puppeteer/Chromium instance. It's Apache-2.0 licensed, originally by Pedro S. Lopez, and the repo now lives under the wwebjs organization (latest v1.34.7, April 2026). Because it runs a full browser, it's heavier on resources than Baileys, but some teams prefer it for fidelity to the official web app.

Two methods cover number checks: getNumberId(number) returns the serialized WhatsApp ID or null if the number isn't registered, and isRegisteredUser(id) returns a boolean. The @c.us suffix is appended automatically.

JS
const { Client, LocalAuth } = require('whatsapp-web.js')
const client = new Client({ authStrategy: new LocalAuth() })

client.on('ready', async () => {
  const numberId = await client.getNumberId('15551234567')
  if (numberId) {
    console.log('Registered:', numberId._serialized) // e.g. [email protected]
  } else {
    console.log('Not on WhatsApp')
  }
})

client.initialize()

Be aware of real-world reliability issues. getNumberId has returned null for numbers that ARE registered after WhatsApp changed its web internals (issue #1814), and validation can be slow or unstable — some users report 3–5 minute delays (issue #2633). Like all unofficial clients, it breaks whenever WhatsApp updates the web app, and the project's own README warns that being blocked is not guaranteed to be avoidable.

Method 4 — The official WhatsApp Business Cloud API (and why it can't do this)

Here's the honest, commonly-misreported fact: the official WhatsApp Business Cloud API has no supported way to check whether a number is on WhatsApp before you message it. Competitor posts that point you to a "contacts" endpoint are describing the old On-Premises API, whose /contacts endpoint (returning wa_id and a valid status) does not exist in the Cloud API.

The On-Premises Business API was officially sunset on October 23, 2025, leaving the Cloud API as the only supported architecture. With the Cloud API you simply send a message to the number after opt-in; a wa_id surfaces only as a side effect of an accepted message via the webhook or send response — not as a standalone lookup.

It's also far from a lightweight check. The Cloud API requires a verified Meta Business account, an approved WhatsApp Business Account (WABA), explicit opt-in, and template/messaging limits. And the model is shifting further away from phone-number identity: starting June 2026, WhatsApp usernames and Business-Scoped User IDs (BSUIDs) roll out. Users can hide their phone numbers, the BSUID becomes the per-business identifier, and numbers may be absent from webhooks unless there was recent interaction.

Method 5 — A hosted Profile / number-check API

If you want programmatic checks without running and babysitting a WhatsApp session, a hosted API does the heavy lifting server-side and returns clean JSON. The WhatsApp Profile API exposes existence checks plus public profile data through a single REST call — no QR code, no Puppeteer, no personal number on the line.

BASH
# Single number: existence + public picture + name + business flag
curl https://whatsapp-proxy.checkleaked.cc/number/15551234567

# Faster variant without the profile picture
curl https://whatsapp-proxy.checkleaked.cc/number/no_picture/15551234567

# Existence only
curl https://whatsapp-proxy.checkleaked.cc/number-simple/15551234567

All three single-number paths share the same shape — base URL, the route segment, then the digits-only number with no trailing slash — so they are easy to template without copy-paste mistakes. Responses include fields such as number, isWAContact (registered or not), isBusiness, and a profile picture URL. For lists, a POST /bulk_check endpoint validates many numbers in one request, and rate-limit headers plus caching are handled for you. The API is also distributed via RapidAPI (whatsapp-data1) and Apify if you prefer those channels, though calling the direct proxy avoids the marketplace markup.

BASH
curl -X POST https://whatsapp-proxy.checkleaked.cc/bulk_check \
  -H 'Content-Type: application/json' \
  -d '{"numbers": ["15551234567", "447700900123", "34600123456"]}'

Pricing as of June 2026: a free Basic tier of 50 requests/month for testing, then Pro at $35/mo for 10,000 requests, Ultra at $139/mo for 50,000, and Mega at $449/mo for 500,000, with per-request overage from $0.005 to $0.010 depending on tier. A no-commitment pay-as-you-go option runs about $40 per 50K requests. These figures can change — always confirm the current plans on the live pricing page before budgeting. The trade-off versus DIY: you offload session management and ban exposure, get unified JSON and built-in bulk support, but take on a per-request cost and an external dependency.

Cleaning a list of 100,000 numbers: the bulk workflow

Validating one number is easy; validating a list of tens or hundreds of thousands is where the real engineering lives. Doing it through your own WhatsApp session (Baileys or whatsapp-web.js) is the worst option at scale — sequential lookups are slow, and the volume itself is a textbook ban trigger. A hosted bulk endpoint or a controlled, throttled pipeline is the practical path. Here is what a sane bulk run looks like, regardless of which tool you use.

  1. Normalize first. Strip spaces, hyphens, parentheses and the leading +; convert national formats to E.164 digits-only; reject anything that isn't a plausible country code + subscriber number. Bad input is the #1 cause of false negatives.
  2. Deduplicate. Hash or sort the normalized list and drop duplicates before you spend any quota — large CRM exports are routinely 10–30% duplicates.
  3. Batch, don't blast. Send numbers in chunks (for example a few hundred per POST /bulk_check call) rather than one giant payload, so a single failure doesn't lose the whole run.
  4. Handle errors per number, not per batch. Treat the result as three buckets — registered, not registered, and unknown/error — and only retry the unknown bucket with backoff. Never retry a confirmed negative.
  5. Cache and re-use. Store results with a timestamp; existence rarely changes day to day, so a 7–30 day cache cuts cost dramatically and lets you re-run safely.

A minimal CSV-driven loop against the hosted bulk endpoint keeps throughput high while preserving per-number error handling:

JS
import fs from 'node:fs'

const all = fs.readFileSync('numbers.csv', 'utf8')
  .split(/\r?\n/).map(s => s.replace(/[^0-9]/g, '')).filter(Boolean)
const numbers = [...new Set(all)]            // dedup
const chunk = (a, n) => a.length ? [a.slice(0, n), ...chunk(a.slice(n), n)] : []

for (const batch of chunk(numbers, 200)) {   // batch
  const res = await fetch('https://whatsapp-proxy.checkleaked.cc/bulk_check', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ numbers: batch })
  })
  if (!res.ok) { /* backoff + retry this batch */ continue }
  const rows = await res.json()
  for (const r of rows) {
    // r.number, r.isWAContact, r.isBusiness  -> write to output CSV / DB
  }
}

Side-by-side comparison

MethodProgrammaticBulkNeeds sessionBan riskOutput
wa.me linkNoNoNoLow (throttled)Visual only
Baileys onWhatsAppYesRiskyYes (QR/pairing)High{ exists, jid }
whatsapp-web.js getNumberIdYesRiskyYes (browser)HighID or null
Cloud APIYes (messaging)No (cannot check)WABA setupLow (official)wa_id as side effect
Hosted Profile APIYesYes (bulk_check)NoNone on your numberJSON

Rule of thumb: for a one-off check, use wa.me. For a hobby bot tied to your own number where occasional bans are acceptable, Baileys or whatsapp-web.js are fine. For compliant opt-in messaging at scale, use the Cloud API (but accept it won't pre-verify). For reliable, scalable existence checks plus profile data without ban exposure, a hosted API is the pragmatic choice — including, as disclosed above, our own.

Frequently asked questions

Skip session management and ban risk

From the team behind this guide: check existence, fetch the public profile picture, display name, and business flag with one REST call — or upload a CSV for bulk validation. Free tier to get started, no QR code or headless browser to maintain.

Try the WhatsApp Profile API

Sources & further reading

Related

More from the blog

Written by
Eduardo Airaudo

Developer and founder of the WhatsApp Profile API. Building WhatsApp tooling and APIs since 2022.

What Our Users Say

Real reviews from our satisfied customers

4.5/5 (170 reviews)