Find personal data in a CSV

This page reads every cell of your CSV and reports, column by column, which ones hold personal data. Ten detectors, ranked by severity, with checksums where a checksum exists. Every sample in the report is redacted before you see it. The file is never uploaded: it is read, matched and reported inside this browser tab, and nothing is stored between visits.

Ten detectors, and a checksum wherever one exists

A regex that matches sixteen digits will call your order numbers credit cards. Run that across a warehouse export and half the columns light up, at which point everybody stops reading the report and the tool has done worse than nothing. So every type here that carries a mathematical check is put through it before the cell counts as a match.

  • US Social Security number, high. The Social Security Administration never issued area 000, area 666 or anything from 900 upward, never issued group 00, and never issued serial 0000. All four rules are applied. On top of that, a bare nine-digit run is only accepted as an SSN when the column name says so; without a helpful header the value has to be punctuated as 078-05-1120 or 078 05 1120. Otherwise every zip+4 and every invoice number in the file becomes a false alarm.
  • Payment card number, high. Thirteen to nineteen digits, spaces or hyphens allowed, and the Luhn check has to pass. Storing these puts the file in PCI scope, which is why they sit at the top of the report.
  • IBAN, high. Validated with mod-97: move the first four characters to the end, turn letters into numbers with A as 10, and the remainder against 97 must be exactly 1. The arithmetic runs in chunks because the number is far larger than a double can hold. Treat a hit as a payment credential, not a reference code.
  • Email address, medium. A shape check that rejects the commas, semicolons and angle brackets that mail clients leave behind, so a pasted address list does not read as one enormous address.
  • Phone number, medium. Seven to fifteen digits, the E.164 range, and either a leading plus, some punctuation, or a column name that says phone, mobile, tel, cell or fax. Seven unpunctuated digits with no supporting header is an order number.
  • Date of birth, medium. A date shape plus a header containing dob, birth or born. A date column on its own is a date, not a birth date, and guessing otherwise would flag every created_at in the file.
  • Person name, medium. Two or three capitalized words with no digits, in a column whose header mentions name, contact, customer, employee, patient, owner or author. Headers like username, filename, product_name and company_name are excluded, because those are the ones that make a name detector unusable.
  • IPv4 and IPv6, low. Both, and the IPv6 detector handles the compressed double-colon forms and the IPv4-mapped tail. An address is personal data under GDPR whenever it can be tied to a person, which is most of the time in an application log.
  • Postal code, low. UK, US zip and zip+4, Canadian, and the plain four and five digit European shapes, again only in a column that says zip, postal or postcode. Harmless on its own. Dangerous in combination, which is a theme on this page.

Order matters as much as the patterns. Dates and postal codes are tested before phone numbers, because to a regex they look the same: 1987-04-12 is eight digits with separators and 94043-1351 is nine, which is exactly the shape of a phone number. Check phones first and every birth date in the file gets reported as a mobile. Checksummed types come first of all, so a valid card is never demoted to a phone.

Worked example: three rows, six columns

Here is contacts.csv, small enough to check by eye:

customer_id,email,phone,card,dob,zip
C-1001,ada.lovelace@northwind.example,+44 7700 900431,4539 5789 0080 4532,1987-04-12,SW1A 1AA
C-1002,grace.hopper@northwind.example,+1 415 555 0132,4111 1111 1111 1111,1992-11-03,94043
C-1003,alan.turing@northwind.example,,,1979-06-25,94043

The report that comes back looks like this:

Personal data scan of contacts.csv
3 rows x 6 columns

HIGH SEVERITY
-------------
  card  ·  Payment card number
    2 of 2 non-blank values (100.0%)
    samples (redacted): **** **** **** 4532, **** **** **** 1111
    storing these puts the file in PCI scope

MEDIUM SEVERITY
---------------
  email  ·  Email address
    3 of 3 non-blank values (100.0%)
    samples (redacted): a***@***.example, g***@***.example
    hash it if you need to join on it

  dob  ·  Date of birth
    3 of 3 non-blank values (100.0%)
    samples (redacted): 1987-**-**, 1992-**-**, 1979-**-**
    generalize to a year or an age band

  phone  ·  Phone number
    2 of 2 non-blank values (100.0%)
    samples (redacted): +**********31, +*********32
    mask all but the last two digits

LOW SEVERITY
------------
  zip  ·  Postal code
    3 of 3 non-blank values (100.0%)
    samples (redacted): SW***, 94***
    safe alone, identifying in combination

Several things in that output are worth pointing at. The email finding shows two samples for three matches, because Ada and Alan both redact to a***@***.example and duplicates are dropped. The zip finding shows two for the same reason. Three distinct samples is the cap, so a column of fifty thousand addresses still costs you five lines of report. The phone samples keep the leading plus and the final two digits and blank everything in between, which is enough to confirm the column is phone numbers and not enough to dial one.

The ranking is severity first, then the share of the column that matched, then the raw count. That is why the card column leads even though it has the fewest matches, and why a column that is entirely email addresses outranks a comments field with one address in it. The share is printed against the non-blank cells rather than the row count, so a mostly empty column does not look like a small problem when the values it does hold are all sensitive.

And then there is customer_id, which the scan does not mention at all. C-1001 is not personal data in any pattern-matching sense. It is personal data in every practical sense, because somewhere there is a table that turns it back into a name. That gap is the subject of the next section, and it is the most important thing on this page.

What a value scan cannot see

This is the part most scanners leave out, so here it is up front. The report tells you what a set of pattern detectors could recognize in the cells of one file. Three whole categories of personal data are invisible to that approach, and no amount of extra regexes fixes any of them.

  • Free text. A support notes column containing "called Priya back about the Camden flat" holds a name and a location, and neither is in a shape a detector can match. Long text columns are where personal data accumulates without anybody deciding to put it there. Read them.
  • Internal identifiers. A customer key, a device id, a session token, a case number. None of them identifies anybody by itself, all of them identify somebody the moment they meet the system that issued them. Under GDPR that makes them pseudonymous data, which is still personal data, and the scan cannot tell them apart from an ordinary reference code.
  • Quasi-identifiers in combination. The famous result here is Latanya Sweeney's: a five digit zip code, a full date of birth and a gender identify a large majority of the US population uniquely. Each of those columns is unremarkable alone. Together they are a name. Postal code plus birth date plus job title usually does the same thing on an HR export. A per-column scan is structurally incapable of seeing a risk that only exists across columns.

When nothing matches, the report does not print a reassuring green tick. It says nothing was detected and then spells out those three limits in the output itself, because a clean scan misread as a clean file is the specific failure that gets data shared when it should not be. Use the finding list as a floor. Read the columns it did not name.

This is not legal advice, and it is not compliance

Nothing on this page makes a file GDPR compliant, HIPAA compliant, or compliant with anything else. A scan is evidence, not a certificate. Compliance depends on why you hold the data, what lawful basis you claim, how long you keep it, who can reach it, what your contracts say and what the rest of your estate does with the same records. A browser tab can see one file for a few seconds and none of that context.

Two distinctions are worth carrying away, because they get blurred constantly in tool marketing. First, pseudonymized data is still personal data. Replacing a name with a token reduces risk and changes what safeguards you need, and it does not take the file outside the regulation. Recital 26 is explicit about it. Second, true anonymization is a much higher bar than replacing values. Data counts as anonymous only when nobody, using any means reasonably likely to be used, can get back to a person. Because re-identification through combination is always on the table, clearing that bar usually means suppressing or generalizing columns until the file cannot single anybody out, which costs you analytical value. Anyone who tells you a one-click tool produces anonymous data is selling something.

What the scan is genuinely good for: catching the column somebody forgot before the export leaves the building, giving a reviewer a ranked list to work through, and producing a findings CSV that is safe to paste into a ticket because every sample in it is already redacted. Those are real jobs. Do them, then talk to whoever owns privacy where you work.

Practical notes

  • Every cell is read. No sampling, because the whole risk is that the leak is in the rows you skipped. The ceiling is twenty million cells; past it the page stops and reports the count rather than locking the tab, and suggests splitting the file.
  • Blank cells are excluded from the denominator. A finding reads as matched against non-blank, so a sparse column is judged on the values it actually holds.
  • One column, several findings. A generic contact column carrying emails on some rows and mobiles on others produces two findings with two counts and two sets of samples, rather than one guess.
  • Headers help, and are used carefully. A column called dob promotes a date to a birth date and a column called full_name promotes two capitalized words to a name. Neither promotion happens on the value alone, because both patterns are far too loose to trust without the header.
  • The findings download is redacted too. It carries the column, the type, the severity, the matched and non-blank counts, the share and the redacted samples. Nothing in it is a real value.
  • Nothing is uploaded. There is no endpoint to upload to. Check the network tab if you want; that is a reasonable thing to do before feeding a tool a file full of card numbers.

Once you know what is in the file, the next step is usually to rewrite it. Send the same CSV to the anonymizer, which hashes, masks, fakes or generalizes column by column and tells you exactly which columns it left alone.

Frequently Asked Questions

What exactly does the scanner look for?

Ten kinds of value. Three are marked high severity: US Social Security numbers, payment card numbers and IBANs. Four are medium: email addresses, phone numbers, dates of birth and person names. Three are low: IPv4 addresses, IPv6 addresses and postal codes. Every type that has a checksum is checked against it, so a card number has to pass Luhn and an IBAN has to pass mod-97 before either is reported.

Why does it not flag my customer id column?

Because a value scan reads values, and C-1001 is not personal data on its face. It becomes personal data the moment somebody joins it to the table that holds the name, which is a fact about your systems and not about this file. The same limit applies to a notes column with a name buried in a sentence, and to quasi-identifiers such as a postal code plus a birth date plus a job title, which together single out one person far more often than people expect. Read the findings as a floor, never as a clean bill of health.

Does a clean report mean the file is safe to share?

No, and the report says so in its own words when nothing is found. Nothing detected means no cell matched one of ten shapes. It does not mean the file carries no personal data, it does not mean the file is anonymous, and it is not a compliance sign-off. Free text, internal keys and combinations of ordinary columns all slip past a value scan by construction. Treat a clean report as one check that passed, then look at the file yourself.

Are the sample values in the report real?

No. Every sample is redacted by the detector that found it, before the string is written anywhere a person could read it. An email becomes a***@***.example, a card becomes four stars four stars four stars then the last four digits, an SSN keeps only its last four, a phone keeps its last two, an IPv4 keeps its first two octets, a birth date keeps only the year. You get enough to recognize the column and nothing you could use to identify anybody. Each finding shows up to three distinct redacted samples.

Why does one column show two findings?

Because a column can hold more than one kind of thing. A contact column where support pasted an email on some rows and a mobile number on others really does carry two types, and reporting only the first would hide half the problem. Each kind gets its own row in the report, with its own match count, its own share of the non-blank cells and its own samples. Columns are sorted by severity first, then by the share of the column that matched, so a column that is entirely email addresses ranks above one with a single stray address in it.

Is this GDPR or HIPAA compliance?

It is not, and nothing on this page is legal advice. A scan tells you what a set of pattern detectors could see in one file. Compliance is about purpose, lawful basis, retention, contracts, access control and the rest of your estate, none of which a browser tab can inspect. Use the report as evidence in a review and as a prompt to fix what it names. Do not use it as a certificate, and do not let a green result end a conversation that should involve your privacy or legal team.

How big a file can it read?

The scan reads every cell, because sampling would let the one leaked column hide in the part that was skipped. That honesty has a cost, so there is a ceiling of twenty million cells. Past it the page stops and tells you the cell count rather than freezing the tab, and suggests splitting the file or scanning a slice of it. A file of a hundred thousand rows and twenty columns is two million cells and finishes in a couple of seconds.

Does the file leave my computer?

No. There is no upload endpoint on this page. The file is read by JavaScript in your tab, matched against the detectors in your tab, and the report is built in your tab. Nothing is kept between visits, so a reload gives you an empty box. That property matters more here than on any other tool on the site, because the whole point of the exercise is a file you are worried about.

Find out what is actually in that export

Free, no account, no upload. Ten detectors, real checksums, redacted samples, and an honest list of what it cannot see.

Back to the PII scanner