# CSV list cleaner

> Deduplicate and validate a CSV of phone numbers and e-mail addresses with the MobileValidate Python SDK: bulk job, automatic pagination and a verdict per row.

Canonical: https://mobilevalidate.com/docs/recipes/csv-list-cleaner · Last updated: 2026-09-25

This recipe cleans a contact list before a CRM import or a transactional send. It reads a CSV, removes duplicates, checks each distinct value once and writes the rows back with a verdict. The full script is [`clean_list.py`](https://github.com/brntech/mobilevalidate-sdk/tree/main/examples/csv-list-cleaner).

## How does it work?

1. It finds a `phone`, `number`, `mobile`, `msisdn` or `e164` column and/or an `email` column.
2. It sends each distinct value once: one lookup for up to 100 values, otherwise a [bulk job](/docs/bulk-jobs).
3. It writes `cleaned.csv` with three new columns: `verdict`, `reachable_on` and `suggestion`.

```python
from mobilevalidate import MobileValidate

mv = MobileValidate()  # reads MOBILEVALIDATE_API_KEY; MobileValidate(sandbox=True) for the test values
job = mv.jobs.create(numbers=numbers, emails=emails, checks=["whatsapp", "email"])
job = mv.jobs.wait(job["id"], wait_timeout=600)
for row in mv.jobs.results(job["id"]):  # pages through all results
    registered = row.get("checks", {}).get("whatsapp.registered", {}).get("registered")  # True / False / None
```

## What verdicts can a row get?

| Verdict | Meaning |
|---|---|
| `ok` | Registered on at least one requested service, or the mailbox exists |
| `not_reachable` | Every requested check answered "not registered" |
| `check_address` | The mailbox was not found |
| `unknown` | No conclusive answer (never billed) |
| `invalid` | Not a valid number or address; `suggestion` says how to fix it |
| `duplicate` | The value already appeared in an earlier row |

## How do I try it?

```bash
pip install -r requirements.txt
python clean_list.py list.csv cleaned.csv
```

The sample `list.csv` contains only [test values](/docs/test-values), so it runs with the public sandbox key. Sandbox jobs are limited to 10 rows; for bigger files, use a [personal test key](/get-test-key) or a live key.

The script prints counts only. It never prints numbers or addresses.

## Frequently asked questions

### Are duplicates billed twice?

No. The script sends each distinct value once and marks repeated rows as duplicate in the output.

### Should I delete contacts with an unknown verdict?

No. Unknown means the check could not give a conclusive answer. It is never billed and says nothing about the contact.
