Skip to main content

PII Detection & Redaction API

The PII endpoint finds personally identifiable information in a text — email addresses, phone numbers, US Social Security numbers, credit card numbers, IP addresses, IBANs and US bank routing numbers — and optionally returns a redacted copy of the text with each entity replaced by a placeholder such as [EMAIL].

Detection is deterministic: every candidate must pass a structural check (for example a Luhn checksum for card numbers, a mod-97 checksum for IBANs, and the ABA checksum for routing numbers), so precision stays high without a machine-learning model, and results are fully reproducible. Every entity comes with exact character offsets into the text you sent, so you can apply the redactions to your own copy of a document — a support transcript, a chat log, an LLM prompt — before storing it or sending it on.

Person names and street addresses are not detected by this endpoint; those need a named-entity model rather than a checksum. For them, use the NER API, which returns people, organizations, locations and other named entities with the same offsets contract.

Try it out

Sample Code

curl -X POST https://api.sapling.ai/api/v1/pii \
-H "Content-Type: application/json" \
-d '{"key":"<api-key>", "text":"Reach me at jane.doe@example.com or (415) 555-0132.", "redact": true}'

Sample Response

{
"entities": [
{
"type": "email",
"text": "jane.doe@example.com",
"start": 12,
"end": 32,
"replacement": "[EMAIL]"
},
{
"type": "phone",
"text": "(415) 555-0132",
"start": 36,
"end": 50,
"replacement": "[PHONE]"
}
],
"flagged": true,
"types": ["email", "phone"],
"redacted_text": "Reach me at [EMAIL] or [PHONE]."
}

Without redact, the response contains only entities, flagged and types.

Request Parameters

POST to https://api.sapling.ai/api/v1/pii

key: String
32-character API key. Can also be supplied via the Authorization header as a bearer token; if both are provided, the key parameter takes precedence.

text: String
Text to scan for PII. Up to 20,000 characters. The text is scanned exactly as sent — HTML is not stripped and whitespace is not normalized — so that the returned offsets line up with your original. Pass plain text rather than HTML markup for the best results.

types: List
Optional list of PII types to detect. Defaults to all types. Supported values:

TypeWhat it matchesValidation
emailEmail addressesAddress format
phonePhone numbers, national or international (+) format, with common separatorsDigit count and separator shape; dates and SSN-shaped numbers are rejected
ssnUS Social Security numbers3-2-4 groups with a consistent separator, plus SSA area/group/serial rules
credit_cardPayment card numbers (Visa, Mastercard, American Express, Discover, JCB, Diners Club, UnionPay, Maestro)Luhn checksum and known issuer prefixes
ip_addressIPv4 and IPv6 addressesParsed as an IP address; version strings and times are rejected
ibanInternational Bank Account NumbersMod-97 checksum and per-country length
us_bank_routingUS ABA bank routing numbersABA checksum and Federal Reserve prefix ranges

redact: Boolean
If true, the response also includes redacted_text: the input with every detected entity replaced by its replacement placeholder. Defaults to false.

Response Parameters

entities: List
The detected PII entities, in document order. Spans never overlap; where two candidate matches overlap, the checksum-validated type wins (a Luhn-valid card number is never also reported as a phone number). Each item has:

  • type: one of the types listed above.
  • text: the matched text, exactly as it appears in the input.
  • start, end: character offsets into the input text, such that text[start:end] is the entity.
  • replacement: the placeholder used for this type in redacted_text, e.g. [EMAIL], [PHONE], [SSN], [CREDIT_CARD], [IP_ADDRESS], [IBAN], [US_BANK_ROUTING].

flagged: Boolean
true if at least one entity was detected.

types: List
The sorted list of distinct PII types found in the text.

redacted_text: String
Only present when redact is true. The input text with each entity replaced by its placeholder. Because the offsets refer to the original text, you can also build your own redaction (for example masking all but the last four digits of a card number) by splicing entities into your copy of the text.

Notes

  • Offsets count Unicode code points (Python str indexing), not UTF-16 code units or bytes. In JavaScript, use Array.from(text) if the input may contain characters outside the Basic Multilingual Plane.
  • Because detection is pattern-based, a syntactically valid but fictitious value (a test card number, a made-up email address) is still reported; the endpoint answers "does this look like PII", not "does this identify a real person".
  • For guardrail patterns around LLM inputs and outputs — including secrets and profanity — see Guardrails; for token-level profanity flagging see the Profanity API.