# Importing Contacts CSV — Best Practices and the 6-Click Field Mapping Wizard

> Format the CSV right before you upload — column headers in row 1, UTF-8 encoding, one email per row. AcelleMail's import wizard handles the rest in 6 clicks. Visual walkthrough of the wizard plus error-recovery patterns + bulk-update strategies in the collapsible section.

- Canonical URL: https://acellesend.com/kb/articles/importing-contacts-csv-best-practices-and-field-mapping
- Author: Ryan Walsh, Audience & Analytics Editor
- Category: List Management — Segments, tags, import/export, double opt-in, verification, hygiene
- Content type: tutorial · Difficulty: beginner
- Tags: Acellemail
- Published: 2026-04-21 · Last updated: 2026-08-15
- Reading time: 10 min
- Publisher: AcelleMail Knowledge Base, https://acellesend.com/kb

---

## Format the CSV right (before you upload)

The single biggest cause of "import failed for 1,247 rows" is malformed CSV. Get these right and the import flows smoothly:

| Requirement | What it means |
|---|---|
| **UTF-8 encoding** | Save the file as UTF-8 — Excel defaults to "CSV (Comma delimited)" which is often Windows-1252 and breaks accented characters. In Excel: Save As → CSV UTF-8. In Numbers: Export → CSV with default encoding. |
| **Column headers in row 1** | The first row is field names: `email,first_name,last_name,city`. Lowercase, snake_case, no spaces. |
| **One email per row** | Don't pack `john@example.com, jane@example.com` into a single cell. One row per subscriber. |
| **Quoted strings if they contain commas** | A `city` value of `Berlin, DE` must be quoted: `"Berlin, DE"`. Most spreadsheet tools do this automatically; hand-rolled CSV files often forget. |
| **Email column required** | Every row must have a non-empty email. AcelleMail rejects rows with blank email. |
| **No extra whitespace** | Leading/trailing spaces in emails or field values are stripped, but `john@example.com ` (with trailing space) might cause confusion in audit logs. Trim before export. |
| **Date format consistent** | If you have a `signup_date` column, use ISO 8601 (`2026-05-19`) — most reliable. Mixed `5/19/2026` + `19/05/2026` will trip the parser. |
## The 6-click import flow (same for every source)

Once you have the CSV ready, AcelleMail's import wizard is the same regardless of where the file came from.

### 1. Open the destination list

In the left sidebar, click **Audience** → choose the list that will receive these subscribers (or create one — *New list* button top-right).

![Lists index](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/01-lists-index.png)

The list detail page shows current subscriber counts and a per-list toolbar:

![List overview](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/02-list-overview.png)

### 2. Click "Import" in the list toolbar

The wizard entrypoint is right there on the list detail page:

![Import entry point](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/03-import-entry.png)

### 3. Upload your CSV

Drop the file into the upload area:

![Upload empty state](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/04-upload-empty.png)

AcelleMail parses the file and confirms detection — rows-counted + sample preview:

![Upload success](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/05-upload-success.png)

### 4. Map the columns

The wizard auto-detects standard columns (`email`, `first_name`, `last_name`) and shows green *Mapped to EMAIL* chips. For non-standard columns, pick the destination from the dropdown:

![Map columns](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/06-map-columns.png)

### 5. Pick duplicate handling

On the same screen, choose what AcelleMail does when a row's email already exists in this list:

- **Skip** — keep the existing subscriber, don't overwrite anything
- **Update** — overwrite name / tags / custom fields with values from this CSV
- **Unsubscribe** — mark the existing row as unsubscribed (rare; for re-importing an opted-out list)

### 6. Run the import

Click **Start import**. The job runs in the background — you can close the popup, work elsewhere, return to **Audience → [list] → Import** to see progress:

![Import history](https://acellesend.com/plugins/acelle/landing/images/help/kb/import-csv/07-import-entry-revisit.png)

Pending → Running → Complete, with rows-imported / rows-skipped / errors counts per job.
## What happens after Start import

The job runs in the background. For a 10k-row file, expect 30-90 seconds. For 100k rows, 5-15 minutes. AcelleMail processes in batches of ~500 with database transactions per batch — safe to close the browser tab; the job continues server-side.

The **Import history** view in the list shows progress per batch:

- **Pending** — job queued, not yet processing
- **Running** — worker actively reading the CSV
- **Complete** — done; click to see the result summary

Result summary breaks down:

- **Rows imported** — net successful new subscribers + updates
- **Rows skipped** — duplicates handled per your chosen rule
- **Rows with errors** — bad emails, invalid date formats, FK violations — with a downloadable error CSV showing exactly which rows failed and why

Download the error CSV, fix the rows in your spreadsheet, re-import just those rows.

## Common CSV import errors and fixes

| Error in error.csv | What it means | Fix |
|---|---|---|
| `Invalid email format` | Row has missing `@`, malformed domain, or trailing whitespace | Validate email syntax in spreadsheet before upload (Excel: use regex) |
| `Email already exists` (with `Skip` mode) | Subscriber on this list already; per skip-rule, not overwritten | Expected; not an error — just informational |
| `Email already exists` (with `Update` mode) | Should be auto-updating; if it's an error here, the import ran in Skip mode | Re-run with duplicate handling = Update |
| `Field 'birthday' has invalid format` | Mixed date formats in CSV | Normalize all dates to ISO 8601 before upload |
| `Field 'lead_score' must be numeric` | A non-number value in a Number-type field | Check CSV — strip non-numeric values or change the field type |
| `Required field 'email' is empty` | Row has no email | Remove the row or fix the value |
| `Charset error: invalid UTF-8 sequence at row 4523` | File saved as Windows-1252 or other encoding | Re-export as UTF-8 |
| `Cannot map column 'birth_date' to field 'birthday'` | CSV header name differs from field name; not auto-detected | Use the field mapping step to manually map |

## Common UI patterns

### Duplicate handling cheat sheet

| Choose | When to use |
|---|---|
| **Skip** | Importing a fresh batch of leads where existing subscribers shouldn't have their names/tags overwritten |
| **Update** | Re-importing the same list from a master CRM where the CRM is the source of truth |
| **Unsubscribe** | Importing a bounced-list export to mark addresses inactive (rare; usually you'd just delete them) |

### When to import vs. when to use the API

| Scenario | Use |
|---|---|
| One-time migration from another platform | CSV import (fastest, easiest) |
| Daily sync from a CRM-of-record | API + scheduled cron (idempotent, automatic) |
| New batch of leads (every week or month) | CSV import works fine, OR set up CSV-upload-to-folder integration via your storage tool |
| Real-time subscribe (signup form) | AcelleMail's built-in signup forms OR your custom form posting to AcelleMail's API |

### Pre-import sanity check

Open the CSV in your spreadsheet tool. Five quick checks before uploading:

1. Row count matches expectations (a leading blank row will offset everything)
2. Column headers are present and snake_case (`email`, not `Email Address`)
3. Email column has no blanks (sort by email; blanks float to top)
4. No duplicate emails within the CSV itself (de-dupe via spreadsheet first; AcelleMail will skip dupes but it's cleaner to remove them upfront)
5. Try a 10-row test import first if the CSV is large — verify field mapping works, then re-run with the full file

## Common UI signals + fixes

| Symptom | Likely cause | UI fix |
|---|---|---|
| Import stuck on "Pending" for >10 minutes | Queue worker isn't running | Operator: check that `queue:work` is running (see [setup queue workers](https://acellesend.com/kb/articles/setting-up-queue-workers-and-cron-jobs)) |
| Import shows "Complete" but list count didn't increase | All rows skipped due to duplicates | Check duplicate handling — should be Update if you want overwrite |
| Custom field column not detected during mapping | Column header doesn't match field tag exactly | Either rename header in CSV, OR manually map in step 4 |
| Wizard rejects the file at upload step | Wrong file type (xlsx not csv), or too large (>50MB default) | Export as plain CSV from your spreadsheet tool; for >50MB, split into multiple files |
| Some subscribers imported, others not | Mixed error types in single file (some invalid emails, some duplicates) | Download error CSV → fix → re-import; the successful rows aren't re-counted |
| Tags column joins as single string | Tag values need to be comma-separated within the cell: `tag1,tag2,tag3` | Re-format the tags column in your spreadsheet |

<details>
<summary><strong>Advanced: bulk-update strategies, API-driven sync, audit + recovery</strong></summary>

For large lists or ongoing-sync scenarios, the CSV-through-UI path is one tool among several.

**Bulk update via re-import:**

If you need to update a single field across thousands of subscribers (e.g. reset everyone's `frequency` to `weekly`), the easiest path:

1. Export the list to CSV (Audience → list → Export → All fields)
2. Open in spreadsheet, edit the column for the values you want
3. Re-import with duplicate handling = **Update**

AcelleMail processes the file in 5-30 minutes depending on size. The advantage over API is no scripting required; the downside is no per-row tracking of which value changed.

**Scripted update via API** (when you need fine control):

```bash
ACELLE_TOKEN="..."

# Set everyone's 'frequency' to 'weekly' in one list:
curl -X POST "https://acellemail.com/api/v1/lists/{list_uid}/subscribers/bulk-update" \
  -H "Authorization: Bearer $ACELLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {"status": "subscribed"},
    "updates": {"fields": {"frequency": "weekly"}}
  }'
```

Returns `{updated_count, failed_count}` synchronously. Handle 429 with backoff for large updates.

**Idempotent CSV import pipeline:**

When syncing from a CRM nightly, the standard pattern is:

```bash
# 1. Export CRM contacts to CSV
crm-export --tag=marketing --output=/tmp/contacts.csv

# 2. Pre-validate (optional but recommended)
csvkit validate /tmp/contacts.csv

# 3. Upload to AcelleMail
curl -X POST "https://acellemail.com/api/v1/lists/{list_uid}/imports" \
  -H "Authorization: Bearer $ACELLE_TOKEN" \
  -F "file=@/tmp/contacts.csv" \
  -F "duplicate_handling=update" \
  -F "field_mapping={\"email\":\"email\",\"first_name\":\"first_name\"}"

# 4. Poll for completion + result
import_uid="..."  # from response
curl -H "Authorization: Bearer $ACELLE_TOKEN" \
  "https://acellemail.com/api/v1/imports/$import_uid"
```

The full audit trail (which rows were updated, which were skipped, which failed) is in the response JSON.

**Resumable imports for very large files:**

For files >100k rows, the wizard can run for 15+ minutes. If the queue worker is restarted mid-import, AcelleMail's import job is designed to resume from the last completed batch — no rows are double-processed. The audit trail shows the exact row offset where the resume happened.

**Pre-import deduplication:**

If your CSV has internal duplicates (same email twice within the file), AcelleMail processes the rows in order and the LAST row's values win for the duplicate. To control which row wins, sort the CSV in your spreadsheet first — most recent record last.

For server-side dedup, use the API in a loop with `upsert: true` and `last_updated_at` field comparison.

**Recovery from a bad import:**

If you imported with wrong field mapping (e.g. mapped `first_name` to `email` and now have garbage), the recovery path:

1. **Don't panic** — the original data is still there in your spreadsheet
2. AcelleMail keeps per-import audit logs (admin → Audit log → filter by import event)
3. Bulk-update the affected field back to correct values via API or re-import

There's no automatic "undo last import" — recovery is always forward-looking. Always test with a 10-row sample first on a non-critical list before running a 100k import.

**Field type changes:**

If you've imported 100k subscribers with `birthday` as a Text field, then realize you want it as a Date field — the change is in two steps:

1. Audience → list → Fields → edit `birthday` → change type Text → Date. AcelleMail re-validates all existing values; rows where the value doesn't parse remain as-is until you fix them.
2. Either bulk-update the bad rows via API, or export → fix in spreadsheet → re-import.

**Custom-field discovery during import:**

If your CSV has a column AcelleMail doesn't know about (e.g. `lifetime_value` and the list doesn't have that field yet), the field-mapping step in the wizard offers **Create new field on import**. Pick the field type (Number / Text / Date) → AcelleMail creates the field on the list + maps the column to it in one click. Useful for one-time migrations from systems with custom schemas.

**Compliance audit trail:**

Every import is logged with:

- Who triggered it (admin user ID)
- When (timestamp)
- Which file (filename + hash)
- How many rows added / updated / skipped / errored
- The full error CSV is retained 90 days (configurable in admin settings)

For GDPR audit requests ("show me when you obtained this subscriber's consent"), the import audit log answers it — the import row references the original CSV which presumably has the source/opt-in date column.

</details>

## Related articles

- [Email list hygiene — clean your list for better deliverability](https://acellesend.com/kb/articles/email-list-hygiene-clean-your-list-for-better-deliverability)
- [Managing subscriber preferences and frequency](https://acellesend.com/kb/articles/managing-subscriber-preferences-and-frequency)
- [Migration from Mailchimp to AcelleMail](https://acellesend.com/kb/articles/migrating-from-mailchimp-to-acellemail)
- [Double opt-in vs single opt-in — which to choose](https://acellesend.com/kb/articles/double-opt-in-vs-single-opt-in-which-to-choose)

---

## Related articles

- [List Hygiene in AcelleMail: Verify, Filter, Blacklist](https://acellesend.com/kb/articles/email-list-hygiene-clean-your-list-for-better-deliverability): AcelleMail gives you three concrete tools to keep a list clean — per-list email verification, the Subscribers status filter, and a customer-managed Blacklist. This walkthrough shows when to use each and what good hygiene looks like in production.
- [Subscriber Preferences & Frequency — A Preference Center That Reduces Unsubscribes](https://acellesend.com/kb/articles/managing-subscriber-preferences-and-frequency): Offer a preference center — "Hey, instead of unsubscribing, want fewer emails per week, or only the product updates?" — and unsubscribe rates drop materially. AcelleMail's built-in Update form covers this in 3 clicks. Visual walkthrough plus per-topic-list patterns in the collapsible.
- [Segments: Slice Your List Into Audiences That Convert](https://acellesend.com/kb/articles/advanced-segmentation-strategies): Build dynamic subscriber groups in AcelleMail using list fields, tags, activities, and email-verification results. Walk-through with real screenshots of the segment builder.
- [Build a Signup Form and Embed It on Your Site](https://acellesend.com/kb/articles/creating-effective-signup-forms-and-landing-pages): AcelleMail has a drag-and-drop visual builder for signup forms with 4 popup layouts and 5 display triggers (immediate, delay, exit intent, on click, first visit). Walkthrough with real screenshots of every panel.
- [Double Opt-In in AcelleMail: Toggle, Template, Decision Guide](https://acellesend.com/kb/articles/double-opt-in-vs-single-opt-in-which-to-choose): AcelleMail has one Double opt-in toggle per list. This walkthrough shows exactly where the toggle lives, what the confirmation email looks like, and a buyer-style decision guide for when to turn it on.
