piidetectionapi.com
Home
Solutions - Fundamentals
What Is PII Detection? NER vs Regex vs Rules Accuracy, Precision & Recall PII in Test Data
Solutions - Compliance
GDPR Personal Data HIPAA PHI Detection CCPA / CPRA PCI DSS Card Data
Solutions - AI & LLM Safety
LLM Guardrails Chatbot PII Filtering RAG Pipelines
Solutions - Data Discovery & DLP
Data Loss Prevention Log File Scanning Support Tickets Email Scanning Documents & PDFs Database Discovery ETL & Streaming Pipelines
Industries - Financial
Banking Fintech Insurance
Industries - Healthcare
Healthcare Pharma & Clinical Trials Telehealth
Industries - Public Sector & Legal
Government & FOIA Law Enforcement Law Firms & eDiscovery Education (FERPA)
Industries - Technology
SaaS Platforms Cybersecurity & IR Telecommunications Gaming & Platforms
Industries - Other
HR & Recruiting Retail & E-commerce Call Centers & BPO Real Estate Travel & Hospitality Marketing & AdTech
How-to Guides - Identity & Contact
Detect Names Detect Email Addresses Detect Phone Numbers Detect Physical Addresses Detect Dates of Birth
How-to Guides - IDs & Financial
Detect SSNs Detect Passport Numbers Detect Drivers Licenses Detect Credit Card Numbers Detect Bank Accounts & IBAN
How-to Guides - Technical & Health
Detect IP & Device IDs Detect Medical Records & PHI
Resources
Pricing API Docs Supported Entities Languages About Contact Sign In Try the Live Demo Get Started
How-To Guide

How to Detect Phone Numbers in Text

Learn how to automatically detect, classify, and locate phone numbers in any format — North American, international, E.164, extensions, and vanity numbers — using PII Detection API's context-aware AI. Find every number without drowning in false positives.

12 min read
Code examples included
Updated Aug 2026

Overview

Phone numbers are among the most frequently occurring pieces of personally identifiable information (PII) in real-world data. They show up in customer support tickets, CRM notes, call center transcripts, chat logs, contact forms, email signatures, invoices, and application logs. Unlike many identifiers, a phone number is directly actionable: anyone who obtains it can call, text, or use it as a lookup key to link records across data sets. That makes reliable phone number detection a foundational requirement for any privacy, compliance, or data loss prevention program.

The challenge is that phone numbers are not one format — they are hundreds. A number can be written as (555) 123-4567, 555.123.4567, +1 555 123 4567, 555-123-4567 ext. 22, or even 1-800-FLOWERS. International numbers introduce country codes, trunk prefixes, and national grouping conventions that vary from country to country. At the same time, plenty of digit strings that look like phone numbers are actually order IDs, invoice numbers, tracking codes, or account references. A naive regular expression either misses valid numbers or floods you with false positives — usually both.

PII Detection API solves this with context-aware AI. Instead of relying only on digit patterns, its transformer-based models read the surrounding text to determine whether a candidate string is genuinely a phone number. The API returns each detected PHONE_NUMBER entity with its exact character offsets and a confidence score, and can optionally return a masked version of the input in the same call. It is one of the 150+ entity types the API detects across 60+ languages.

Input Text
Customer Rachel Torres called from +44 20 7946 0958 about order #4415-2210. Callback requested at (312) 555-0184 ext. 12.
Detection Result (masked output)
Customer [NAME] called from [PHONE] about order #4415-2210. Callback requested at [PHONE].
200+ Country Formats
NANP, E.164, and national dialing conventions worldwide
Context-Aware
Distinguishes phone numbers from order IDs and account numbers
Precise Offsets
Start/end character positions and confidence for every match

Why Detect Phone Numbers

Phone numbers sit squarely inside the definition of personal data in every major privacy framework, and in several of them they are called out explicitly. Knowing where phone numbers live in your systems — and being able to prove it — is a prerequisite for compliance, breach response, and safe data sharing.

Regulatory Drivers

  • GDPR (EU): A phone number is personal data under Article 4 because it relates to an identifiable natural person. Subject access requests, right-to-erasure requests, and data minimization obligations all require you to locate phone numbers across free-text fields, not just structured columns.
  • HIPAA (US healthcare): Telephone numbers and fax numbers are two of the eighteen identifiers that must be removed under the Safe Harbor de-identification method. A single overlooked callback number in a clinical note keeps the whole record classified as protected health information.
  • CCPA/CPRA (California): Phone numbers are enumerated as personal information, which means they are in scope for consumer access, deletion, and opt-out rights.
  • TCPA (US telemarketing): While the TCPA governs how numbers may be called rather than how they are stored, it raises the stakes of phone data handling — consent records, do-not-call scrubbing, and dialing lists all depend on accurate identification of numbers in your data.

Operational Use Cases

  • Support tickets and chat logs: Customers paste their numbers into free-text fields constantly. Detecting them lets you mask tickets before analytics, model training, or offshore routing. See our guide to PII detection in support tickets.
  • Call center transcripts: Speech-to-text output contains spoken numbers in messy, sometimes worded-out forms ("five five five, one two three four"). Contact centers and BPOs use detection to sanitize transcripts before storage.
  • Application logs: Phone numbers leak into logs through request payloads and error dumps, quietly converting your logging pipeline into a PII store.
  • LLM inputs and outputs: Guardrails for chatbots and AI assistants strip phone numbers from prompts before they reach a third-party model, and from completions before they reach users.
  • Data loss prevention: Outbound email and file scanning flags documents that contain customer contact numbers before they leave your perimeter.

Tip: A phone number is a powerful linkage key. Even in an otherwise anonymized data set, one remaining mobile number can re-identify a person by joining against marketing databases or breach dumps. Treat phone detection as a baseline control, not an optional extra.

The Format Explosion Problem

The reason phone number detection is genuinely hard is the sheer diversity of legitimate formats. The International Telecommunication Union's E.164 standard defines a number as up to fifteen digits including a country code — but almost nobody writes numbers in pure E.164. People write numbers the way their country, keyboard habits, and mood dictate. A detection system has to recognize all of the following as the same kind of entity:

North American Numbering Plan (NANP)

The US, Canada, and much of the Caribbean share a ten-digit plan, but the same number appears in many typographic disguises: (555) 123-4567, 555-123-4567, 555.123.4567, 555 123 4567, 5551234567, and with the country code as +1 (555) 123-4567 or 1-555-123-4567. Parentheses, dots, hyphens, and spaces are all in everyday use, and OCR or transcription noise adds further variants.

International and National Conventions

Outside the NANP, each country has its own grouping habits. A London number may be written +44 20 7946 0958 internationally but 020 7946 0958 domestically — note the leading trunk zero that disappears when the country code is used. French numbers are conventionally written in five pairs: 01 23 45 67 89. German numbers have variable-length area codes, so 030 901820 (Berlin) and 089 12345678 (Munich) have different shapes. Japanese numbers appear as +81-3-1234-5678 or 03-1234-5678. UK mobiles start with 07; in many countries the mobile prefix itself signals number type. A detector trained only on US patterns will miss most of the world's phone numbers.

Extensions, Vanity Numbers, and Short Codes

Business numbers frequently carry extensions written as x1234, ext. 1234, extension 22, or #1234. The extension is part of the PII — it identifies a specific desk or person — so detection should capture it together with the base number. Vanity numbers such as 1-800-FLOWERS or 1-866-CALL-NOW replace digits with letters using the keypad mapping (2=ABC, 3=DEF, and so on); they contain few literal digits yet are fully dialable numbers. Short codes (five- or six-digit SMS numbers like 72345) are shared infrastructure rather than personal identifiers, and a good detector treats them differently from subscriber numbers.

Spoken and Obfuscated Numbers

Transcripts and user-generated content add another layer: numbers written out in words ("call me at five five five, one two three, four five six seven"), partially worded hybrids ("555 one two three four"), and deliberately obfuscated forms ("five5five-123-4567") used to evade platform filters. Because PII Detection API reads meaning rather than matching characters, it recognizes these forms that no practical regex can cover.

Format CategoryExampleDetection Notes
NANP standard(555) 123-4567All delimiter variants (dots, dashes, spaces, none) detected
E.164 international+442079460958Country code parsed; up to 15 digits per ITU E.164
UK domestic020 7946 0958Trunk-prefix form recognized alongside +44 form
French paired01 23 45 67 89Five-pair national convention supported
German variable area code030 901820Variable-length area codes handled by context, not fixed masks
Japanese+81-3-1234-5678Hyphenated and domestic 0-prefixed forms both detected
With extension555-123-4567 ext. 22Extension captured within the same entity span
Vanity1-800-FLOWERSKeypad letter mapping recognized as a dialable number
Spoken / transcribedfive five five one two three fourWorded-out digits detected in transcripts
Obfuscatedfive5five.123.4567Mixed word/digit evasion forms flagged by the AI model

Note: Format coverage interacts with language coverage. A German customer email will contain German number conventions and German context words ("Rufnummer", "erreichbar unter"). PII Detection API pairs both — see the full list of supported languages.

Quick Start

Detecting phone numbers takes a single request. Send your text to the API endpoint with the PHONE_NUMBER entity selected, and you get back every detected number with its position, the matched text, a confidence score, and an optional masked version of the input.

curl -X POST https://piidetectionapi.com/api/moderate.php \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "YOUR_API_KEY",
    "api_type": "pii_detection",
    "text": "Call me at (312) 555-0184 or on my UK line +44 20 7946 0958.",
    "entities": ["PHONE_NUMBER"],
    "mask_mode": "replace"
  }'
import requests

resp = requests.post(
    "https://piidetectionapi.com/api/moderate.php",
    json={
        "api_key": "YOUR_API_KEY",
        "api_type": "pii_detection",
        "text": "Call me at (312) 555-0184 or on my UK line +44 20 7946 0958.",
        "entities": ["PHONE_NUMBER"],
        "mask_mode": "replace",
    },
    timeout=30,
)
data = resp.json()
for e in data["detected_entities"]:
    print(e["type"], e["text"], e["start"], e["end"], e["confidence"])
const resp = await fetch("https://piidetectionapi.com/api/moderate.php", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    api_key: "YOUR_API_KEY",
    api_type: "pii_detection",
    text: "Call me at (312) 555-0184 or on my UK line +44 20 7946 0958.",
    entities: ["PHONE_NUMBER"],
    mask_mode: "replace"
  })
});
const data = await resp.json();
data.detected_entities.forEach(e =>
  console.log(e.type, e.text, e.start, e.end, e.confidence));

The response contains the structured entity list plus the masked text:

{
  "detected_entities": [
    {"type": "PHONE_NUMBER", "text": "(312) 555-0184", "start": 11, "end": 25, "confidence": 0.99},
    {"type": "PHONE_NUMBER", "text": "+44 20 7946 0958", "start": 43, "end": 59, "confidence": 0.98}
  ],
  "anonymized_text": "Call me at [PHONE] or on my UK line [PHONE].",
  "entities_detected": 2,
  "processing_time_ms": 142,
  "mask_mode_used": "replace",
  "status": 200
}

Three fields do most of the work downstream. The start and end offsets let you highlight, mask, or extract numbers yourself with exact precision. The confidence score lets you route borderline matches to review instead of making a binary keep-or-drop decision. And anonymized_text gives you a ready-to-store masked version when detection and masking should happen in one step. You can try all of this without writing code in the interactive demo.

Avoiding False Positives on Digit Strings

The hardest part of phone detection is not finding digits — it is deciding which digit strings are not phone numbers. Business text is saturated with numeric identifiers that overlap in length and shape with phone numbers: order IDs, invoice numbers, tracking codes, customer account numbers, ticket references, timestamps, and IP addresses. A pattern-only approach cannot tell them apart, which is why regex-based DLP tools are notorious for flagging every ten-digit invoice number as a phone number.

Context resolves the ambiguity. Consider the string 555-12-3456 versus 555-123-4567: the first groups its digits 3-2-4 like a US Social Security number, the second 3-3-4 like a NANP phone number. Now consider "your confirmation number is 3125550184" versus "you can reach me on 3125550184" — identical strings, different meanings, and only the surrounding words distinguish them. PII Detection API's transformer models weigh grouping, prefixes such as + and trunk zeros, and above all the semantic context: verbs like "call", "text", "reach", labels like "Tel:", "Mobile:", "Fax:", and the discourse role the number plays in the sentence.

Common Collisions and How Context Separates Them

  • Order and invoice IDs: "Order #5551234567 shipped" — preceded by an order label, followed by fulfillment language. Not flagged as a phone.
  • SSNs: 3-2-4 grouping and context like "SSN" or "Social Security" route the string to the SSN entity type instead — see how to detect SSNs.
  • Credit card fragments: Sixteen digits in 4-4-4-4 groups belong to CREDIT_CARD_NUMBER detection, with Luhn-consistent structure as a signal.
  • Dates and timestamps: "20240715123456" in a log line reads as a timestamp from its position and neighbors, not as a dialable number.
  • IP addresses and versions: Dotted digit groups such as "10.20.30.40" or "v2.4.1.100" are structurally distinct and classified as IP_ADDRESS or ignored.
Regex-Only Approach (typical result)
Order [PHONE?] was placed by the customer at [PHONE?] — invoice [PHONE?] refers to account [PHONE?].
Context-Aware Detection
Order 5512094417 was placed by the customer at [PHONE] — invoice 2024-88121 refers to account 55512034.

The difference matters operationally: over-flagging destroys the utility of masked data (analysts lose order IDs they need) and erodes trust in the pipeline, while under-flagging leaks PII. For a deeper comparison of detection techniques and their error profiles, read NER vs Regex vs Rules and our guide to measuring precision and recall.

Code Examples

International, Multi-Format Text

This example feeds the API a support-queue snippet containing NANP, UK, French, and extension formats in one pass. No format hints are needed — the model recognizes each national convention automatically:

import requests

text = (
    "US office: 555.123.4567 x22. Paris desk: 01 23 45 67 89. "
    "London: 020 7946 0958. Toll-free: 1-800-FLOWERS."
)

resp = requests.post(
    "https://piidetectionapi.com/api/moderate.php",
    json={
        "api_key": "YOUR_API_KEY",
        "api_type": "pii_detection",
        "text": text,
        "entities": ["PHONE_NUMBER"],
        "threshold": 0.6,
    },
    timeout=30,
)
for e in resp.json()["detected_entities"]:
    print(f"{e['text']!r} ({e['confidence']:.2f})")
# '555.123.4567 x22' (0.98)
# '01 23 45 67 89' (0.96)
# '020 7946 0958' (0.97)
# '1-800-FLOWERS' (0.93)
curl -X POST https://piidetectionapi.com/api/moderate.php \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "YOUR_API_KEY",
    "api_type": "pii_detection",
    "text": "US office: 555.123.4567 x22. Paris desk: 01 23 45 67 89. London: 020 7946 0958. Toll-free: 1-800-FLOWERS.",
    "entities": ["PHONE_NUMBER"],
    "threshold": 0.6
  }'
const text =
  "US office: 555.123.4567 x22. Paris desk: 01 23 45 67 89. " +
  "London: 020 7946 0958. Toll-free: 1-800-FLOWERS.";

const resp = await fetch("https://piidetectionapi.com/api/moderate.php", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    api_key: "YOUR_API_KEY",
    api_type: "pii_detection",
    text,
    entities: ["PHONE_NUMBER"],
    threshold: 0.6
  })
});
const { detected_entities } = await resp.json();
console.table(detected_entities);

Full Contact-Information Sweep

Phone numbers rarely travel alone — they appear next to names and email addresses in signatures and contact blocks. Sweeping all three entity types in one request is the standard pattern for sanitizing tickets, emails, and CRM notes:

import requests

resp = requests.post(
    "https://piidetectionapi.com/api/moderate.php",
    json={
        "api_key": "YOUR_API_KEY",
        "api_type": "pii_detection",
        "text": "Contact John Doe at [email protected] or 555-123-4567.",
        "entities": ["PERSON_NAME", "EMAIL_ADDRESS", "PHONE_NUMBER"],
        "mask_mode": "replace",
    },
    timeout=30,
)
data = resp.json()
print(data["anonymized_text"])
# Contact [NAME] at [EMAIL] or [PHONE].
curl -X POST https://piidetectionapi.com/api/moderate.php \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "YOUR_API_KEY",
    "api_type": "pii_detection",
    "text": "Contact John Doe at [email protected] or 555-123-4567.",
    "entities": ["PERSON_NAME", "EMAIL_ADDRESS", "PHONE_NUMBER"],
    "mask_mode": "replace"
  }'
const resp = await fetch("https://piidetectionapi.com/api/moderate.php", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    api_key: "YOUR_API_KEY",
    api_type: "pii_detection",
    text: "Contact John Doe at [email protected] or 555-123-4567.",
    entities: ["PERSON_NAME", "EMAIL_ADDRESS", "PHONE_NUMBER"],
    mask_mode: "replace"
  })
});
const data = await resp.json();
console.log(data.anonymized_text);
// Contact [NAME] at [EMAIL] or [PHONE].

Excluding Your Own Public Numbers

Company hotlines and public support numbers are not personal data, and masking them makes documents confusing. The custom_instruction field accepts plain-language exclusions so your published numbers pass through untouched while customer numbers are still caught:

import requests

resp = requests.post(
    "https://piidetectionapi.com/api/moderate.php",
    json={
        "api_key": "YOUR_API_KEY",
        "api_type": "pii_detection",
        "text": "Our hotline is 1-800-555-0199. The customer's cell is (312) 555-0184.",
        "entities": ["PHONE_NUMBER"],
        "mask_mode": "replace",
        "custom_instruction": "Do not flag our public support hotline 1-800-555-0199.",
    },
    timeout=30,
)
print(resp.json()["anonymized_text"])
# Our hotline is 1-800-555-0199. The customer's cell is [PHONE].
curl -X POST https://piidetectionapi.com/api/moderate.php \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "YOUR_API_KEY",
    "api_type": "pii_detection",
    "text": "Our hotline is 1-800-555-0199. The customer'\''s cell is (312) 555-0184.",
    "entities": ["PHONE_NUMBER"],
    "mask_mode": "replace",
    "custom_instruction": "Do not flag our public support hotline 1-800-555-0199."
  }'
const resp = await fetch("https://piidetectionapi.com/api/moderate.php", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    api_key: "YOUR_API_KEY",
    api_type: "pii_detection",
    text: "Our hotline is 1-800-555-0199. The customer's cell is (312) 555-0184.",
    entities: ["PHONE_NUMBER"],
    mask_mode: "replace",
    custom_instruction: "Do not flag our public support hotline 1-800-555-0199."
  })
});
console.log((await resp.json()).anonymized_text);

E.164 and Normalization

Detection tells you where a phone number is; normalization tells you which phone number it is. E.164 is the ITU standard for globally unambiguous numbers: a leading +, the country code, and the national number, with no spaces or punctuation and at most fifteen digits — for example +13125550184 or +442079460958. It is the canonical form used by telephony APIs, SMS gateways, and CRM deduplication.

A common production pattern is to combine the two steps: use PII Detection API to find each number's exact span and text, then normalize the extracted string to E.164 in your own code (libraries such as libphonenumber handle the country-specific rules, including dropping trunk zeros when a country code is added). The API's precise start/end offsets make the extraction step trivial, and the confidence score tells you which candidates are safe to normalize automatically.

Normalization is also where detection quality pays off. If your detector returns an order ID as a "phone number", normalization produces a garbage E.164 string that pollutes downstream systems — another reason context-aware detection beats pattern matching. Conversely, when the detector correctly captures an extension ("555-123-4567 ext. 22"), you can split the base number for E.164 while preserving the extension separately, rather than losing it or corrupting the number.

Tip: If you only need masked output, skip normalization entirely — mask_mode ("replace", "redact", or "hash") gives you a sanitized string in the same API call. Use "hash" when you need to count or join on distinct numbers without storing them: the same number always produces the same hash within your account.

Tuning Detection for Your Data

Out of the box, PII Detection API applies a confidence threshold of 0.5, which balances precision and recall for general text. Two knobs let you adapt this to your corpus.

Confidence Threshold

The threshold parameter (0–1) sets the minimum confidence for a match to be returned. Raise it toward 0.8 when your text is dense with non-phone digit strings (e-commerce logs, financial exports) and false positives are costly. Lower it toward 0.3 for compliance-critical masking of transcripts, where a missed number is worse than an occasional over-mask. Because every entity carries its own confidence score, you can also run at a low threshold and implement a two-tier policy in your code: auto-mask above 0.85, queue 0.5–0.85 for human review.

Custom Instructions

The custom_instruction field (up to 500 characters) accepts natural-language guidance: exclude your published hotlines, ignore numbers inside code samples, or treat five-digit strings in a given column as store IDs. This replaces the brittle allow-list regexes that traditional DLP tools require, and it travels with the request, so different pipelines can apply different policies against the same API key. Pricing is per request regardless of options — see pricing for volume tiers.

Best Practices

1. Scan Free Text, Not Just Phone Fields

Structured phone columns are the easy part. The numbers that cause incidents live in comment fields, ticket bodies, transcripts, and log payloads. Route every free-text field through detection before storage or export.

2. Detect the Full Contact Cluster

Request PERSON_NAME, EMAIL_ADDRESS, and ADDRESS together with PHONE_NUMBER. A masked phone number next to an unmasked name and email is only marginal progress; the cluster is what identifies a person.

3. Use Offsets for Auditability

Store the entity metadata (type, offsets, confidence) alongside masked documents. When an auditor or a data subject asks what was found and removed, you can answer precisely — and you can re-mask historical data if your policy tightens.

4. Test Against Your Own Formats

Every business has house styles: "Cust ph 3125550184", numbers pasted with copy artifacts, or region-specific conventions from your markets. Before production, run a sample through the live demo or the API and review the hits and misses, then adjust threshold and custom_instruction accordingly.

5. Keep Latency Budgets in Mind

Typical processing time is well under 200 ms per request, which fits synchronous chat and form-submission paths. For bulk backfills, batch texts up to the 50,000-character request limit and parallelize requests rather than concatenating unrelated documents.

Note: Masking numbers in new data does not clean up numbers already sitting in old logs and backups. Pair real-time detection with periodic scans of data at rest — see scanning application logs for PII.

Frequently Asked Questions

Which countries' phone formats are supported?

Detection covers the numbering plans of 200+ countries and territories, including NANP (US/Canada), all European national conventions, and Asia-Pacific formats, in both international (+country code) and domestic (trunk-prefixed) forms. Because the models also read context in 60+ languages, a Japanese number in Japanese text is detected just as reliably as a US number in English text.

Are extensions captured as part of the phone number?

Yes. Extensions written as "x1234", "ext. 1234", "extension 22", or "#1234" are included in the detected entity span, so masking removes the full dialable identity, not just the base number. The extension is part of what identifies a person, so treating it as PII is the safe default.

Can the API detect vanity numbers like 1-800-FLOWERS?

Yes. Vanity numbers that substitute keypad letters for digits are recognized as phone numbers even though they contain few literal digits. Note that toll-free and published business numbers are often not personal data; use custom_instruction to exclude your own public numbers from masking.

How do you avoid flagging order numbers, account numbers, and other digit strings?

The models classify by context, not just pattern: labels ("Order #", "Tel:"), surrounding verbs ("call me at" versus "your invoice"), and digit grouping all feed the decision. This is the main advantage over regex-based tools, which cannot distinguish a ten-digit order ID from a ten-digit phone number. If a specific ID scheme in your data still collides, a one-line custom instruction resolves it.

Does detection work on speech-to-text transcripts?

Yes. Numbers spoken aloud and transcribed as words ("five five five, one two three four"), hybrid word/digit forms, and transcription artifacts (extra spaces, repeated digits from disfluencies) are handled by the AI model. This makes the API suitable for call center recordings, voicemail transcription, and meeting notes.

Does the API return numbers in E.164 format?

The API returns each number exactly as it appears in your text, with precise character offsets, so masking and highlighting stay faithful to the original. For canonicalization, extract the matched text using the offsets and normalize it to E.164 in your code with a library such as libphonenumber — the detection step guarantees you are only normalizing genuine phone numbers.

Start Detecting Phone Numbers Today

Test the API on your own text in the live demo, or get an API key and integrate in minutes.

Try the Live Demo View Pricing