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.
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]"}'
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.
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. |
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_NAME | Full or partial person names | John Smith, Dr. Jane Wilson |
EMAIL_ADDRESS | Email addresses | [email protected] |
PHONE_NUMBER | Phone numbers (all formats) | (555) 123-4567, +1-555-123-4567 |
SSN | US Social Security Numbers | 123-45-6789 |
NATIONAL_ID | National ID numbers | Various country formats |
TAX_ID | Tax identification numbers | EIN, VAT numbers |
DATE_OF_BIRTH | Birth dates | 05/12/1978, March 15, 1990 |
DRIVERS_LICENSE_NUMBER | Driver's license numbers | D45678901 |
PASSPORT_NUMBER | Passport numbers | 123456789 |
Financial & Payment Card Data
| Entity Type | Description | Examples |
|---|---|---|
CREDIT_CARD_NUMBER | Credit/debit card numbers (PANs) | 4111-1111-1111-1111 |
CREDIT_CARD_EXPIRATION_DATE | Card expiration dates | 12/25, 03/2027 |
CVV_NUMBER | Card security codes | 123, 4567 |
FINANCIAL_ACCOUNT_NUMBER | Bank account numbers | 9876543210 |
IBAN_CODE | International bank account numbers | GB29 NWBK 6016 1331 9268 19 |
SWIFT_BIC | SWIFT/BIC codes | DEUTDEFF |
ROUTING_NUMBER | Bank routing numbers | 021000021 |
Medical & PHI
| Entity Type | Description | Examples |
|---|---|---|
MEDICAL_RECORD_NUMBER | Medical record identifiers | MRN-2024-98765 |
HEALTH_INSURANCE_ID | Health insurance identifiers | INS-ABC-12345 |
MEDICAL_DATA | General medical information | Conditions, symptoms |
MEDICAL_TERM | Medical terminology | Hypertension, Metformin |
PRESCRIPTION | Prescription information | Medication names and dosages |
DIAGNOSIS | Medical diagnoses | Type 2 Diabetes, ICD codes |
TREATMENT | Treatment information | Procedures, therapies |
BLOOD_TYPE | Blood type information | A+, O-, AB+ |
BIOMETRIC_DATA | Biometric identifiers | Fingerprint, facial, voice data |
Credentials & Secrets
| Entity Type | Description | Examples |
|---|---|---|
AUTH_TOKEN | Authorization tokens | Bearer eyJhbGciOiJI... |
API_KEY | API keys and secrets | sk-live-xxx, AKIA... |
PASSWORD | Password patterns | Various password formats |
AWS_CREDENTIALS | AWS access credentials | AWS access keys, secrets |
AZURE_AUTH_TOKEN | Azure authentication tokens | Azure AD tokens |
GCP_CREDENTIALS | Google Cloud credentials | GCP service account keys |
SSH_KEY | SSH private keys | -----BEGIN RSA PRIVATE KEY----- |
PRIVATE_KEY | Private cryptographic keys | PEM formatted keys |
DATABASE_CONNECTION_STRING | Database connection strings | postgres://user:pass@host |
Location Data
| Entity Type | Description | Examples |
|---|---|---|
ADDRESS | Street addresses | 123 Main Street, Apt 4B |
CITY | City names | San Francisco |
STATE | State/Province names | California, CA, Ontario |
ZIP_CODE | Postal/ZIP codes | 90210, SW1A 1AA |
COUNTRY | Country names | USA, United Kingdom |
GPS_COORDINATES | GPS coordinates | 37.7749, -122.4194 |
Device & Network Data
| Entity Type | Description | Examples |
|---|---|---|
IP_ADDRESS | IP addresses (v4 and v6) | 203.0.113.55, 2001:db8::1 |
MAC_ADDRESS | MAC addresses | 00:1A:2B:3C:4D:5E |
DEVICE_ID | Device identifiers | UUID, UDID patterns |
IMEI | IMEI numbers | 353456789012345 |
SERIAL_NUMBER | Serial numbers | SN-ABC123456 |
URL | URLs and web addresses | https://example.com/page |
COOKIE | Cookie data | Session cookies, tracking IDs |
USER_AGENT | User agent strings | Mozilla/5.0 (Windows NT...) |
Demographic Data
| Entity Type | Description | Examples |
|---|---|---|
AGE | Age information | 35 years old, born in 1988 |
GENDER | Gender information | Male, Female, Non-binary |
ETHNIC_GROUP | Ethnic group references | Ethnicity identifiers |
MARITAL_STATUS | Marital status | Single, Married, Divorced |
RELIGION | Religious affiliation | Religious identifiers |
POLITICAL_AFFILIATION | Political affiliation | Party affiliations |
SEXUAL_ORIENTATION | Sexual orientation | Orientation identifiers |
EMPLOYMENT | Employment information | Job 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']}")
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"])
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.
- 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
}
- 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 |
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 →