Documentation menu

Concepts

Idempotency

How to retry job creation without creating the same job twice.

Why it matters

You send POST /v1/jobs and the connection drops before the response arrives. You do not know whether the job was created. If you send the request again without protection, you may start the same work twice and pay for it twice.

An idempotency key removes the doubt. You attach a unique string to the request. If the API has created a job for that string in the last 24 hours, it returns the first response and creates nothing new. The key is claimed before the job is created, so even two requests that arrive together create one job.

Send a key

Add an Idempotency-Key header to POST /v1/jobs. The value is any string of 1 to 255 characters. A random UUID is a good choice. Generate one key for each job you intend to create, and reuse it for every retry of that request.

curl -X POST "https://api.smolmac.com/v1/jobs" \
  -H "Authorization: Bearer $SMOL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2f0e-8f0b-4c53-9d3a-2b7c1c0f5a11" \
  -d '{"operation":"compress","input":{"upload":"upl_Zk3q8WcT1nRb5LxYp0Ha"}}'

What a replay returns

When the key has been used before with the same body, no job is created. The response is the stored response of the first request: status 202, the same job object, and one extra header.

a replayed response
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
Smol-Idempotent-Replay: true
Smol-Request-Id: req_Hq8Zt2LmW4xNc0VbR7ya
Smol-Version: 2026-10-01

The body is the job as it was when it was created, with status: "queued". It is not the job's current state. Take the id from it and call GET /v1/jobs/{id} to see where the job is now.

A replay does not count against your concurrent jobs, and it works even when the upload it names has already been used and deleted.

A repeat that arrives too early

The key is claimed the moment the first request arrives. If a repeat arrives while that first request is still being handled, there is no response to replay yet. The repeat is refused, and no second job is created.

409 Conflict
HTTP/1.1 409 Conflict
Content-Type: application/json; charset=utf-8
Retry-After: 1
Smol-Request-Id: req_Vn3Kc8TqL0xWb5Yd2RmH

{
  "error": {
    "type": "invalid_request_error",
    "code": "idempotency_in_progress",
    "message": "The first request with this Idempotency-Key is still being handled. Retry in a moment.",
    "param": null,
    "request_id": "req_Vn3Kc8TqL0xWb5Yd2RmH",
    "doc_url": "https://smolmac.com/docs/reference/errors#idempotency_in_progress"
  }
}

Wait for the time in Retry-After, one second, and send the same request again. Once the first request has finished you get its response as a replay. If the first request failed, the key is free again and your repeat is handled as a new request.

The conflict error

If the key has been used before with a different body, the request is refused. This protects you from reusing a key by mistake for a different job.

409 Conflict
{
  "error": {
    "type": "invalid_request_error",
    "code": "idempotency_conflict",
    "message": "This Idempotency-Key was already used with a different request.",
    "param": null,
    "request_id": "req_Hq8Zt2LmW4xNc0VbR7ya",
    "doc_url": "https://smolmac.com/docs/reference/errors#idempotency_conflict"
  }
}

Rules

  • Scope. A key belongs to your account and to a mode. All of the account's live API keys share one set of idempotency keys, and all of its test keys share another.
  • Lifetime. A key is remembered for 24 hours from its first use. Keys are cleared by a clean-up that runs once an hour, so one can be remembered for up to an hour longer. After that it is forgotten, and a request with the same key creates a new job. Do not use a key again for a different job.
  • Only successes are remembered. If the first request failed, for example with 400 or 429, the key is released. You can correct the request and send it with the same key.
  • Simultaneous repeats are safe. The key is claimed before the job is created. Of two requests with the same key and body that arrive together, one creates the job and the other gets 409 idempotency_in_progress. If the first request died without finishing, its claim is given up after 60 seconds and the next request with the key goes ahead.
  • Length. An empty key, or one longer than 255 characters, is refused with 400, bad_request.

Other endpoints

The header is read only by POST /v1/jobs. Other endpoints ignore it.

  • Direct requests store nothing, so there is nothing to duplicate. A retry runs the work again. See Errors and retries.
  • Uploads. Creating an upload twice gives two upload ids. Use either one. Sending the content to the same upload id twice replaces the first content.
  • GET and DELETE. A GET changes nothing. Deleting a job twice returns the same response both times. Deleting a webhook endpoint twice returns 404 the second time.