edgepiityped character spans
taxonomy 2026.11 GitHub

Python library · Apache-2.0 · local weights

Know exactly which characters are personal data.

edgepii finds personally identifiable information as typed character spans on the string you passed in. Every result row proves one thing: source[start:end] == text. If a detector returns an offset that fails that check, the library drops the span and counts it. It never searches the string for a repair.

  • 11public types
  • 1model pass per call
  • 0cloud PII services
  • 2pinned public corpora

What it returns

Real output from the rules lane. No weights, no network.

Source

Customer Ada Lovelace called from +852 5555 0101. Email ada@example.com. Card 4242424242424242. Order 48291.

Masked

Customer Ada Lovelace called from <PHONE_1>. Email <EMAIL_1>. Card <PAYMENT_1>. Order 48291.

Span table

TypeStartEndTextScore
PHONE3448+852 5555 0101—
EMAIL5671ada@example.com—
PAYMENT789442424242424242421.00

A dash means no checksum covers that form, so the library reports no score. It does not invent a probability. Order 48291 stays clear because an internal id is not in the type list.

Offsets you can check

A span is a slice of your string, not a sentence a model rewrote. You can assert it in a test and diff it in a review.

Rules beside models

Email, phone, card, IBAN, and IP follow a published grammar or checksum. Names need a model. The preset runs both and merges the results.

Secrets stay gone

A password is masked irreversibly and never enters the vault. Restoring it would leak the credential into the next system.

Measured in public

Two pinned corpora with a sha256 each. Exact-span F1 per type, with the misses counted. See the score.

The whole API in one example

Three calls: build a locator, locate spans, mask them.

python
from edgepii import Locator, redact

source = (
    "Customer Ada Lovelace called from +852 5555 0101. "
    "Email ada@example.com. Card 4242424242424242. Order 48291."
)

result = Locator.preset("rules").locate(source)

for span in result.spans:
    proof = source[span.start:span.end] == span.text
    print(span.type, span.start, span.end, repr(span.text), proof)

masked, vault = redact(source, result.spans)
print(masked)
# Customer Ada Lovelace called from <PHONE_1>. Email <EMAIL_1>. Card <PAYMENT_1>. Order 48291.

Eleven public types

One YAML file is the only type list. Rank breaks an overlap.

  • SECRETsecret
  • GOV_IDgov_id
  • PAYMENTpayment
  • ADDRESSperson
  • EMAILcontact
  • IP_ADDRESScontact
  • PERSONperson
  • PHONEcontact
  • USERNAMEcontact
  • DATE_OF_BIRTHquasi
  • ORGquasi
Headshot of Raphael Mansuy, author of edgepii.

Built by Raphael Mansuy

Data engineering and AI strategist. Author of The Definitive Guide to Data Integration. Ranked 12 in the Favikon Top 20 AI Experts in France for 2026.