Sanitext

Remove PII before sending text to an LLM

To remove PII before sending text to an LLM, run the text through a detection API first, mask spans like names, emails, and phone numbers, then send the clean text to ChatGPT, Claude, or Gemini. Sanitext does this in one call and runs its own model, so scrubbing never touches a third-party AI.

Why does PII leak when you call an LLM?

When you send user text straight to ChatGPT, Claude, or Gemini, every name, email, phone number, and address in that text goes with it. That data lands in a third-party system you do not control.

The problem gets worse with the modern AI stack. Raw text often ends up in three places at once: the LLM provider's logs, your RAG vector index, and your agent's trace history. Each copy is a new place where personal data sits exposed.

For regulated data this is a real risk. Support tickets, call transcripts, and chat logs are full of personal details that users did not expect you to forward to an outside model. You want the LLM's answer, not the liability.

  • —LLM provider keeps the prompt in its request logs
  • —RAG pipelines embed raw text into a vector database
  • —Agent frameworks save full traces for debugging
  • —User text can contain names, emails, phones, addresses, account numbers, and secrets

What is the mask, call, restore pattern?

The fix is a three-step wrapper around your LLM call. First, mask the PII. Second, call the LLM with the clean text. Third, optionally restore the original values in the response.

Mask: send the user text to a detection API. It finds spans like FIRSTNAME, EMAIL, and PHONE and swaps them for placeholders such as [FIRSTNAME] or [EMAIL]. Now no real personal data is in the prompt.

Call: pass the masked text to ChatGPT, Claude, or Gemini as you normally would. The model still understands the structure of the request because the placeholders keep the sentence intact.

Restore (optional): if you need the real values back in the final answer, keep a map of placeholder to original value on your own server and swap them back after the model replies. The LLM never sees the real data, but your user does.

  • —Mask: detect spans and replace with [LABEL] placeholders
  • —Call: send clean text to your LLM
  • —Restore: swap placeholders back locally if you need the real values
  • —The LLM, your RAG index, and your traces only ever store masked text

Why does Sanitext fit this pattern?

The masking step itself must not leak data. Many redaction tools call a hosted LLM to find PII, which means your text hits a third-party model before it ever reaches ChatGPT. That defeats the point.

Sanitext owns its model. It is an open-weights model from the OpenAI privacy-filter family, Apache-2.0 licensed, running on our own Cloudflare infrastructure. The detection step never sends your text to a third-party AI.

Because we run our own model, there is no per-token AI cost. Pricing is flat and transparent: Free gives a one-time 300K characters with no card, Hobby is $29/mo for 12M, Pro is $149/mo for 120M, and Scale is $499/mo for 450M. Overage runs $0.35 to $0.60 per million characters, up to about 2x cheaper than metered hyperscalers.

Sanitext detects across 30+ languages and labels FIRSTNAME, LASTNAME, EMAIL, PHONE, ADDRESS, DATE, URL, ACCOUNT, and SECRET. We do not retain raw text. We log counts and timings only, and EU data residency plus a DPA are available on Enterprise.

One honest note: this is a redaction and data-minimization aid, not an anonymization or compliance guarantee. It removes the obvious identifiers from your prompts. It does not by itself make you GDPR or HIPAA compliant.

How do you integrate it?

Use POST /v1/redact when you just want clean text to forward to the LLM. It returns the text with spans masked as [LABEL]. Use POST /v1/detect when you need the exact spans, including label, byte offsets, score, and matched text, so you can build your own placeholder map for the restore step.

Sign up, pay through Stripe, get an API key, and run a curl in about 60 seconds. There is no model to host and no GPU to manage.

A few practical tips. Mask before you embed text for RAG, not after, so your vector index never stores raw PII. Cache nothing that contains the original values outside your own systems. And keep the placeholder-to-value map server-side and short-lived if you use restore, so the mapping never travels to the model.

  • —/v1/redact returns masked text ready to send to any LLM
  • —/v1/detect returns spans for building a restore map
  • —Mask before embedding for RAG, not after
  • —Keep any restore map server-side and short-lived

python

import requests, os
from openai import OpenAI

SANITEXT_KEY = os.environ["SANITEXT_API_KEY"]
llm = OpenAI()

def redact(text: str) -> str:
    r = requests.post(
        "https://api.sanitext.app/v1/redact",
        headers={"Authorization": f"Bearer {SANITEXT_KEY}"},
        json={"text": text},
    )
    r.raise_for_status()
    return r.json()["text"]  # e.g. "Hi, I'm [FIRSTNAME], email [EMAIL]"

def ask_llm(user_text: str) -> str:
    # 1. mask PII before it ever leaves your server
    clean = redact(user_text)

    # 2. call the LLM with masked text only
    resp = llm.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": clean}],
    )
    return resp.choices[0].message.content

user_input = "My name is Maria Lopez, call me at +34 600 123 456 about order 5512."
print(ask_llm(user_input))
# The LLM, its logs, and your traces only ever see masked placeholders.

FAQ

Does the redaction step send my text to a third-party AI?+

No. Sanitext runs its own open-weights model on our Cloudflare infrastructure. Your text is scanned for PII without ever reaching ChatGPT, Claude, Gemini, or any other hosted model. We log counts and timings only, never raw text.

Can I restore the original values in the LLM response?+

Yes. Use /v1/detect to get spans with byte offsets, then keep a placeholder-to-value map on your own server. After the LLM replies, swap placeholders back to the real values. The model only ever sees masked text, but your user sees the full answer.

Does masking PII before an LLM call make me GDPR or HIPAA compliant?+

No. This is a redaction and data-minimization aid, not a compliance guarantee. It removes obvious identifiers like names, emails, and phone numbers from your prompts, which reduces exposure. Full GDPR or HIPAA compliance still depends on your contracts, controls, and processes.

Will placeholders confuse the LLM?+

Usually not. Placeholders like [FIRSTNAME] or [EMAIL] keep the sentence structure intact, so the model still understands the request. For most tasks like summarizing, classifying, or answering, the real identifier value is not needed to produce a useful result.

Should I mask text before adding it to a RAG index?+

Yes. Mask before you embed, not after. If you embed raw text, the personal data is copied into your vector database and travels with every retrieval. Masking first keeps your index free of identifiers while preserving the meaning the model needs.

Mask PII before your next LLM call

Sign up free for a one-time 300K characters, no card. Get an API key and wrap your ChatGPT, Claude, or Gemini call in 60 seconds.

Get your free API key