How it works
The whole engine is decidable from the text in front of it. That is a limit — it is why person names are absent — and it is also the entire reason the numbers on this site can be checked.
Each one is a rule or a checksum — Luhn for cards, mod-97 for IBANs, a base64 header that has to decode to JSON with an alg field, a published vendor prefix for a credential. There is no model in this path and no network call, so the same input always gives you the same output, and every figure we publish about it means something.
A JWT contains things that look like phone numbers. A PEM block contains things that look like everything. So spans are ranked — private key, then JWT, then credential, then IBAN, card, SSN, email, address, phone — and a lower-priority span that overlaps an accepted one is dropped entirely. Never shortened, because half a redacted card number is a leaked card number.
In tokenize mode each distinct value gets a stable numbered token, shared across every document in the call, and you get a mapping back. In mask mode it becomes a fixed per-type marker and there is no mapping at all. If your text already contained something shaped like one of our tokens, new ones are numbered above it and the collision is reported rather than absorbed.
A Luhn-valid number with no card-network prefix. Ten bare digits in North American dialling shape. A high-entropy string next to a secret-shaped label. Each of those is counted in unsure and handed back with offsets — left in the text, because removing them would wreck precision on every order number you own.
What gets written is a row of counts: characters, findings by type, unsure, credits. Not your text, not the output, not the mapping, and not in the logs — errors are recorded as identifiers rather than messages, because a message can quote its input.
The closed enum
Email address
An RFC-shaped local part, an @, a domain with at least one dot and a 2–24 character TLD. Bounded on both sides so an address inside a longer token is not half-matched.
Phone number
Either an E.164 number (a leading + and 8–15 digits) or a North American number written with separators or parentheses, with the area code and exchange code both starting 2–9. A bare run of ten digits with no separators is NOT treated as a phone number.
Payment card number
A 13–19 digit run that passes the Luhn checksum AND carries a known network prefix (Visa, Mastercard, Amex, Discover, JCB, Diners, UnionPay, Maestro). Luhn alone is reported as unsure, never redacted.
IBAN
Two letters, two check digits, then the exact body length that country publishes, validated with the ISO 7064 mod-97 checksum.
US Social Security number
AAA-GG-SSSS with a dash or space separator, rejecting the ranges the SSA never issues (area 000/666/900-999, group 00, serial 0000). Also matched when nine bare digits directly follow the words 'SSN' or 'social security'.
IP address
A dotted quad with every octet in 0–255, or an IPv6 address parsed structurally (never regex-guessed). A quad written directly after a 'v' is treated as a version string and left alone.
API key or token
A credential carrying a published vendor prefix — OpenAI, Anthropic, GitHub, AWS, Google, Slack, Stripe, SendGrid, GitLab, DigitalOcean, Hugging Face, npm, Shopify, Twilio, Notion, Linear. High-entropy strings with no known prefix are reported as unsure, never redacted.
JSON Web Token
Three base64url segments where the first one actually decodes to JSON containing an `alg` field. A string that merely looks like a JWT is not one.
Private key block
A complete PEM block, from a BEGIN … PRIVATE KEY line to its matching END line, including the body.
Mask or tokenize
What it will not do
There is no rule that separates a person's name from any other pair of capitalised words, and this engine deliberately runs no model. A name-detector built on a list would miss every name not on the list while looking like it worked. Names stay visible in the output — see the unsure count and the hero on the homepage.
Street addresses vary by country in ways no regex settles, and the plausible patterns collide with ordinary prose. A partial address redaction is worse than none: it removes the street and leaves the city, so the record still identifies one household.
A date is only a birth date because of what surrounds it. Redacting every date would gut a document; redacting some of them would be a guess about which.
Outside the US SSN there is no checksum we can rely on across issuing countries, and the formats overlap with ordinary reference numbers.
US ABA routing numbers do carry a checksum, but nine bare digits with no label match far too many internal identifiers. Nine-digit runs in the SSA's issued range are reported in `unsure` rather than removed.
A random-looking string is as likely to be a hash, a content id or a base64 payload as a credential. Only credentials with a published vendor prefix are redacted; the rest are counted in `unsure` so you can look.
This app takes text. It does no OCR and no transcription — use Extractor or Transcriber first, then send the text here.
Limits and price
per call
200,000
characters, across documents
per call
50
documents, one token namespace
price
1 cr
per 10,000 characters, rounded up