# CSV Contact Cleaner: Dedupe Emails, Reject Report, Excel-Safe (`xjonume/csv-contact-cleaner`) Actor

Clean contact CSVs: remove duplicate and invalid emails, get a reason for every rejected row, block Excel formula injection. CSV URL, Google Sheets or text.

- **URL**: https://apify.com/xjonume/csv-contact-cleaner.md
- **Developed by:** [Jo NuMe](https://apify.com/xjonume) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $500.00 / 1,000 1,000 input rows processeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## CSV Contact Cleaner: Dedupe Emails, Reject Report, Excel-Safe

Give it a contact list (CSV URL, public Google Sheets link or pasted text). Get back a clean list you can import into your CRM or emailing tool, **plus a file explaining every row that was removed**.

### What it does

- **Removes duplicate emails** (case and spaces ignored, first occurrence kept).
- **Rejects invalid emails and broken rows** (wrong number of columns) with a reason per row: `duplicate_email`, `invalid_email`, `malformed_row`.
- **Neutralises spreadsheet formula injection** (CSV injection): cells starting with `=`, `+`, `-`, `@` get a leading apostrophe, so opening the file in Excel or Google Sheets never runs a formula. Real phone numbers like `+33 6 12 34 56 78` are left untouched.
- **Keeps all your columns** (city, company, tags...) and accounts for every input row: kept + duplicates + invalid = input.
- Detects comma / semicolon / tab separators and UTF-8 or Windows-1252 encoding (French Excel exports).
- Recognises `email`, `e-mail`, `mail`, `courriel` headers, or any column name you set.
- Gives **SHA-256 fingerprints** of the input and outputs, so you can prove which file was processed.

It does **not** contact mail servers (no SMTP/MX check) and does not store your data after the run beyond Apify's normal storage retention.

### Input

| Field | Description |
|---|---|
| `csvUrl` | Public CSV URL or Google Sheets link (max 20 MB) |
| `csvText` | Or paste the CSV directly |
| `emailColumn` | Optional, if your email column has another name |
| `requireFullFile` | Optional. Stop (nothing charged for rows) instead of cleaning only the first rows when your max cost per run is too low for the whole file |

### Output

- **Dataset**: the cleaned rows (export as CSV, Excel, JSON from the Output tab).
- **Key-value store**:
  - `CLEANED_CSV`: cleaned file, same separator as the input;
  - `REJECTS_CSV`: rejected rows with source row number and reason;
  - `OUTPUT`: JSON report (counts, reasons, detected separator/encoding, SHA-256).

Example report:

```json
{"summary": {"input_rows": 5, "kept_rows": 2, "duplicate_rows": 1, "invalid_rows": 2, "escaped_cells": 1},
 "reject_reasons": {"duplicate_email": 1, "invalid_email": 1, "malformed_row": 1}}
```

### Pricing

Pay per event: **$0.50 per started block of 1,000 input rows** (a 250-row file costs $0.50, a 10,000-row file $5.00), plus Apify's tiny run-start fee. A file with a header but no data rows costs only the run start.

The Actor checks your **maximum cost per run** (Run options) **before** charging. If it is too low for the whole file, the first rows that fit are cleaned and only those are charged; the status message and the `unprocessed_rows` field of `OUTPUT` say how many rows were left out (duplicates are then checked within the cleaned rows). Tick `requireFullFile` to stop instead: nothing is charged for rows and the message tells you how many blocks the file needs.

***

### En français

Nettoie une liste de contacts CSV : supprime les emails en double, rejette les emails invalides et les lignes cassées **en expliquant chaque rejet**, neutralise les formules dangereuses pour Excel (injection CSV) sans abîmer les numéros `+33`, conserve toutes vos colonnes et fournit des empreintes SHA-256. Séparateur `,` `;` ou tabulation, encodage UTF-8 ou Windows-1252 détectés automatiquement. Tarif : 0,50 $ par tranche entamée de 1 000 lignes. Si votre coût maximum par exécution ne couvre pas tout le fichier, les premières lignes qui tiennent dans ce plafond sont nettoyées et seules celles-ci sont facturées ; cochez `requireFullFile` pour arrêter sans rien facturer.

# Actor input Schema

## `csvUrl` (type: `string`):

Public URL of a CSV file (UTF-8 or Windows-1252, comma / semicolon / tab separated) or a public Google Sheets link. Max 20 MB. Leave empty if you paste the CSV below.

## `csvText` (type: `string`):

Paste the CSV content here instead of giving a URL. The first line must be a header with an email column (email, e-mail, mail or courriel).

## `emailColumn` (type: `string`):

Only needed if your email column has another name than email / e-mail / mail / courriel.

## `requireFullFile` (type: `boolean`):

By default, when your maximum cost per run is too low for the whole file, the first rows that fit are cleaned and charged, and the rest is reported as not processed. Tick this to stop instead, without any charge for rows.

## Actor input object example

```json
{
  "csvText": "email,name,phone\nalice@example.com,Alice Demo,+33 1 23 45 67 89\nALICE@example.com,Alice Duplicate,\nnot-an-email,Bob Demo,0102030405\ncarol@example.org,=HYPERLINK(\"http://evil.example\"),\n",
  "requireFullFile": false
}
```

# Actor output Schema

## `cleanedRows` (type: `string`):

No description

## `cleanedCsv` (type: `string`):

No description

## `rejectsCsv` (type: `string`):

No description

## `report` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "csvText": `email,name,phone
alice@example.com,Alice Demo,+33 1 23 45 67 89
ALICE@example.com,Alice Duplicate,
not-an-email,Bob Demo,0102030405
carol@example.org,=HYPERLINK("http://evil.example"),`
};

// Run the Actor and wait for it to finish
const run = await client.actor("xjonume/csv-contact-cleaner").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "csvText": """email,name,phone
alice@example.com,Alice Demo,+33 1 23 45 67 89
ALICE@example.com,Alice Duplicate,
not-an-email,Bob Demo,0102030405
carol@example.org,=HYPERLINK(\"http://evil.example\"),
""" }

# Run the Actor and wait for it to finish
run = client.actor("xjonume/csv-contact-cleaner").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "csvText": "email,name,phone\\nalice@example.com,Alice Demo,+33 1 23 45 67 89\\nALICE@example.com,Alice Duplicate,\\nnot-an-email,Bob Demo,0102030405\\ncarol@example.org,=HYPERLINK(\\"http://evil.example\\"),\\n"
}' |
apify call xjonume/csv-contact-cleaner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,xjonume/csv-contact-cleaner"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hchnzic0jmTvKs4VD/builds/qG2G4aufaWwAsviE3/openapi.json
