Completion jobs
A completion job runs on the server after you close the connection, so you submit it once and poll for the result. Use a job for a plain text completion that must keep running after your connection drops, and a synchronous /v1/completions request for streaming, logprobs, or activations.
Run the example script
The script needs Python 3.11 or later and httpx. Download the example script, set ACS_API_KEY, and run it. The script calls the production API unless ACS_API_BASE names another deployment. Submit and poll a completion job lists its steps.
python python_completion_jobs.py \
--model llama-8b --prompt 'Hello' --max-tokens 128 \
--idempotency-key my-experiment-item-001 --deadline 900
To resume after a lost connection, run the script again with the same idempotency key, API key, and request parameters. The --deadline limit stops the polling but does not cancel the job on the server.
Submit a job
Send POST /v1/completion-jobs with your bearer key, an Idempotency-Key header, and a completion request as JSON:
{"model":"llama-8b","prompt":"Hello","max_tokens":128}
An idempotency key is 1 to 128 visible ASCII characters long, and each API key has its own set of idempotency keys.
| You send | The server returns |
|---|---|
| A new idempotency key | 202 with the job's id, status_url, and result_url |
| The same key and the same request | 200 with the existing job |
| The same key and a different request | 409 |
Only the submitting API key can read or cancel a job, so another key on the same account gets 404 job_not_found.
Poll for the result
GET /v1/completion-jobs/{id} returns 200 with the job's metadata, in this format:
{
"id": "...",
"request_id": "...",
"status": "...",
"status_url": "...",
"result_url": "...",
"usage_pending": ...,
"expires_at": ...,
"result_expired": ...,
...
}
A 200 status from this endpoint does not mean that the completion succeeded.
GET /v1/completion-jobs/{id}/result returns 202 with Retry-After: 1 while the job waits in the queue or runs. Once the job ends, the endpoint returns the stored body with the status that /v1/completions would have given. A finished job can return these statuses, among others:
| Outcome | Status |
|---|---|
| The completion succeeded | 200 |
| A token budget refused the request when it ran | 429 |
| The upstream model failed | 502, or 400 when the failure was in the request |
| The worker lost the job, or the model server was cold, saturated, or behind an open circuit breaker | 503 |
| The request ran past the 14-minute window | 504 |
| Someone paused or revoked the key after you submitted the job | 403 |
| The server failed while running the job | 500 with job_failed |
| You cancelled the job | 409 |
| The job waited in the queue for more than 24 hours | 408 with job_queue_expired |
| The stored content has expired | 410 |
Check the HTTP status of the result before you read choices.
You can still fetch a result after you use up your token budget, but not with a paused or revoked key.
Cancel a job
POST /v1/completion-jobs/{id}/cancel cancels a queued job at once. A running job gets cancel_requested and ends as cancelled unless the completion had already finished.
Reference
A job needs an explicit model and max_tokens. It takes non-streaming completion JSON, including batches of prompts, n, seed, and echo.
| Limit | Value |
|---|---|
Requested output, max_tokens × n × number of prompts |
4096 tokens |
| Request body | 64 KiB |
| Stored result | 2 MiB. The server fails a job with a larger result and does not cut it down. |
| Queued and running jobs | 8 per key, 32 per account, 128 in total |
| Stored jobs, including tombstones | 100 per account, 1000 in total |
We run at most four jobs at once across all users, so eight jobs from one key do not all run in parallel.
The submit endpoint refuses a job with these errors:
429 job_capacitywhen the new job would exceed a job cap.400 unsupported_job_modewhen the body includesstream: true,stream_options, an activation or steering field,prompt_logprobs, orlogprobswith any value, including0. Use the synchronous API for those modes.400 invalid_job_request, instead ofunsupported_job_mode, when the body also fails the completion schema, such asstream_optionswithoutstream: true.
A job counts against the same per-key limits, model limits, and budgets as a synchronous request. Near a budget cap, the API shortens the job's max_tokens. If synchronous requests fill the 16 request slots and the queue of 64 requests, a job can end with 429 queue_full.
Every job endpoint sends Cache-Control: no-store, private.
Retention and interrupted work
We delete a job's request and result 24 hours after the job ends, and /result then returns 410. For seven days after the job ends, we keep a tombstone that ties the job's idempotency key to the job. After those seven days, every endpoint for the job returns 404 job_not_found, and a request with the same idempotency key creates new work.
Do not reuse idempotency keys across experiment items.
A job that the worker loses becomes interrupted and does not run again.
While a job runs, usage_pending: true means that its token reservation is still open. On an ended job, it means that the server failed to confirm the upstream work or its accounting.
An open reservation on an ended job keeps the tombstone and blocks the idempotency key past seven days until the server or our team reconciles it. The server reconciles it automatically about an hour after the reservation goes stale. The request and result still expire after 24 hours.
If an ended job still shows usage_pending: true, email us at infra@acsresearch.org with the job ID and request ID before you submit that work again.