Batch operations
Four batch endpoints let you move many records in a single call:
| Endpoint | Does |
|---|---|
POST …/records/batch/create/ | Create many |
POST …/records/batch/update/ | Update many |
POST …/records/batch/upsert/?matching_property=X | Upsert many, matched on X |
POST …/records/batch/delete/ | Delete many |
Batches also count as one request against your rate limit, which is the main reason to prefer them.
207 Multi-Status
Section titled “207 Multi-Status”Batches don’t succeed or fail as a unit. They return 207 with a per-item result, so a single bad record doesn’t discard the rest of the work:
{ "data": [ { "status": 201, "data": { "id": "rec_01J8Z…" } }, { "status": 422, "error": { "code": "request_invalid", "reason": "schema_invalid", "field": "/records/1/properties/ahoy_email", "detail": "Not a valid email address." }} ]}Results come back in the order you sent them, so index n in the response
corresponds to index n in your request.
Limits
Section titled “Limits”- An empty batch returns
request_invalidwithreason: "empty_batch". - Too many items returns
reason: "batch_too_large". Chunk and send again.
Replays are safe
Section titled “Replays are safe”A write whose values already match stored state is a success, not a conflict. Ahoy treats a no-change write as idempotent and returns the current record.
That means a retried batch after a network timeout won’t fail on the records
that already landed, and a batch containing the same record twice won’t error.
Combined with PUT upserts, this makes at-least-once delivery from your side
safe to implement.
Upserts need a good matching property
Section titled “Upserts need a good matching property”matching_property must be a readable property, present and non-null in every
item. If it matches two or more existing records the item returns
conflict with reason: "ambiguous_match", since
there’s no single record to update.
Prefer a property you know is unique — an external id from your own system is usually the right choice.