On this page
Every text a team sends to a language model can hold a name, an address, an IBAN or the ID number of a customer. Before that text goes anywhere, someone has to know what is in it.
Today we release Temperate PII, our first open model. It finds 13 types of personal data in European text, from names and street addresses to tax IDs, passport numbers and API keys. Because it needs no GPU, you can run it on the same machine that holds the text.
We built it for Temperate. Its memory uses the model to keep the right details of a text, and to leave out what it must never store, such as a password or an API key that someone pasted by accident. We tried several open and closed tools first, but on European text they were too slow or missed too much, and most of the time both. They fell short most on the numbers that differ per country, such as VAT numbers, passport numbers and driving licences.
We found no open model that did this well for European text, so we made ours open. The weights are on Hugging Face and the code is on GitHub, both under Apache-2.0. This page shows how we measured it, where it does better than Azure AI Language, and where it still misses.
Results
We measured Temperate PII on 2,041 texts in English, Dutch, German, French, Spanish and Italian. They hold 2,720 values that must be masked, and 4,307 that must stay as they are, such as company names, product codes and order numbers. Temperate PII missed 13 values and masked 8 that it should have left alone.
We ran the PII service of Azure AI Language over the same texts and scored it with the same scorer [1]. It missed 402 values and masked 145 by mistake.
Mistakes on 2,041 benchmark texts
Personal data missed, of 2,720
- Temperate PII13
- Azure AI Language402
Clean text masked by mistake, of 4,307
- Temperate PII8
- Azure AI Language145
Every type of personal data
On email addresses, IBANs and street addresses, the two are close. The gap is in the numbers that differ per country. Azure found 48% of the VAT numbers, 62% of the passport numbers and 88% of the driving licences. Temperate PII found all VAT and passport numbers, and 98.8% of the driving licences. Azure has no category for API keys and tokens, so it found none of the 211 in the benchmark.
Azure finds more person names: 99.5% against our 98.0%. It is the one type where our recall is lower.
Recall per type
- Person names98.0% / 99.5%
- Street addresses100.0% / 99.6%
- Phone numbers100.0% / 97.3%
- Email addresses100.0% / 100.0%
- IBANs100.0% / 100.0%
- Credit card numbers100.0% / 97.1%
- National ID numbers100.0% / 95.5%
- Tax IDs100.0% / 100.0%
- VAT numbers100.0% / 48.2%
- Passport numbers100.0% / 62.5%
- Driving licences98.8% / 87.8%
- IP addresses99.3% / 100.0%
- API keys and tokens100.0% / not supported
How it works
Most personal data has a format. An IBAN carries a checksum, a Dutch BSN passes the eleven test, and a card number passes the Luhn check. Rules find these values, with a pack per country for Belgium, France, Germany, Italy, the Netherlands, Spain and the United Kingdom. Names and street addresses have no format, so the model finds those.
The model is a student of six layers, distilled from a larger teacher and built on Multilingual MiniLM. It has 107 million parameters and runs as an int8 ONNX model. We trained it on open data: the PII masking set of ai4privacy and the multilingual set of nym [2][3].
A model that finds names also finds words that only look like names. A precision step after the model drops weak spans, such as a surname on its own with no title, greeting or earlier full name. "Vraag het aan Bakker van financiën" stays as it is: masking every word that could be a surname would also mask company and place names.
Hi Sanne, please send the invoice to NL91 ABNA 0417 1643 00 and call me on +31 6 1234 5678. Ask Bakker in finance if anything is unclear.
Every language
We trained the model on six languages, and its recall stays above 99% in each of them. The service accepts all 24 official languages of the EU. For a language the model was not trained on, the answer says so, and lists the types it could not check.
Recall per language
- Spanish 482 values99.8%
- French 483 values99.6%
- English 449 values99.6%
- German 393 values99.5%
- Italian 481 values99.4%
- Dutch 432 values99.3%
It is also fast enough to sit in front of every request. For a text of 2,000 characters, the median answer took 23 ms and the 95th percentile 31 ms, one request at a time on an Apple M1 Max with four threads, while other work ran on the same machine.
Where it misses
The benchmark is text we built, so we also wrote 26 business texts by hand: emails, invoices, contracts and meeting notes, in six languages. They hold 83 names and addresses. Temperate PII finds 76 of the 83, and masks one of the 112 company, product and place names in them by mistake. Three of its misses, as they are:
- "Katrin Weber", a full name in a German email.
- "Mark", a first name on its own in an English support mail.
- "79098 Freiburg im Breisgau", a postcode with its town in a German invoice.
We found one more while we wrote this page. In "Bel Sanne de Vries op 06 12345678", the model finds the phone number but not the name. With a greeting before it, it finds the name.
How we measure
- The benchmark. 2,041 texts. 963 are passages of openly licensed prose, most of them from Wikipedia, in which we replaced the real names and put in made-up identifiers that pass their national checks. We generated 975 more and wrote 103 by hand.
- Kept apart from training. A check compares every benchmark text with the training data: by its source, by its exact text, and by runs of eight words in a row. It found 30 shared runs of eight words, stock phrases, and none at ten words. No benchmark text is in the training data.
- One scorer for both. The same code scores Temperate PII and Azure. We mapped 39 of Azure's categories onto our 13 types. Azure also finds organizations and dates, which we do not score, and those spans did not count against it.
- Speed. 1,000 requests of 2,000 characters each, one at a time, after 100 requests to warm up.
- Checking it. The benchmark, the scorer and Azure's raw answers are in the repository, so you can run every number on this page again [4].
What these numbers do not say
- We wrote the cases, and that helps us. In our own measurements, scores on text like this run up to about 15 points higher than scores on real documents.
- Each figure comes from one run. Azure's comes from a run on 12 September 2026, and Azure can change its model under the same name.
- The model was trained on six languages. In the other 18 languages of the EU, the rules still work, but names and addresses are not checked.
- The speed was measured on one laptop. A server with other cores gives other numbers.
Try it
The weights are on Hugging Face as temperate-works/pii, and the code, the benchmark and the training scripts are on GitHub [4][5]. Both are under Apache-2.0. It runs from source with Rust, or in a container with two commands.
docker build -t temperate-pii .
docker run -p 8090:8090 temperate-piiSources
- [1] Microsoft, Azure AI Language, PII detection, model version 2026-05-01, run on 12 September 2026.
- [2] ai4privacy, pii-masking-openpii-1.5m, CC BY 4.0.
- [3] Wismut, nym-pii-multilingual-data, MIT.
- [4] Temperate, temperate-works/pii on GitHub: the code, the benchmark and the training scripts.
- [5] Temperate, temperate-works/pii on Hugging Face: the model and its card.