Importing contacts from CSV
Bulk-load contacts and their custom fields from a CSV file
Plunk supports bulk-importing contacts from a CSV file — useful for migrating from another platform, loading an initial list, or syncing a large batch of contacts that you can't reasonably stream through /v1/track.
CSV format
The CSV must have a header row. The only required column is email. Every other column becomes a custom field on the contact under data.<columnname>.
Headers are lowercased on import, so a firstName column becomes the field firstname. Use lowercase names in your templates and segment filters, like {{firstname}}, or name the columns in lowercase to begin with.
A minimal file:
email
ada@example.com
grace@example.com
linus@example.comA richer file with custom fields:
email,firstname,plan,signupdate
ada@example.com,Ada,pro,2026-01-15T00:00:00Z
grace@example.com,Grace,enterprise,2025-11-02T00:00:00Z
linus@example.com,Linus,free,2026-04-21T00:00:00ZIn this example, every imported contact ends up with data.firstname, data.plan, and data.signupdate set.
Rules and limits
- File size: up to 5 MB per upload. For larger lists, split the file and run multiple imports.
- Encoding: UTF-8. Non-UTF-8 files may produce garbled custom field values.
- Email column: must be present and valid. Rows with missing or invalid emails are reported back as errors.
- Reserved column names:
id,createdAt,updatedAt, and the auto-generated URL variables (unsubscribeUrl, etc.) are silently filtered out. Don't include them as columns. - Subscription status: an optional
subscribedcolumn sets whether each contact can receive marketing email.true,1andyessubscribe the contact; any other value, such asfalseorno, unsubscribes it. Leave the cell empty, or omit the column, to keep an existing contact's status unchanged. New contacts without a value are created subscribed. - Date columns: use ISO 8601 (
2026-05-06T12:00:00Z) so they're typed as dates and become usable withwithin/olderThansegment operators. - Boolean columns:
true,false,yes,no(case-insensitive) are stored as booleans and get the boolean toggle in segment filters. - Numeric columns: plain integers and decimals (
42,3.14) are stored as numbers and become usable withgt/ltsegment operators. Leading-zero values (01234),+-prefixed numbers, and scientific notation stay strings so IDs, zip codes, and phone numbers aren't corrupted. - Existing contacts: if a row's email matches an existing contact, the import updates the contact (merging the CSV's columns into
data). It doesn't create a duplicate or overwrite the whole record.
Importing your CSV
- Open Contacts and click Import.
- Pick your CSV file. Plunk validates the header and shows a preview of the first few rows.
- Confirm. The import is queued and runs in the background.
- The Imports page shows progress and any per-row errors when complete.
For automation, use the import API:
Step 1 — upload the CSV
curl -X POST https://send-api.akmarketingseo.com/contacts/import \
-H "Authorization: Bearer sk_..." \
-F "file=@contacts.csv"The response includes a jobId:
{ "jobId": "imp_abc123", "status": "queued" }Step 2 — poll for status
curl https://send-api.akmarketingseo.com/contacts/import/imp_abc123 \
-H "Authorization: Bearer sk_..."Poll every few seconds until status is completed or failed. The response includes counts:
{
"jobId": "imp_abc123",
"status": "completed",
"totalRows": 12000,
"imported": 11985,
"updated": 8,
"skipped": 0,
"errors": [
{ "row": 47, "email": "bad@", "reason": "invalid_email" },
{ "row": 1042, "email": "@example", "reason": "invalid_email" }
]
}imported counts new contacts created. updated counts existing contacts whose data was patched. errors lists per-row issues with row number and reason so you can fix and re-upload.
Tips
- Migrating unsubscribes: when moving from another platform, export unsubscribed, bounced and complained contacts too, and import them with
subscribedset tofalse. They stay on file for their history but are never sent marketing email. - Custom fields appear in segments immediately: as soon as the import finishes, you can build dynamic segments on any of the imported columns.
- Idempotent reruns: re-running the same CSV updates contacts in place rather than creating duplicates. If you fix a column and re-upload, all matching rows get the corrected value.
Related
- Custom fields — how the
data.<column>fields you import are typed and used. - Bulk operations —
bulk-subscribe,bulk-unsubscribe,bulk-deletefor mass actions on existing contacts (max 1,000 per call).