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

API Integration Guide

From zero to detecting PII in production: quickstart, drop-in Python and Node.js wrappers, pipeline and webhook patterns, and how to test everything against the live demo.

1

Quickstart: First Detection Call

The PII Detection API finds, classifies, and locates sensitive data in text: 150+ entity types across personal, financial, medical/PHI, credential, location, device/network, and demographic categories, in 60+ languages. Each detected entity comes back with its type, the matched text, exact character offsets, and a confidence score.

Base URL: https://piidetectionapi.com/api/moderate.php — all requests are POST with a JSON body.

Authentication is done by including your api_key in the JSON request body. Every request must also include "api_type": "pii_detection". Get your key on the get started page.

Important: Replace YOUR_API_KEY in all examples below with your own API key. Never expose your API key in client-side code or public repositories — call the API from your backend.
Minimal cURL 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:

JSON 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
}
That's the whole integration surface: one endpoint, one JSON shape. Everything below is about using it well.
2

Request & Response Essentials

All request fields:

ParameterTypeDescription
api_keystringRequiredYour API key for authentication.
api_typestringRequiredAlways "pii_detection".
textstringRequiredThe content to scan. Up to 50,000 characters per request.
entitiesarrayOptionalEntity types to detect, e.g. ["PERSON_NAME","EMAIL_ADDRESS","SSN"]. If omitted, all 150+ types are detected. Full catalog: entities.php.
exclude_entitiesarrayOptionalEntity types to skip — neither reported nor masked. Example: ["MEDICAL_TERM", "TREATMENT"].
mask_modestringOptional"replace" (default) — [TYPE] placeholders.
"redact" — removes the match.
"hash" — consistent hashes (same value, same token).
thresholdnumberOptionalMinimum confidence 0–1 for an entity to be returned. Default 0.5.
custom_instructionstringOptionalNatural-language exclusions to preserve specific terms. Max 500 characters. Paid plans only.

Every successful response (status 200) includes:

FieldTypeDescription
detected_entitiesarrayThe detection results — each entity with type, text, start, end, confidence. Offsets are zero-based and end-exclusive: text == input[start:end].
anonymized_textstringThe input with detected entities masked according to mask_mode.
entities_detectedintNumber of entities found.
processing_time_msintServer-side processing time in milliseconds.
mask_mode_usedstringThe masking strategy that was applied.
statusintHTTP-style status code (200 for success).
3

Python SDK-Style Wrapper

A small client class you can drop into any project: typed helpers for detection, redaction, and a boolean "contains PII?" check, with retries built in.

Python — pii_client.py
# pip install requests
import os, time
import requests

class PIIDetectionClient:
    """Minimal client for the PII Detection API."""

    API_URL = "https://piidetectionapi.com/api/moderate.php"

    def __init__(self, api_key=None, timeout=60, retries=3):
        self.api_key = api_key or os.environ["PII_API_KEY"]
        self.timeout = timeout
        self.retries = retries

    def detect(self, text, entities=None, exclude_entities=None,
               mask_mode="replace", threshold=0.5, custom_instruction=None):
        payload = {
            "api_key": self.api_key,
            "api_type": "pii_detection",
            "text": text,
            "mask_mode": mask_mode,
            "threshold": threshold,
        }
        if entities:
            payload["entities"] = entities
        if exclude_entities:
            payload["exclude_entities"] = exclude_entities
        if custom_instruction:
            payload["custom_instruction"] = custom_instruction

        for attempt in range(self.retries + 1):
            resp = requests.post(self.API_URL, json=payload, timeout=self.timeout)
            data = resp.json()
            if data["status"] == 200:
                return data
            if data["status"] == 429 or data["status"] >= 500:
                time.sleep(2 ** attempt)   # retryable: backoff
                continue
            raise RuntimeError(f"PII API error {data['status']}: {data.get('error')}")
        raise RuntimeError("PII API: max retries exceeded")

    def contains_pii(self, text, threshold=0.5):
        """True if any entity is detected at or above the threshold."""
        return self.detect(text, threshold=threshold)["entities_detected"] > 0

    def redact(self, text, mask_mode="replace"):
        """Return only the masked text."""
        return self.detect(text, mask_mode=mask_mode)["anonymized_text"]


# Usage
client = PIIDetectionClient()

result = client.detect("Contact John Doe at [email protected] or 555-123-4567.")
for e in result["detected_entities"]:
    print(e["type"], e["text"], e["start"], e["end"], e["confidence"])

if client.contains_pii(user_message):
    safe_text = client.redact(user_message)
4

Node.js Module

The same wrapper as an ES module for Node 18+ (built-in fetch). Export one client instance and reuse it across your app.

JavaScript — piiClient.mjs
const API_URL = 'https://piidetectionapi.com/api/moderate.php';

export class PIIDetectionClient {
    constructor({ apiKey = process.env.PII_API_KEY, timeoutMs = 60000, retries = 3 } = {}) {
        this.apiKey = apiKey;
        this.timeoutMs = timeoutMs;
        this.retries = retries;
    }

    async detect(text, options = {}) {
        const payload = {
            api_key: this.apiKey,
            api_type: 'pii_detection',
            text,
            mask_mode: options.maskMode ?? 'replace',
            threshold: options.threshold ?? 0.5,
        };
        if (options.entities) payload.entities = options.entities;
        if (options.excludeEntities) payload.exclude_entities = options.excludeEntities;
        if (options.customInstruction) payload.custom_instruction = options.customInstruction;

        for (let attempt = 0; attempt <= this.retries; attempt++) {
            const res = await fetch(API_URL, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload),
                signal: AbortSignal.timeout(this.timeoutMs)
            });
            const data = await res.json();
            if (data.status === 200) return data;
            if (data.status === 429 || data.status >= 500) {
                await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
                continue;
            }
            throw new Error(`PII API error ${data.status}: ${data.error}`);
        }
        throw new Error('PII API: max retries exceeded');
    }

    async containsPII(text, threshold = 0.5) {
        const { entities_detected } = await this.detect(text, { threshold });
        return entities_detected > 0;
    }

    async redact(text, maskMode = 'replace') {
        const { anonymized_text } = await this.detect(text, { maskMode });
        return anonymized_text;
    }
}

// Usage
import { PIIDetectionClient } from './piiClient.mjs';
const pii = new PIIDetectionClient();

const result = await pii.detect('Contact John Doe at [email protected]', {
    entities: ['PERSON_NAME', 'EMAIL_ADDRESS']
});
console.log(result.detected_entities);
5

PHP Integration

For PHP backends, a single function wraps the whole API. Store the key in an environment variable and call it wherever user content enters your system.

PHP — detect_pii.php
<?php
function detect_pii(string $text, array $options = []): array
{
    $payload = array_merge([
        'api_key'  => getenv('PII_API_KEY'),
        'api_type' => 'pii_detection',
        'text'     => $text,
    ], $options);

    $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        => 60,
    ]);
    $data = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $data ?? ['status' => 500, 'error' => 'no response'];
}

// Usage: scan a support ticket before saving it
$result = detect_pii($ticketBody, [
    'entities'  => ['PERSON_NAME', 'EMAIL_ADDRESS', 'PHONE_NUMBER', 'CREDIT_CARD_NUMBER'],
    'mask_mode' => 'replace',
]);

if ($result['status'] === 200 && $result['entities_detected'] > 0) {
    $safeBody = $result['anonymized_text'];
}
6

Selective Detection & Thresholds

By default the API scans for everything. Two knobs narrow the results to what your workflow acts on:

entities restricts detection to the listed types — smaller responses, explicit policy:

Python — only financial identifiers
result = client.detect(
    "Card 4111-1111-1111-1111, IBAN GB29 NWBK 6016 1331 9268 19, routing 021000021.",
    entities=["CREDIT_CARD_NUMBER", "IBAN_CODE", "ROUTING_NUMBER", "CVV_NUMBER"],
)

threshold trades recall for precision. Discovery scans (DLP, compliance audits) run low; automated redaction runs high:

Python — two-tier review workflow
result = client.detect(document, threshold=0.3)   # catch everything plausible

auto_redact  = [e for e in result["detected_entities"] if e["confidence"] >= 0.8]
human_review = [e for e in result["detected_entities"] if e["confidence"] < 0.8]
Rule of thumb: 0.2–0.4 for discovery where a miss is worse than a false alarm; 0.8+ for unattended redaction where a false positive damages the document. Background on the tradeoff: measuring detection accuracy.
7

Mask Modes

Detection results are always the same; mask_mode only changes how anonymized_text is rendered:

mask_modeBehaviorExample output
"replace"Typed placeholders — readable and auditableContact [NAME] at [EMAIL] or [PHONE].
"redact"Removes the matched value entirelyContact at or .
"hash"Consistent hashes — the same value always yields the same token, so counts and joins survive maskingContact [HASH_NAME_A1B2C3] at [HASH_EMAIL_F4E5D6] or [HASH_PHONE_K9L8M7].
Python — compare all three modes
text = "Contact John Doe at [email protected] or 555-123-4567."

for mode in ["replace", "redact", "hash"]:
    result = client.detect(text, mask_mode=mode)
    print(f"{mode:8s} -> {result['anonymized_text']}")

Need a custom format — partial masking, your own placeholder scheme, HTML highlighting? Build it from the offsets in detected_entities; the extended documentation has a complete redaction-pipeline tutorial.

8

Custom Instructions & Excluding Entity Types

Two complementary tools keep non-sensitive content out of your results:

exclude_entities skips whole categories. Anything matching those types is neither reported nor masked:

Python — keep clinical vocabulary visible
result = client.detect(
    "The patient saw the doctor at the hospital. Dr. Smith ordered tests for John Doe.",
    exclude_entities=["MEDICAL_TERM", "TREATMENT", "DIAGNOSIS"],
)
print(result["anonymized_text"])
# The patient saw the doctor at the hospital. Dr. [NAME] ordered tests for [NAME].

custom_instruction preserves specific terms in plain English (max 500 characters, paid plans):

Python — preserve your company details
result = client.detect(
    "Contact John Doe at Acme Corporation, 123 Business Ave. Email: [email protected]",
    custom_instruction="Do not flag 'Acme Corporation' or '123 Business Ave' as these are our company details.",
)
print(result["anonymized_text"])
# Contact [NAME] at Acme Corporation, 123 Business Ave. Email: [EMAIL]
Which one? Whole categories → exclude_entities. Specific words or phrases → custom_instruction. They can be combined in one request.
9

Pipeline & Webhook Integration Patterns

The API is stateless and fast, so it slots into event-driven systems as a filter step. Three patterns cover most deployments:

Pattern A — inline gate (webhooks, chat, form submissions). Scan the payload the moment it arrives; block, mask, or route based on what is found:

JavaScript — Express webhook handler
app.post('/webhook/ticket-created', async (req, res) => {
    const result = await pii.detect(req.body.ticket.description, {
        entities: ['PERSON_NAME', 'EMAIL_ADDRESS', 'PHONE_NUMBER',
                   'CREDIT_CARD_NUMBER', 'SSN'],
        maskMode: 'replace'
    });

    if (result.entities_detected > 0) {
        // store the masked copy; log types + offsets only, never raw values
        await saveTicket({ ...req.body.ticket, description: result.anonymized_text });
        await auditLog(req.body.ticket.id,
            result.detected_entities.map(({ type, start, end }) => ({ type, start, end })));
    } else {
        await saveTicket(req.body.ticket);
    }
    res.sendStatus(200);   // respond quickly; keep heavy work async
});

Pattern B — queue consumer (Kafka, SQS, ETL). Put the detection call inside your consumer so PII is found before records land in the warehouse. Batch with bounded concurrency and requeue on 429/5xx. Full architectures: PII detection in ETL & streaming pipelines.

Pattern C — LLM guardrail. Scan prompts before they reach a model and completions before they reach users; block or mask based on entities_detected. Walkthrough: PII detection for LLM guardrails.

Sizing note: keep each message under 50,000 characters and match your worker concurrency to your plan's rate limit (Free: 60 req/min, Professional: 300 req/min — see pricing).
10

Testing with the Interactive Demo

Before writing integration code, use the live demo to explore how detection behaves on your real data shapes. Paste representative samples — a support ticket, a log line, a clinical note — and check:

  • Which entity types fire on your content, so your entities list matches reality;
  • Confidence scores for borderline matches, to pick a sensible threshold;
  • What each mask mode does to readability of the output;
  • False positives you should handle with exclude_entities or custom_instruction.

Then reproduce the same configuration in code and add a smoke test to CI: a fixture string with known PII, asserting that the expected types are detected and that offsets slice back to the matched text. Sample texts in other languages are worth testing too — see supported languages.

Python — pytest smoke test
def test_detects_known_pii():
    text = "Contact John Doe at [email protected] or 555-123-4567."
    result = client.detect(text)

    types = {e["type"] for e in result["detected_entities"]}
    assert {"PERSON_NAME", "EMAIL_ADDRESS", "PHONE_NUMBER"} <= types

    for e in result["detected_entities"]:
        assert text[e["start"]:e["end"]] == e["text"]
11

Error Handling & Retries

The API returns descriptive error messages. Always check the status field in the response:

StatusMeaningWhat to do
200SuccessProcess detected_entities.
400Bad RequestFix the request: missing api_type or text, invalid mask_mode, or unknown entity type. Do not retry unchanged.
401UnauthorizedCheck the API key. Do not retry unchanged.
402Insufficient CreditsTop up credits or wait for the quota reset.
413Payload Too LargeText exceeds 50,000 characters — chunk the document.
429Rate LimitedRetry with exponential backoff; honor Retry-After.
500Server ErrorRetry with exponential backoff.

The wrappers in sections 3 and 4 already implement the retry policy: backoff on 429/5xx, immediate failure on 4xx. Two additional practices for production:

  • Fail closed for guardrails: if the detection call ultimately fails in a privacy-critical path, treat the content as if it contained PII rather than letting it through unscanned.
  • Alert on 402 early: monitor credit-related failures so a quota exhaustion never silently disables scanning.

Ready to Get Started?

Try the live demo on your own text, then grab an API key and ship your first detection call in minutes.

 Try the Demo  View Pricing