How to Redact PII from the Anthropic (Claude) API in Python

Share
August 31, 2026·7 min read·security

If your application sends user-generated text to the Anthropic Claude API, it will eventually send personal data — a Social Security number in a support ticket, a customer name in a chat message, a card number in a form. This guide shows three ways to catch and redact that PII in Python before it reaches Anthropic's servers.

The Problem

When you call client.messages.create(), the entire prompt — including any PII embedded in it — leaves your infrastructure and transits Anthropic's. Even under Anthropic's zero-retention commercial terms, that transit can still violate HIPAA, CCPA, GDPR, or your own data-classification policy.

The risk: raw PII in a Claude prompt
import anthropic
client = anthropic.Anthropic()

# This sends "John Smith, SSN 123-45-6789" to Anthropic
response = client.messages.create(
    model="claude-sonnet-4",
    max_tokens=1024,
    messages=[{
        "role": "user",
        "content": "Summarize this support ticket: Customer John Smith "
                   "(SSN 123-45-6789) called about billing issue #4521.",
    }],
)

Approach 1: Regex Pattern Matching

The fastest approach. Sanitize the message content before you build the messages array. Catches structured PII like SSNs and cards, but misses free-text PII like names and addresses.

Regex-based redaction before the Claude call
import re
import anthropic

SSN = re.compile(r'\b(?!000|666|9\d{2})\d{3}[- ]?(?!00)\d{2}[- ]?(?!0000)\d{4}\b')
CARD = re.compile(r'\b(?:\d[ -]*?){13,16}\b')

def redact(text: str) -> str:
    text = SSN.sub("[SSN_REDACTED]", text)
    text = CARD.sub("[CARD_REDACTED]", text)
    return text

client = anthropic.Anthropic()
raw = "Customer John Smith (SSN 123-45-6789), card 4111-1111-1111-1111."
response = client.messages.create(
    model="claude-sonnet-4",
    max_tokens=1024,
    messages=[{"role": "user", "content": redact(raw)}],
)
# Claude sees: "Customer John Smith (SSN [SSN_REDACTED]), card [CARD_REDACTED]."
#              ^ name still leaks — regex can't catch it

Limitation: You'd need a separate pattern for every PII type — phone numbers, emails, driver's licenses, passports, IBANs — and regex still can't catch context-dependent PII like person names. Maintaining dozens of patterns is error-prone.

Approach 2: NLP-Based Detection (Presidio)

Microsoft Presidio combines NLP models with pattern matching to detect 30+ entity types — SSNs, cards, names, addresses, and more. Much broader coverage than regex, at the cost of some latency.

Presidio redaction before the Claude call
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
import anthropic

analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()

def redact_pii(text: str) -> str:
    results = analyzer.analyze(
        text=text, language="en",
        entities=["US_SSN", "CREDIT_CARD", "PHONE_NUMBER",
                  "EMAIL_ADDRESS", "PERSON", "US_DRIVER_LICENSE"],
    )
    return anonymizer.anonymize(text=text, analyzer_results=results).text

client = anthropic.Anthropic()
raw = "Customer John Smith (SSN 123-45-6789) called about billing."
response = client.messages.create(
    model="claude-sonnet-4",
    max_tokens=1024,
    messages=[{"role": "user", "content": redact_pii(raw)}],
)
# Claude sees: "Customer <PERSON> (SSN <US_SSN>) called about billing."

Trade-off: Presidio adds ~30–200ms per request and you must deploy and maintain the NLP models yourself. Great for batch jobs; can be tight for real-time chat.

Approach 3: Gateway-Level Redaction (Zero Code Changes)

Instead of adding redaction code to every call, route requests through an AI gateway that detects and redacts PII before forwarding to Anthropic. Because the gateway is OpenAI-compatible, you can even call Claude through the OpenAI SDK — one client, any model, automatic 30+ entity redaction.

Call Claude through the gateway with the OpenAI SDK
from openai import OpenAI

# Point the OpenAI SDK at the gateway — then request a Claude model
client = OpenAI(
    base_url="https://api.aisecuritygateway.ai/v1",
    api_key="aisg_your_key_here",
)

response = client.chat.completions.create(
    model="claude-sonnet-4",   # Claude, called via the OpenAI SDK
    messages=[{
        "role": "user",
        "content": "Customer John Smith (SSN 123-45-6789) called about "
                   "billing issue #4521. Card ending 4111-1111-1111-1111.",
    }],
)
# What Anthropic sees: "Customer [PERSON] (SSN [US_SSN]) called about
#   billing issue #4521. Card ending [CREDIT_CARD]."

Prefer to keep the native Anthropic SDK? Point its base_url at the gateway and the same redaction applies:

Or keep the native Anthropic SDK
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.aisecuritygateway.ai",
    api_key="aisg_your_key_here",
)

response = client.messages.create(
    model="claude-sonnet-4",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Customer John Smith, SSN 123-45-6789..."}],
)
# PII is redacted at the gateway before it ever reaches Anthropic.

Which Approach Should You Use?

CriteriaRegexPresidio NLPGateway
Entity coveragePer pattern30+ entity types30+ entity types
Setup time5 minutes30–60 minutes2 minutes
Added latency< 1ms30–200ms< 50ms
Catches names/addressesNoYesYes
Code changes requiredPer API callPer API call2 lines total
MaintenanceHigh (pattern updates)Medium (model updates)None
Works across providersManual per providerManual per providerAutomatic

Beyond SSNs: Other PII in Claude Traffic

Production Claude traffic carries far more than SSNs:

  • Credit/debit card numbers — Luhn-validated, all major networks
  • Phone numbers — US, UK, and international formats
  • Email addresses — including corporate domains
  • Person names — NLP-based, handles “John Smith” and “Dr. Jane Doe”
  • Physical addresses — street, city, state, ZIP
  • Driver's license & passport numbers — multi-region formats
  • Medical record numbers — HIPAA-relevant
  • IBAN / bank account numbers — EU banking identifiers

Claude's vision support also means PII can arrive inside images — a scanned form, a screenshot. A gateway that runs OCR-based redaction catches those too, which regex and text-only Presidio pipelines miss.

Stop writing PII regex for every provider

Occludra auto-redacts 30+ entity types from every API call — Claude, GPT, Gemini, Llama, and 8+ more — in under 50ms, with two lines of code. Text and image OCR.

Related Articles

Security6 min read

How to Redact SSNs from OpenAI API Calls (Python)

The same three approaches, applied to the OpenAI API.

Security11 min read

Prompt-Level PII Redaction Under 50ms

Gateway-layer DLP without introducing unacceptable latency.