Detect and redact account & credit-card numbers
Sanitext detects account and credit-card numbers under the model label ACCOUNT. POST text to /v1/detect and get back each match as a span with byte offsets, the matched text, and a confidence score. POST to /v1/redact to replace every match with [ACCOUNT] in one call.
Card and account numbers are some of the highest-risk data you can leak into logs, prompts, or support tickets. Sanitext finds them in free text and masks them before that text leaves your control. Detection runs on our own open-weights model on our own Cloudflare infra, so the raw number never touches a third-party LLM.
What the ACCOUNT label covers
| Number type | Example format | Detected as |
|---|---|---|
| Credit/debit card | 4111 1111 1111 1111 | ACCOUNT |
| IBAN | DE89 3704 0044 0532 0130 00 | ACCOUNT |
| Bank account number | 000123456789 | ACCOUNT |
| Truncated card | ending in 4242 | ACCOUNT |
| Customer/invoice account | Acct #88-44210 | ACCOUNT |
What counts as an account or credit-card number?
The ACCOUNT label covers numeric identifiers that point to a financial account or instrument inside free-flowing text. The model is trained to read context, not just digit patterns, so it flags the number a human would read as sensitive.
Sanitext returns numbers as found. It does not validate them against a card network or bank. A flagged span means the text looks like an account number, not that the account exists.
- —Credit and debit card numbers (13 to 19 digits, with or without spaces or dashes)
- —Bank account numbers and IBANs
- —Routing, sort code, and SWIFT/BIC references when written as an account
- —Loan, invoice, and customer account numbers in a financial context
- —Partial card numbers like the last four digits when context makes them sensitive
How does detection work?
You send raw text to POST /v1/detect. Sanitext returns a list of spans. Each span has a label (ACCOUNT), the matched text, a start and end byte offset, and a confidence score between 0 and 1.
Byte offsets are exact positions in the UTF-8 text, so you can slice, highlight, or redact the match yourself without re-searching the string. This matters when the same number appears more than once.
If you only need clean output, POST the same text to /v1/redact. Sanitext replaces every ACCOUNT match with the placeholder [ACCOUNT] and hands back the masked text in one call. We log counts and timings only, never the raw text.
- —label: the entity type, here ACCOUNT
- —text: the exact substring that matched
- —start / end: UTF-8 byte offsets into your input
- —score: model confidence from 0 to 1, useful for setting a threshold
What edge cases and formats does it handle?
Card and account numbers appear in many shapes. Sanitext handles common separators and formatting so you do not have to normalize text first.
Because detection is context-aware, a 16-digit number in a sentence about a payment is flagged, while a 16-digit order tracking code may score lower. Use the score to tune how aggressive your redaction is.
- —Spaces or dashes between digit groups, for example 4111 1111 1111 1111 or 4111-1111-1111-1111
- —IBANs with country prefixes and grouped digits, for example DE89 3704 0044 0532 0130 00
- —Numbers wrapped in quotes, parentheses, or labels like Card: or Cuenta:
- —Masked or truncated values such as ****1234 or ending in 4242
- —Multiple account numbers in one block, each returned as its own span
Does it work in other languages?
Yes. Sanitext detects ACCOUNT entities in 30+ languages. The label and API shape stay the same no matter the language of the surrounding text.
Account and card numbers are mostly digits, so the value itself looks similar across languages. What changes is the words around it. The model reads cues like Cuenta, Compte, Konto, or IBAN in their native language to decide whether a number is an account.
bash — POST /v1/detect → ACCOUNT spans
curl https://api.sanitext.app/v1/detect \
-H "Authorization: Bearer $SANITEXT_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Charge card 4111 1111 1111 1111, IBAN DE89 3704 0044 0532 0130 00."}'
# Response
{
"spans": [
{
"label": "ACCOUNT",
"text": "4111 1111 1111 1111",
"start": 12,
"end": 31,
"score": 0.98
},
{
"label": "ACCOUNT",
"text": "DE89 3704 0044 0532 0130 00",
"start": 38,
"end": 65,
"score": 0.96
}
]
}
# POST /v1/redact on the same text returns:
# "Charge card [ACCOUNT], IBAN [ACCOUNT]." FAQ
What label does Sanitext use for credit-card numbers?+
Sanitext uses a single label, ACCOUNT, for both credit and debit card numbers and bank account numbers like IBANs. /v1/detect returns each match as an ACCOUNT span with byte offsets and a confidence score, and /v1/redact masks them as [ACCOUNT].
Does Sanitext validate card numbers with a Luhn check?+
No. Sanitext flags text that looks like an account or card number using a context-aware model. It does not run a Luhn check or confirm the card is real or active. A match means the text is likely sensitive, not that the account exists.
Is this enough for PCI DSS compliance?+
No. Sanitext is a redaction and data-minimization aid, not a PCI DSS compliance guarantee. It helps you keep card numbers out of logs, prompts, and tickets, but you still need scoped controls, review, and your own assessment for full PCI coverage.
Does the card number get sent to a third-party LLM?+
No. Sanitext runs an open-weights model on our own Cloudflare infrastructure, so your text never goes to an outside LLM. We log request counts and timings only, with no raw-text retention. EU data residency and a DPA are available on Enterprise.
Mask card and account numbers in 60 seconds
Sign up, grab an API key, and run your first /v1/redact call with curl. Free tier gives you 300K characters with no card required.
Get your free API key