Start a batch
One call, one domain, up to 100 briefs. Rows are accepted or refused individually rather than the whole call failing, so a batch that runs past a limit still starts everything there was room for.
POST
/v1/editions/{slug}/articles/batchParameters
| Param | In | Notes |
|---|---|---|
| slug | path | The domain UUID. Every row in a batch is written for this one domain. |
| jobs | body | Required. 1 to 100 rows, each shaped exactly like the single-article body. Two rows may not share an id. |
Request
curl -X POST https://api.mastheads.app/v1/editions/3f9c1e84-2b7a-4d6e-9c15-8a0d7e6b4f21/articles/batch \
-H "Authorization: Bearer mh_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"jobs": [{"input": "Harbour timetable"}, {"input": "Bus lane consultation"}]}'Response
202 Accepted
{
"accepted": [
"7c2b9f10-4a5d-4e88-9b31-6f0c2a1d5e73",
"b8e0f3a6-91c4-4d20-8f7e-33ac1b5d9e04"
],
"refused": [
{
"index": 2,
"job_id": null,
"detail": "not started - this key's hourly limit of 20 API actions leaves room for 2"
}
],
"ids": [
"7c2b9f10-4a5d-4e88-9b31-6f0c2a1d5e73",
"b8e0f3a6-91c4-4d20-8f7e-33ac1b5d9e04"
]
}The call is 202 even when some rows are refused: nothing is silently dropped, so read refused rather than only checking the status code. A batch is cut at whichever limit bites first - this key's hourly room, the newsroom's own ceiling, or the plan's allowance.
| Field | Type | Notes |
|---|---|---|
| accepted | array | The job ids that were started. Poll each on GET /v1/jobs/{id}. |
| refused | array | One object per row that did not start, each with its input index, its job_id when you sent one, and a detail sentence saying why. |
| ids | array | Every row that was handed on, in the order you sent them. It is how a caller who sent no ids of their own matches a refusal back to a row. |
Errors
| Status | Code | When |
|---|---|---|
| 401 | invalid_key | Missing, invalid, or revoked API key. |
| 401 | account_closed | The account this key belongs to has been closed. |
| 402 | plan_required | The account's current plan does not include the content API. This is checked on the request, not only when the key was made. |
| 503 | unavailable | The plan or the data store could not be read. It refuses rather than guessing. |
| 402 | payment_required | A guard behind the door refused on plan or billing grounds. The message says which. |
| 403 | key_read_only | The key was created as Read only. A key's permission is fixed when it is made, so create a new key with Full access. |
| 403 | wrong_newsroom | A full-access key spends only in the newsroom its owner was in when it was created. If that person now acts in a different newsroom, create a new key there. A read-only key is not gated this way. |
| 403 | forbidden | A guard behind the door refused. The message says which. |
| 404 | not_found | No such domain on your account, or the path segment is not a UUID. Another tenant's domain is a 404, never a 403. |
| 422 | invalid_params | The body did not validate: a missing or over-long input, an id that is not a UUID, more than 100 rows, or two rows sharing an id. |
| 429 | rate_limited | Over this key's 20 paid actions an hour, or over its 120 requests/minute. Retry-After: 60, and nothing was started. |
Every request needs a key - Authentication. The error shape and the full status table are on Rate limits & errors.