POST runs up to 100 leads through the same intake logic as POST /leads and always answers 200, with a per-row outcome inside the body. So a row that fails while it’s being processed doesn’t cost you the rest of the call.
Where
Settings→API & Integrations
Your role needs
Update access to Lead intake API
update-companies. Admin have it by default.Authenticated by a workspace API key, not by a signed-in session. Creating a key needs the same permission as the rest of the API settings tab.
Status codes
There’s no top-level
409 for a duplicate lead here, unlike POST /leads. Under on_duplicate: "error", a duplicate inside a batch shows up as a failed row in the 200 response instead of rejecting the whole call. See Response envelope below.Request body
Every other field a single lead accepts (
job_title, company, tags, custom_fields, and the rest) works the same way inside each entry of leads[]. See the lead fields reference for the full list, and POST /leads for what each option switch does and its default.
A switch applies to the whole call, so its effect repeats on every row: enrich is on unless you turn it off, and each new company a batch creates from a domain queues its own website analysis. Send enrich: false on a bulk import if you only want the leads stored.
Every row runs on its own
Each row runs inside its own savepoint, because the whole request already runs inside one transaction. A row that trips a database constraint rolls back only that row, never the ones before or after it.dry_run: true previews every row the same way POST /leads does: each one runs for real inside a savepoint that’s then rolled back, so nothing in the batch is saved.
Shape is checked in one pass over the whole array, not row by row as each is processed. name, the contact-detail requirement, and every other field’s format are validated against the entire leads array before the first row runs. So one row with no name or no way to reach the person fails the whole call with a 422, and none of the batch runs at all. Fix that row (or remove it) and resend the whole batch. Once shape passes, a problem the service only finds while processing a row (an unknown tag_id, an unknown tag name when you sent create_missing_tags: false, a duplicate under on_duplicate: "error") is captured as a failed row instead, and the rest of the batch keeps going.
Rows are processed in order, and on a real call an earlier row is already visible to the ones after it: send the same person twice in one batch and the second row matches the first, so on the default on_duplicate: "update" you get a created followed by an updated rather than two leads. A dry run is the exception: each preview is rolled back before the next row starts, so two copies of the same person both come back created.
A created row fires the new-lead trigger, exactly as a single POST /leads would. An updated row doesn’t. But tags are still applied on an update, and a tag the lead didn’t already carry starts a trigger of its own. A skipped row attaches no tag and fires nothing. None of this spends credits: a lead you hand over is stored as non-chargeable. See Automations on POST /leads for the trigger names.
Response envelope
The body is always{ data, meta }. data is an array with one entry per row, in the same order you sent them:
A duplicate rejected under
on_duplicate: "error" is the one failed row that still carries a lead_id, the id of the lead it matched:
One row inside a 200 batch response
field at all:
An unhandled row failure
meta counts the whole call:
meta for a batch of 5, one duplicate
Idempotency and rate limits
AnIdempotency-Key header works exactly as it does on POST /leads. Send one and a retried call with the same body returns the stored response instead of running again. Validation runs before that replay check, though: a malformed batch fails with 422 without ever touching the idempotency cache, so fixing the payload and resending under the same key still runs as a fresh attempt.
However many leads it carries, one batch call counts as a single request against your rate limit, so a hundred leads sent this way cost one request instead of a hundred. Full mechanics, including the exact limit and the idempotency replay window, are on Idempotency, deduplication and rate limits.
Related
POST /leads
The single-lead version of this endpoint: request body, duplicate handling and dry run.
Idempotency, deduplication and rate limits
Retry safely, and see what a batch call costs against your rate limit.
Errors reference
Every status the API returns and what to change before retrying.