PlunkPlunk
Guides

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>.

A minimal file:

email
ada@example.com
grace@example.com
linus@example.com

A 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:00Z

In 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, subscribed, createdAt, updatedAt, and the auto-generated URL variables (unsubscribeUrl, etc.) are silently filtered out. Don't include them as columns.
  • Date columns: use ISO 8601 (2026-05-06T12:00:00Z) so they're typed as dates and become usable with within / olderThan segment operators.
  • 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

  1. Open Contacts and click Import.
  2. Pick your CSV file. Plunk validates the header and shows a preview of the first few rows.
  3. Confirm. The import is queued and runs in the background.
  4. 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://next-api.useplunk.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://next-api.useplunk.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

  • Unsubscribed contacts: imported contacts are created subscribed by default. If your CSV represents users who never opted in, set up a workflow that filters out anyone you shouldn't email — or import them and then bulk-unsubscribe them with POST /contacts/bulk-unsubscribe to keep them on file but unmailed.
  • 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.
  • Custom fields — how the data.<column> fields you import are typed and used.
  • Bulk operationsbulk-subscribe, bulk-unsubscribe, bulk-delete for mass actions on existing contacts (max 1,000 per call).