File upload

POST /v1/files

File upload exists for the lists that will not fit in a JSON body: the million-row export, the CRM dump nobody has opened since 2019. You stream the file up, we stream results back, and your process never holds the whole thing in memory.

Send a CSV and we detect the email column by scanning the first few hundred rows for address-shaped values. If your file has several candidates, name the one you mean with email_column rather than hoping we pick right. A plain TXT file with one address per line needs no configuration at all.

Every other column in your CSV is carried through untouched to the output file, so the verdict lands next to the record it belongs to and you are not left joining on email addresses afterwards.

Shell
curl https://api.zapbounce.com/v1/files \
  -H "Authorization: Bearer zb_live_..." \
  -F "file=@contacts.csv" \
  -F "email_column=work_email" \
  -F "has_header=true"
JSON
{
  "file_id": "fil_3ab991",
  "batch_id": "bat_77c204",
  "rows": 842301,
  "unique": 791004,
  "detected_column": "work_email",
  "encoding": "utf-8",
  "status": "queued"
}

Fields

FieldTypeWhat it carries
filemultipartCSV or TXT. Gzip is accepted and recommended above about 50 MB.
email_columnstringColumn name or zero-based index. Skip it and we detect, which is fine for single-column files.
has_headerbooleanDefaults to true. Get this wrong and your header row becomes an invalid address.
encodingstring (provisional)We sniff UTF-8, Latin-1 and UTF-16. Whether this can be overridden by hand is still open.

A field marked provisional could still be renamed before v1 is declared stable. Every other field is fixed.

A CRM export with three email columns

Say your CRM export has 310,000 rows and three address columns: work_email, personal_email and assistant_email. The file is 140 MB, so you gzip it first. Column detection would have to guess between three candidates here, so you pass email_column=work_email and leave nothing to chance.

Read the response before you walk away. detected_column should echo the name you sent. rows should match your own count of data rows. A figure that's one too high usually means has_header is wrong, and your header is about to be judged as an address. Seeing unique well below rows is normal for CRM data, where one person often sits on several records.

Want the personal addresses checked too? Upload the same file again with email_column=personal_email. Each upload becomes its own batch with its own id, so label the two outputs clearly before you merge anything back.

Five checks on the file before it leaves your machine

Open the file in a text editor and not a spreadsheet. Spreadsheets quietly reformat long numbers and dates on save, and you won't see the change until the output looks wrong.

Search for company names with commas in them, such as Acme, Inc. If that field isn't wrapped in quotes, every column after it shifts one place to the right for that row. We can't repair that, and the verdict will land beside the wrong record.

Count the lines yourself with a tool like wc -l, so you have a number to compare with rows. Look at the encoding value we report, too. If you expected UTF-8 and something else was sniffed, names with accents may come back garbled even though the addresses are fine.

When the output arrives, pick five rows at random and confirm each verdict sits next to the right person. It takes two minutes, and it's the only check that catches a shifted column before your CRM import does.

Questions developers ask

What is the largest file you take?

Stream a million rows and it works. Gzip anything over 50 MB and the upload stops being the slow part.

Do my other columns survive?

Yes. The output file is your file with verdict columns appended, in the original row order.

Is the source file kept?

Only while the batch runs, plus the retention window you set on your account. You can delete it earlier from the dashboard or the API.

Try it against a sandbox key

Scripted verdicts, no SMTP connections, no credits. Live keys come with 100 free checks a month and no card.