Quick Start Guide

Send text, get back every piece of sensitive data it contains — typed, located, and scored — in a single JSON response. Redaction is one optional parameter away.

Start Building →

Getting Started

The PII Detection API is a REST API that finds and classifies personally identifiable information (PII), protected health information (PHI), payment card data (PCI), and credentials/secrets in free text. Every request returns a structured detected_entities array — each entity with its type, the matched text, exact character offsets (start/end), and a confidence score — plus an optional masked version of your input (anonymized_text).

Detection is context-aware: transformer-based NER models understand surrounding language in 60+ languages, so "Bill" the person is detected while "bill" the invoice is not. Regex-only scanners cannot make that distinction. See supported languages and the full entity catalog.

Base URL: https://piidetectionapi.com/api/moderate.php — all requests are POST with a JSON body and api_type set to "pii_detection".

Three steps to your first detection:

1. Create an account and copy your API key. 2. POST your text to the endpoint below. 3. Iterate over detected_entities in the response. You can also try everything without writing code in the interactive demo.

Authentication

All API requests are authenticated with an API key passed as the api_key field in the JSON request body. There are no separate authentication headers to configure.

API Key Authentication

Include your API key in every request body along with api_type: "pii_detection":

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]"}'
Keep your API key secure! Never expose it in client-side code or commit it to version control. Store it in an environment variable and proxy browser traffic through your own backend.

PII Detection Endpoint

One endpoint handles all detection requests. Send up to 50,000 characters of text per call and receive every detected entity back with type, matched text, offsets, and confidence.

POST /api/moderate.php

Detect PII in Text

Scans the submitted text for sensitive entities and returns them as structured JSON, together with an optional masked copy of the input.

Request Body

Parameter Type Required Description
api_key string Required Your API key for authentication
api_type string Required Always "pii_detection"
text string Required Content to scan. Up to 50,000 characters per request; chunk longer documents into multiple calls.
entities array Optional Entity types to detect, e.g. ["PERSON_NAME", "EMAIL_ADDRESS", "SSN"]. Default: all 150+ supported types. See the catalog below and the full list on entities.php.
exclude_entities array Optional Entity types to skip. Anything matching these types is neither reported nor masked. Example: ["MEDICAL_TERM", "TREATMENT"] keeps generic clinical vocabulary visible while names and IDs are still detected.
mask_mode string Optional How anonymized_text is built: "replace" (default, [TYPE] placeholders), "redact" (removes the match), "hash" (consistent hashes — the same value always yields the same token).
threshold number Optional Minimum confidence (0–1) an entity needs to be included in the response. Default 0.5. Raise it for precision, lower it for recall.
custom_instruction string Optional Natural-language exclusions, e.g. "Do not flag 'Acme Corporation' or the address '123 Business Ave'". Maximum 500 characters.

Example Request

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."
     }'

Response Schema

Every successful response returns the detected entities first — that array is the core product of the API. The masked text is derived from it.

Example Response

{
    "detected_entities": [
        {
            "type": "PERSON_NAME",
            "text": "John Doe",
            "start": 8,
            "end": 16,
            "confidence": 0.95
        },
        {
            "type": "EMAIL_ADDRESS",
            "text": "[email protected]",
            "start": 20,
            "end": 36,
            "confidence": 0.98
        },
        {
            "type": "PHONE_NUMBER",
            "text": "555-123-4567",
            "start": 40,
            "end": 52,
            "confidence": 0.97
        }
    ],
    "anonymized_text": "Contact [NAME] at [EMAIL] or [PHONE].",
    "entities_detected": 3,
    "processing_time_ms": 187,
    "mask_mode_used": "replace",
    "status": 200
}

Response Fields

Field Type Description
detected_entities array One object per detected entity. Sorted by position in the input text.
detected_entities[].type string The entity type, e.g. PERSON_NAME, SSN, CREDIT_CARD_NUMBER.
detected_entities[].text string The exact substring of the input that was matched.
detected_entities[].start / end integer Zero-based character offsets into the original text. end is exclusive, so text == input[start:end].
detected_entities[].confidence number Model confidence between 0 and 1. Filter server-side with threshold or client-side as you prefer.
anonymized_text string The input with every detected entity masked according to mask_mode.
entities_detected integer Total number of entities found (length of detected_entities).
processing_time_ms integer Server-side processing time in milliseconds.
mask_mode_used string The masking strategy that was applied to build anonymized_text.
status integer HTTP-style status code, 200 on success. See error handling.
Offsets are exact. Because start/end index into the original text, you can build your own highlighting, redaction, or tokenization on top of the detection output without any string searching.

Entity Type Catalog

The API detects 150+ entity types. The most commonly used types are grouped below by category; the complete list with descriptions and per-type examples lives on the entities page. Pass any of these values in entities or exclude_entities.

Personal Identifiers

Entity Type Description Examples
PERSON_NAMEFull or partial person namesJohn Smith, Dr. Jane Wilson
EMAIL_ADDRESSEmail addresses[email protected]
PHONE_NUMBERPhone numbers (all formats)(555) 123-4567, +1-555-123-4567
SSNUS Social Security Numbers123-45-6789
NATIONAL_IDNational ID numbersVarious country formats
TAX_IDTax identification numbersEIN, VAT numbers
DATE_OF_BIRTHBirth dates05/12/1978, March 15, 1990
DRIVERS_LICENSE_NUMBERDriver's license numbersD45678901
PASSPORT_NUMBERPassport numbers123456789

Financial & Payment Card Data

Entity Type Description Examples
CREDIT_CARD_NUMBERCredit/debit card numbers (PANs)4111-1111-1111-1111
CREDIT_CARD_EXPIRATION_DATECard expiration dates12/25, 03/2027
CVV_NUMBERCard security codes123, 4567
FINANCIAL_ACCOUNT_NUMBERBank account numbers9876543210
IBAN_CODEInternational bank account numbersGB29 NWBK 6016 1331 9268 19
SWIFT_BICSWIFT/BIC codesDEUTDEFF
ROUTING_NUMBERBank routing numbers021000021

Medical & PHI

Entity Type Description Examples
MEDICAL_RECORD_NUMBERMedical record identifiersMRN-2024-98765
HEALTH_INSURANCE_IDHealth insurance identifiersINS-ABC-12345
MEDICAL_DATAGeneral medical informationConditions, symptoms
MEDICAL_TERMMedical terminologyHypertension, Metformin
PRESCRIPTIONPrescription informationMedication names and dosages
DIAGNOSISMedical diagnosesType 2 Diabetes, ICD codes
TREATMENTTreatment informationProcedures, therapies
BLOOD_TYPEBlood type informationA+, O-, AB+
BIOMETRIC_DATABiometric identifiersFingerprint, facial, voice data

Credentials & Secrets

Entity Type Description Examples
AUTH_TOKENAuthorization tokensBearer eyJhbGciOiJI...
API_KEYAPI keys and secretssk-live-xxx, AKIA...
PASSWORDPassword patternsVarious password formats
AWS_CREDENTIALSAWS access credentialsAWS access keys, secrets
AZURE_AUTH_TOKENAzure authentication tokensAzure AD tokens
GCP_CREDENTIALSGoogle Cloud credentialsGCP service account keys
SSH_KEYSSH private keys-----BEGIN RSA PRIVATE KEY-----
PRIVATE_KEYPrivate cryptographic keysPEM formatted keys
DATABASE_CONNECTION_STRINGDatabase connection stringspostgres://user:pass@host

Location Data

Entity Type Description Examples
ADDRESSStreet addresses123 Main Street, Apt 4B
CITYCity namesSan Francisco
STATEState/Province namesCalifornia, CA, Ontario
ZIP_CODEPostal/ZIP codes90210, SW1A 1AA
COUNTRYCountry namesUSA, United Kingdom
GPS_COORDINATESGPS coordinates37.7749, -122.4194

Device & Network Data

Entity Type Description Examples
IP_ADDRESSIP addresses (v4 and v6)203.0.113.55, 2001:db8::1
MAC_ADDRESSMAC addresses00:1A:2B:3C:4D:5E
DEVICE_IDDevice identifiersUUID, UDID patterns
IMEIIMEI numbers353456789012345
SERIAL_NUMBERSerial numbersSN-ABC123456
URLURLs and web addresseshttps://example.com/page
COOKIECookie dataSession cookies, tracking IDs
USER_AGENTUser agent stringsMozilla/5.0 (Windows NT...)

Demographic Data

Entity Type Description Examples
AGEAge information35 years old, born in 1988
GENDERGender informationMale, Female, Non-binary
ETHNIC_GROUPEthnic group referencesEthnicity identifiers
MARITAL_STATUSMarital statusSingle, Married, Divorced
RELIGIONReligious affiliationReligious identifiers
POLITICAL_AFFILIATIONPolitical affiliationParty affiliations
SEXUAL_ORIENTATIONSexual orientationOrientation identifiers
EMPLOYMENTEmployment informationJob titles, employers

Mask Modes

Detection is always the primary output. When you also need a sanitized copy of the input, mask_mode controls how anonymized_text is built:

mask_mode Behavior Example output
replace (default) Substitutes typed placeholders so the text stays readable and auditable Contact [NAME] at [EMAIL]
redact Removes the matched value entirely Contact at
hash Replaces each value with a consistent hash — the same input value always maps to the same token, preserving referential integrity for analytics and joins Contact [HASH_NAME_A1B2C3] at [HASH_EMAIL_F4E5D6]
import requests

text = "Contact John Doe at [email protected] or 555-123-4567."

for mode in ["replace", "redact", "hash"]:
    resp = requests.post(
        "https://piidetectionapi.com/api/moderate.php",
        json={
            "api_key": "YOUR_API_KEY",
            "api_type": "pii_detection",
            "text": text,
            "mask_mode": mode,
        },
        timeout=30,
    )
    data = resp.json()
    print(f"{mode:8s} -> {data['anonymized_text']}")
Tip: hash mode is ideal when downstream systems must still count or join on a value ("how many tickets mention the same customer?") without ever seeing the raw PII.

Confidence Threshold

Every entity carries a confidence score between 0 and 1. The threshold parameter (default 0.5) drops entities below your chosen minimum before the response is built — both from detected_entities and from the masking applied to anonymized_text.

Tuning Precision vs. Recall

Use a high threshold (0.8–0.9) when false positives are costly, e.g. auto-redacting legal documents. Use a low threshold (0.2–0.4) when missing PII is worse than over-flagging, e.g. DLP scanning or compliance discovery, and route low-confidence hits to human review.

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.",
        "threshold": 0.8,   # only high-confidence entities
    },
    timeout=30,
)
data = resp.json()
for e in data["detected_entities"]:
    print(e["type"], e["text"], e["confidence"])
Compliance workloads: for GDPR/HIPAA discovery, keep the threshold low and review borderline entities rather than raising the threshold and silently missing them. See measuring detection accuracy.

Custom Instructions

Natural-Language Exclusions

The custom_instruction field lets you steer detection in plain English. Terms you name are neither reported as entities nor masked — perfect for keeping your own company details, product names, or public reference numbers visible.

Use Cases:
  • Preserve your company name and address in scanned documents
  • Keep specific product names or trademarks out of the detection results
  • Exclude internal reference numbers that look like IDs but are not personal data
  • Leave public information (published office addresses, support lines) untouched

How to Use

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 Acme Corporation, 123 Business Ave, San Francisco. His email is [email protected] and phone is 555-123-4567.',
        mask_mode: 'replace',
        custom_instruction: "Do not flag 'Acme Corporation' or '123 Business Ave' as these are our company details."
    })
})
.then(response => response.json())
.then(data => console.log(data.detected_entities));

Example Output

{
    "detected_entities": [
        {"type": "PERSON_NAME", "text": "John Doe", "start": 8, "end": 16, "confidence": 0.95},
        {"type": "CITY", "text": "San Francisco", "start": 55, "end": 68, "confidence": 0.92},
        {"type": "EMAIL_ADDRESS", "text": "[email protected]", "start": 83, "end": 96, "confidence": 0.98},
        {"type": "PHONE_NUMBER", "text": "555-123-4567", "start": 110, "end": 122, "confidence": 0.97}
    ],
    "anonymized_text": "Contact [NAME] at Acme Corporation, 123 Business Ave, [CITY]. His email is [EMAIL] and phone is [PHONE].",
    "entities_detected": 4,
    "processing_time_ms": 203,
    "mask_mode_used": "replace",
    "status": 200
}
Important Notes:
  • Maximum 500 characters per instruction
  • Available with a valid API key (paid plans)
  • Instructions are processed securely and never stored
  • To skip whole categories instead of specific terms, use exclude_entities

Code Examples

The same request shape works from any language that can send JSON over HTTPS. Below: detect-all scans in cURL, Python, JavaScript, and PHP, followed by selected-entity detection in each language.

Detect Everything — cURL

Omit entities to scan for all 150+ types:

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."
     }'

Detect Everything — Python

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.",
    },
    timeout=30,
)
data = resp.json()
for e in data["detected_entities"]:
    print(e["type"], e["text"], e["start"], e["end"], e["confidence"])

Detect Everything — JavaScript (Node 18+ / Browser via backend proxy)

const response = await fetch('https://piidetectionapi.com/api/moderate.php', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: process.env.PII_API_KEY,
        api_type: 'pii_detection',
        text: 'Contact John Doe at [email protected] or 555-123-4567.'
    })
});
const data = await response.json();
data.detected_entities.forEach(e => {
    console.log(`${e.type}: "${e.text}" [${e.start}-${e.end}] (${e.confidence})`);
});

Detect Everything — PHP

<?php
$payload = [
    'api_key'  => getenv('PII_API_KEY'),
    'api_type' => 'pii_detection',
    'text'     => 'Contact John Doe at [email protected] or 555-123-4567.',
];

$ch = curl_init('https://piidetectionapi.com/api/moderate.php');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_TIMEOUT        => 30,
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);

foreach ($data['detected_entities'] as $e) {
    printf("%s: %s [%d-%d] (%.2f)\n",
        $e['type'], $e['text'], $e['start'], $e['end'], $e['confidence']);
}

Selected Entities Only

Pass entities to restrict detection to the types you care about — faster to review and cheaper to audit. Example: scan support tickets for names, emails, and phone numbers only.

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"
     }'
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()
for e in data["detected_entities"]:
    print(e["type"], e["text"], e["start"], e["end"], e["confidence"])
const response = await fetch('https://piidetectionapi.com/api/moderate.php', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: process.env.PII_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 response.json();
console.log(data.entities_detected, 'entities found');
<?php
$payload = [
    'api_key'   => getenv('PII_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',
];

$ch = curl_init('https://piidetectionapi.com/api/moderate.php');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_TIMEOUT        => 30,
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data['anonymized_text'], "\n";

Threshold Tuning — cURL & JavaScript

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.",
       "threshold": 0.8
     }'
// Low threshold for DLP-style discovery: catch everything, review later
const response = await fetch('https://piidetectionapi.com/api/moderate.php', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        api_key: process.env.PII_API_KEY,
        api_type: 'pii_detection',
        text: logLine,
        threshold: 0.3
    })
});
const data = await response.json();
const needsReview = data.detected_entities.filter(e => e.confidence < 0.6);

Mask Modes — cURL & PHP

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.",
       "mask_mode": "hash"
     }'
<?php
// Compare all three mask modes for the same input
foreach (['replace', 'redact', 'hash'] as $mode) {
    $ch = curl_init('https://piidetectionapi.com/api/moderate.php');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS     => json_encode([
            'api_key'   => getenv('PII_API_KEY'),
            'api_type'  => 'pii_detection',
            'text'      => 'Contact John Doe at [email protected] or 555-123-4567.',
            'mask_mode' => $mode,
        ]),
        CURLOPT_TIMEOUT        => 30,
    ]);
    $data = json_decode(curl_exec($ch), true);
    curl_close($ch);
    echo str_pad($mode, 8), ' -> ', $data['anonymized_text'], "\n";
}

Custom Instruction — Python

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 Acme Corporation, 123 Business Ave. Email: [email protected]",
        "mask_mode": "replace",
        "custom_instruction": "Do not flag 'Acme Corporation' or '123 Business Ave' as these are our company details.",
    },
    timeout=30,
)
print(resp.json()["anonymized_text"])
# Contact [NAME] at Acme Corporation, 123 Business Ave. Email: [EMAIL]

Rate Limits

Rate limits are applied per API key and vary by plan. When a limit is exceeded the API returns status 429 with a Retry-After header; back off and retry.

Plan Requests / minute Notes
Free 60 Ideal for testing and development
Professional 300 Production applications with moderate traffic
Enterprise Custom Dedicated capacity, on-premise deployment available — contact us
Payload limit: up to 50,000 characters of text per request. For long documents, split on paragraph boundaries and merge the entity offsets afterwards — a complete walkthrough is in the extended documentation. See plan details on the pricing page.

Error Handling

The API uses standard HTTP-style codes, returned both as the HTTP status and in the JSON status field. Error responses include a human-readable error message.

Status Code Meaning Description
200 OK Request successful; detection results in the body
400 Bad Request Missing api_type or text, invalid mask_mode, malformed JSON, or unknown entity type
401 Unauthorized Invalid or missing API key
402 Insufficient Credits Not enough remaining credits for this request — top up or wait for the quota reset
413 Payload Too Large Text exceeds 50,000 characters — split into smaller requests
429 Rate Limited Too many requests — honor Retry-After and use exponential backoff
500 Server Error Internal server error — safe to retry with backoff
{
    "error": "api_key is invalid or missing",
    "status": 401
}

For ready-made retry loops with exponential backoff in Python and Node.js, see the integration guide.

Try It Without Code

Paste sample text into the interactive demo and watch entities, offsets, and confidence scores appear live — then check the pricing plans when you are ready to integrate.

Open the Demo →