Enterprise Controls and TrustOctober 27, 2026Flatkey Team

PII Redaction API Guide (2026): AWS, Google Cloud, Azure, and Presidio Compared for LLM Apps

A PII redaction API finds and masks personal data in text. Compare Amazon Comprehend, Google Sensitive Data Protection, Azure Language PII and Presidio, with code, pricing units, real detection results and an LLM redaction pipeline.

PII Redaction API Guide (2026): AWS, Google Cloud, Azure, and Presidio Compared for LLM Apps

PII Redaction API Guide (2026): AWS, Google Cloud, Azure, and Presidio Compared for LLM Apps

A PII redaction API takes text, finds personal data such as names, emails, phone numbers, card numbers, and government IDs, and returns either the locations of that data or a copy of the text with it masked, replaced, or tokenized. For AI product teams, the main job is to strip or pseudonymize personal data before prompts, documents, and logs reach a third-party LLM. The four options most teams evaluate are Amazon Comprehend DetectPiiEntities, Google Cloud Sensitive Data Protection (formerly Cloud DLP), Azure Language PII detection, and the open-source Microsoft Presidio.

This guide compares them on endpoints, auth, language coverage, pricing units, and output format, with runnable examples using synthetic data. It also shows real detection results from a local Presidio test, including the misses and false positives you should plan for. All provider details were checked against official docs on October 27, 2026.

Flatkey does not offer a built-in PII redaction API. The patterns below run in your own service before a request is sent through any model gateway, including Flatkey. For the policy side of the same problem, read our AI API redaction policy guide.

Redaction, masking, entity detection, and tokenization: what's the difference?

Technique What it returns Reversible? Typical use
Entity detection Entity type, confidence score, and character offsets n/a (no change to text) Decide what to do per entity; audit what data is flowing
Redaction Text with PII removed or replaced by a label like [PERSON_1] No Prompts to external LLMs, logs, analytics
Masking Text with characters hidden, such as ***********1111 No Support screens that need partial values
Tokenization / pseudonymization Text with PII replaced by a consistent token or encrypted value Yes, with the key or lookup table LLM workflows that must put real values back into the answer
Field-level redaction Known fields (like email in JSON) dropped or replaced by schema Depends Structured payloads where you know which fields hold PII

For structured data, field-level rules are faster and more reliable than any model. Use PII detection APIs for free text: chat messages, support tickets, transcripts, and documents.

PII redaction APIs compared (October 2026)

Amazon Comprehend Google Sensitive Data Protection Azure Language PII Microsoft Presidio
Type Managed API Managed API Managed API (also containers) Open source, self-hosted
Main call DetectPiiEntities projects.content.deidentify analyze-text with PiiEntityRecognition AnalyzerEngine + AnonymizerEngine
Returns redacted text directly No, returns offsets (use offsets to redact) Yes Yes (redactedText) Yes
Reversible tokenization No Yes (crypto-based transformations) No Yes (encrypt / decrypt operators)
Language coverage for PII English and Spanish Many infoTypes, some country-specific Multiple languages per docs Depends on the NLP model you load
Input limit / unit 100 KB per request; billed per 100 characters, 3-unit minimum Billed per GiB; 1 KB minimum per request Billed per text record Your compute
Free allowance 50K units (5M characters) per API per month for 12 months First 1 GiB per month for inspection 5,000 text records per month (free tier) Free software
Data leaves your network Yes (AWS) Yes (Google Cloud) Yes, unless you run the container No

Sources: Amazon Comprehend PII docs, Comprehend pricing, Google de-identification docs, Sensitive Data Protection pricing, Azure PII overview, Azure Language pricing, and Presidio docs, all checked October 27, 2026.

What does it cost?

  • Amazon Comprehend: AWS's own example for Detect PII: 10,000 customer comments of 550 characters each count as 6 units per request, so 60,000 units at $0.0001 per unit cost $6 (Comprehend pricing, example 7). Short requests are billed at the 3-unit (300-character) minimum, so batching small messages saves money.
  • Google Sensitive Data Protection: content inspection is free for the first 1 GiB per month, then $3.00 per GiB, falling to $2.00 per GiB at higher volume; transformation is listed at $2.00 per GiB, then $1.00. A minimum of 1 KB is billed per request.
  • Azure Language: PII detection is billed per text record, with 5,000 free text records per month on the free tier. Paid tiers are listed on the Azure pricing page for your region.
  • Presidio: no license fee; you pay for the compute that runs the analyzer and the NLP model.

Amazon Comprehend: DetectPiiEntities

Comprehend detects PII in English or Spanish text. DetectPiiEntities returns each entity's Type, Score, BeginOffset, and EndOffset; the maximum input is 100 KB of UTF-8 text (API reference). It does not return redacted text, so you replace the spans yourself.

import boto3

comprehend = boto3.client("comprehend", region_name="us-east-1")
text = "Hi, I'm Dana Whitfield. Email dana.whitfield@example.com or call 212-555-0187."

resp = comprehend.detect_pii_entities(Text=text, LanguageCode="en")

# Replace from the end so earlier offsets stay valid
redacted = text
for e in sorted(resp["Entities"], key=lambda e: e["BeginOffset"], reverse=True):
    if e["Score"] >= 0.5:
        redacted = redacted[:e["BeginOffset"]] + f"[{e['Type']}]" + redacted[e["EndOffset"]:]
print(redacted)

Auth uses standard AWS credentials (IAM role, SSO, or access keys) with permission for comprehend:DetectPiiEntities.

Google Cloud Sensitive Data Protection: content.deidentify

Google's API inspects and transforms in one call. You choose infoTypes (such as EMAIL_ADDRESS and PHONE_NUMBER) and a transformation: replaceWithInfoTypeConfig, redactConfig, characterMaskConfig, or crypto-based pseudonymization (de-identification docs).

curl -X POST \
  "https://dlp.googleapis.com/v2/projects/$PROJECT_ID/content:deidentify" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "item": {"value": "Email dana.whitfield@example.com or call 212-555-0187."},
    "inspectConfig": {
      "infoTypes": [{"name": "EMAIL_ADDRESS"}, {"name": "PHONE_NUMBER"}, {"name": "PERSON_NAME"}]
    },
    "deidentifyConfig": {
      "infoTypeTransformations": {
        "transformations": [{"primitiveTransformation": {"replaceWithInfoTypeConfig": {}}}]
      }
    }
  }'

The response's item.value holds text like Email [EMAIL_ADDRESS] or call [PHONE_NUMBER]. Google recommends listing only the infoTypes you need, because scanning for the default set adds latency.

Azure Language: PII entity recognition

Azure's analyze-text endpoint returns both the entities and a redactedText field. The redactionPolicies parameter controls the output, for example entityMask for labels or characterMask for asterisks (REST reference).

curl -X POST "$AZURE_LANGUAGE_ENDPOINT/language/:analyze-text?api-version=2026-05-01" \
  -H "Ocp-Apim-Subscription-Key: $AZURE_LANGUAGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "PiiEntityRecognition",
    "parameters": {"redactionPolicies": [{"policyKind": "entityMask"}]},
    "analysisInput": {"documents": [
      {"id": "1", "language": "en", "text": "My name is John Doe My phone number is 424 878 9192"}
    ]}
  }'

Microsoft's documented sample response returns "redactedText": "My name is [PERSON_1] My phone number is [PHONENUMBER_1]", plus each entity's category, offset, length, and confidence score. Azure also supports PII detection for conversations and documents, and offers containers for running the model in your own environment.

Node.js version

const res = await fetch(
  `${process.env.AZURE_LANGUAGE_ENDPOINT}/language/:analyze-text?api-version=2026-05-01`,
  {
    method: "POST",
    headers: {
      "Ocp-Apim-Subscription-Key": process.env.AZURE_LANGUAGE_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      kind: "PiiEntityRecognition",
      parameters: { redactionPolicies: [{ policyKind: "entityMask" }] },
      analysisInput: {
        documents: [{ id: "1", language: "en", text: "Call Dana at 212-555-0187." }],
      },
    }),
  }
);
const data = await res.json();
console.log(data.results.documents[0].redactedText);

Microsoft Presidio: open-source PII redaction

Presidio runs entirely in your own infrastructure. The analyzer combines NLP named-entity recognition, regular expressions, checksums, and context words; the anonymizer applies operators like replace, mask, redact, hash, and encrypt. Its documentation is explicit that automated detection cannot guarantee it finds all sensitive data, so plan additional safeguards.

We installed Presidio (analyzer 2.2.365 with spaCy's en_core_web_lg model) and ran it on this synthetic sentence:

Hi, I'm Dana Whitfield. Email dana.whitfield@example.com or call 212-555-0187. Card 4111 1111 1111 1111, SSN 078-05-1120. Order #A-77231 ships to 42 Elm Street.

from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import OperatorConfig

text = ("Hi, I'm Dana Whitfield. Email dana.whitfield@example.com or call 212-555-0187. "
        "Card 4111 1111 1111 1111, SSN 078-05-1120. Order #A-77231 ships to 42 Elm Street.")

results = AnalyzerEngine().analyze(text=text, language="en")
for r in sorted(results, key=lambda r: r.start):
    print(r.entity_type, repr(text[r.start:r.end]), round(r.score, 2))

out = AnonymizerEngine().anonymize(
    text=text,
    analyzer_results=results,
    operators={
        "DEFAULT": OperatorConfig("replace", {"new_value": "<REDACTED>"}),
        "PERSON": OperatorConfig("replace", {"new_value": "<PERSON>"}),
        "EMAIL_ADDRESS": OperatorConfig("replace", {"new_value": "<EMAIL>"}),
        "CREDIT_CARD": OperatorConfig("mask", {"masking_char": "*", "chars_to_mask": 15, "from_end": False}),
    },
)
print(out.text)

What it caught and what it missed

Text Detected as Score Verdict
Dana Whitfield PERSON 0.85 Correct
dana.whitfield@example.com EMAIL_ADDRESS 1.0 Correct
example.com (inside the email) URL 0.5 False positive, overlaps the email
212-555-0187 PHONE_NUMBER 0.4 Correct but low confidence
4111 1111 1111 1111 CREDIT_CARD 1.0 Correct (checksum validated)
078-05-1120 PHONE_NUMBER 0.4 Wrong type: this is a famous invalid sample SSN, so the SSN recognizer rejected it
Elm Street LOCATION 0.85 Partly correct; house number "42" was left in the text
A-77231 (order ID) not detected n/a Missed: business identifiers need custom rules

Three lessons from this small test:

  1. Thresholds trade misses for noise. When we re-ran with score_threshold=0.5, the phone number (score 0.4) was dropped and left unredacted. Tune thresholds per entity type, not globally.
  2. Your own identifiers need custom recognizers. Adding a PatternRecognizer for \bA-\d{5}\b with score 0.6 caught the order ID. A realistic SSN-format value (536-90-4399) was detected as US_SSN at 0.85.
  3. Reversible tokenization works. Using the encrypt operator with a 128-bit key and the DeanonymizeEngine with decrypt restored the original text exactly.

A PII redaction pipeline for LLM apps

The common pattern for AI products is redact, call the model, then restore what the user needs to see:

  1. Field rules first. Drop or replace known PII fields from structured payloads by schema.
  2. Detect in free text. Run a PII API or Presidio on messages, tickets, and documents.
  3. Pseudonymize consistently. Replace each entity with a stable placeholder (<PERSON_1>) and keep the mapping in your service, never in the prompt.
  4. Call the LLM with the redacted text.
  5. Restore placeholders in the model output only where the user is allowed to see the real values.
  6. Log the redacted version and the entity counts, not the original.
import os, re
from openai import OpenAI
from presidio_analyzer import AnalyzerEngine

analyzer = AnalyzerEngine()
client = OpenAI(api_key=os.environ["FLATKEY_API_KEY"], base_url="https://router.flatkey.ai/v1")

def non_overlapping(results, min_score=0.35):
    kept = []
    # prefer higher scores, then longer spans; drop anything overlapping a kept span
    for r in sorted(results, key=lambda r: (-r.score, -(r.end - r.start))):
        if r.score >= min_score and all(r.end <= k.start or r.start >= k.end for k in kept):
            kept.append(r)
    return sorted(kept, key=lambda r: r.start, reverse=True)

def pseudonymize(text):
    mapping, counters = {}, {}
    for r in non_overlapping(analyzer.analyze(text=text, language="en")):
        counters[r.entity_type] = counters.get(r.entity_type, 0) + 1
        token = f"<{r.entity_type}_{counters[r.entity_type]}>"
        mapping[token] = text[r.start:r.end]
        text = text[:r.start] + token + text[r.end:]
    return text, mapping

ticket = "Customer Dana Whitfield (dana.whitfield@example.com) says order A-77231 never arrived."
safe, mapping = pseudonymize(ticket)
reply = client.chat.completions.create(
    model="claude-haiku-5-5",
    messages=[{"role": "user", "content": f"Draft a short apology email for this ticket:\n{safe}"}],
).choices[0].message.content
for token, value in mapping.items():  # restore only on your side
    reply = reply.replace(token, value)
print(reply)

The non_overlapping step matters: without it, the URL detected inside the email address corrupts the replacement. With it, our test ticket became Customer <PERSON_1> (<EMAIL_ADDRESS_1>) says order A-77231 never arrived. The order ID still passes through, which is why the custom recognizer from the previous section belongs in production. Presidio's own anonymizer resolves overlaps for you.

Failure handling and testing

Risk Mitigation
PII API timeout or 5xx error Fail closed: do not send the unredacted text to the LLM; queue and retry
Low-confidence detections Per-entity thresholds; review samples weekly
Unsupported language Detect language first; AWS Comprehend PII only covers English and Spanish
Business identifiers (order IDs, account numbers) Custom regex recognizers or field rules
False positives breaking meaning Allowlists for product names and public addresses
PII in model output Run detection on responses before logging or displaying to other users
Logs containing originals Log redacted text and entity counts only

Build a labeled test set of synthetic messages that look like your real traffic, including names from many cultures, international phone formats, and your own ID formats. Measure recall per entity type before launch and after every rule change.

Choosing a PII redaction API

  • Already on AWS and text is English or Spanish: Amazon Comprehend is simple and cheap at small volume.
  • Need reversible tokenization as a managed service, or many country-specific identifiers: Google Sensitive Data Protection.
  • Need redacted text directly, conversation or document PII, or on-premises containers: Azure Language PII.
  • Data must not leave your network, or you need full control of rules: Presidio, with custom recognizers and your own evaluation.

Many teams combine them: Presidio or field rules in the hot path, and a managed API for batch document scans.

How this fits with Flatkey

Flatkey is a model gateway: one key and an OpenAI-compatible endpoint for many LLMs. It does not detect or redact PII for you, so run redaction in your application before requests leave your service, as in the pipeline above. The Flatkey enterprise docs describe zero retention of request content, which reduces exposure but does not replace redaction. Related reading: AI API redaction policy, secure API key management, LLM cost tracking, and the Flatkey model catalog.

FAQ

What is a PII redaction API?

It is an API that detects personal data in text, such as names, emails, phone numbers, and ID numbers, and returns the locations of that data or a version of the text with it removed, masked, or replaced.

What is the best PII redaction API?

It depends on your stack and data. Amazon Comprehend suits English and Spanish text on AWS, Google Sensitive Data Protection is strongest for reversible tokenization and many infoTypes, Azure returns redacted text directly and runs in containers, and Presidio is best when data must stay in your network.

Is there a free PII redaction API?

Presidio is free open-source software. The managed services have free allowances: AWS Comprehend covers 50K units (5M characters) per API per month for 12 months, Google's first 1 GiB of inspection per month is free, and Azure's free tier includes 5,000 text records per month.

Should I redact PII before sending prompts to ChatGPT, Claude, or other LLM APIs?

Yes, when the model does not need the real values. Pseudonymize names, contact details, and IDs, send placeholders to the model, and restore the values in your own service if the user needs them.

Can PII redaction be reversed?

Simple redaction and masking cannot. Tokenization or encryption-based pseudonymization can be reversed with the key or mapping table, which you should store securely and separately from the redacted data.

How accurate are PII detection APIs?

None is perfect. In our Presidio test, a phone number scored only 0.4 and an order ID was missed until we added a custom rule. Measure recall on your own synthetic data and fail closed when the detector errors.

Does Flatkey redact PII?

No. Flatkey routes requests to models; redaction should happen in your application before the request is sent.