Skip to content
ACS Infra

Run a Job

A Job runs one Completion request on the server. The API runs it to the end, also when your script stops or your connection drops, and keeps the result for you to fetch. Set ACS_API_BASE and ACS_API_KEY first, as Set up shows.

The App's Jobs page leads to Run a Job from a shell script, a recipe that does each step below from one script.

Submit a Job

  1. Send a body of POST /v1/completions without stream, or with "stream": false, and an Idempotency-Key of your choice:

    curl "$ACS_API_BASE/completion-jobs" \
      -H "Authorization: Bearer $ACS_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: sky-001" \
      -d '{"model": "llama-8b", "prompt": "The sky is", "max_tokens": 2}'

    The API answers 202 with the queued Job, and its Location header is the Job's status_url:

    Job
    {
      "cancel_requested": false,
      "created_at": "2026-11-02T18:35:05.226+00:00",
      "expires_at": null,
      "finished_at": null,
      "id": "01a19d8b-2892-7afa-8b51-35c9fc2f5572",
      "model": "llama-8b",
      "request_id": "req_job_01a19d8b28927afa8b5135c9fc2f5572",
      "result_expired": false,
      "result_url": "/v1/completion-jobs/01a19d8b-2892-7afa-8b51-35c9fc2f5572/result",
      "started_at": null,
      "status": "queued",
      "status_url": "/v1/completion-jobs/01a19d8b-2892-7afa-8b51-35c9fc2f5572",
      "usage_pending": false
    }
  2. Put the Job's id in a shell variable for the next commands, for example JOB_ID=01a19d8b-2892-7afa-8b51-35c9fc2f5572.

To resume after a lost connection, send the same request with the same Idempotency-Key. The API answers 200 with the Job that the key made, as it is now, and runs nothing again. The same key with another request answers 409 idempotency_conflict. Use a new key for each piece of work.

The API checks and admits the request when you submit the Job. If it refuses the request, it creates no Job and answers the error object, as POST /v1/completions would:

  • 400 for a field that is not valid. param names the field, such as stream for "stream": true.
  • 400 model_not_found for a model that the API does not serve, with param model.
  • 429 budget_exceeded when your tokens cannot hold the request.
  • 429 job_capacity when you have too many Jobs. Jobs in Limits lists the caps.
  • 429 spend_cap while new GPU work is paused (Spend limit).

Check on the Job

Read the Job:

curl "$ACS_API_BASE/completion-jobs/$JOB_ID" \
  -H "Authorization: Bearer $ACS_API_KEY"
Job
{
  "id": "01a19d8b-2892-7afa-8b51-35c9fc2f5572",
  "request_id": "req_job_01a19d8b28927afa8b5135c9fc2f5572",
  "model": "llama-8b",
  "status": "succeeded",
  "cancel_requested": false,
  "created_at": "2026-11-02T18:35:05.226+00:00",
  "started_at": "2026-11-02T18:35:05.245+00:00",
  "finished_at": "2026-11-02T18:35:05.287+00:00",
  "expires_at": "2026-12-02T18:35:05.287+00:00",
  "result_expired": false,
  "status_url": "/v1/completion-jobs/01a19d8b-2892-7afa-8b51-35c9fc2f5572",
  "result_url": "/v1/completion-jobs/01a19d8b-2892-7afa-8b51-35c9fc2f5572/result",
  "usage_pending": false
}

Read the Job again until its status is succeeded, failed, or cancelled. A 200 from this route means that the API read the Job. It does not mean that the Completion succeeded. A Job is in one of these states:

statusMeaning
queuedThe Job waits to run.
runningThe API sends the request to Inference. usage_pending is true, as the request holds tokens of your Budget.
succeededThe Completion answered 200. Its result is ready.
failedThe Completion answered an error, or the API failed to keep its result.
cancelledYou canceled the Job before its Completion ended.

The times are ISO 8601 in UTC. created_at is when you submitted the Job, started_at is when the API first started it, and finished_at is when it ended. expires_at is when its result expires, 30 days after it ended, and null until then.

A Job can wait queued while other Jobs run. The order of the work says which Job the API starts next.

Fetch the result

Fetch the result from the Job's result_url:

curl -i "$ACS_API_BASE/completion-jobs/$JOB_ID/result" \
  -H "Authorization: Bearer $ACS_API_KEY"

The result has the status and the body that POST /v1/completions would have answered: 200 and the Completion, or the error object with its own status. When the Completion failed, its error has request_id beside error: the Job's request_id, not the X-Request-Id of this answer. Check the status before you read choices. The result keeps the status and the body, not the headers of the answer, such as Retry-After. The route answers these statuses:

StatusWhen
200The Completion succeeded. The body is the Completion.
202The Job is queued or running. The body is the Job, and Retry-After: 1 asks you to try again in a second.
400, 429, 502, 503, or 504The Completion failed with this status, as POST /v1/completions answers it. For example, 429 budget_exceeded when your token budget could not hold the request when it ran.
409 job_cancelledYou canceled the Job.
410 job_result_expiredThe result expired, 30 days after the Job ended. The Job itself stays.
500 job_failedThe API failed to run the Job or to keep its result. It does not run the Job again.

The Job takes its Hold when it runs, not when you submit it, as Holds of background work explains.

Ask for Activations in a Job

A Job can ask for Activations and add Steering vectors, as a request to POST /v1/completions can. Its result then has activations.

  1. Submit a Job that reads Layers 0 and 2:

    curl "$ACS_API_BASE/completion-jobs" \
      -H "Authorization: Bearer $ACS_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: sky-layers-001" \
      -d '{"model": "llama-8b", "prompt": "The sky is", "max_tokens": 2, "output_residual_stream": [0, 2]}'
  2. Put the new id in JOB_ID, and wait until the Job is succeeded.

  3. Read the shape of the result's residual stream with jq:

    curl "$ACS_API_BASE/completion-jobs/$JOB_ID/result" \
      -H "Authorization: Bearer $ACS_API_KEY" | jq -c '.activations.residual_stream.shape'
    Shape
    [2,5,4096]
  4. Decode the result's activations as Decode the tensor shows.

Cancel a Job

You can cancel a Job that is queued or running. This Job asks for 2,000 tokens, so it is still running when you cancel it.

  1. Submit the Job:

    curl "$ACS_API_BASE/completion-jobs" \
      -H "Authorization: Bearer $ACS_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: sky-long-001" \
      -d '{"model": "llama-8b", "prompt": "The sky is", "max_tokens": 2000}'
  2. Put the new id in JOB_ID, then cancel the Job:

    curl -X POST "$ACS_API_BASE/completion-jobs/$JOB_ID/cancel" \
      -H "Authorization: Bearer $ACS_API_KEY"
    Job
    {
      "id": "01a19d8b-3294-749c-a4f1-ae24ecf281f4",
      "request_id": "req_job_01a19d8b3294749ca4f1ae24ecf281f4",
      "model": "llama-8b",
      "status": "cancelled",
      "cancel_requested": true,
      "created_at": "2026-11-02T18:35:07.784+00:00",
      "started_at": "2026-11-02T18:35:07.820+00:00",
      "finished_at": "2026-11-02T18:35:07.859+00:00",
      "expires_at": "2026-12-02T18:35:07.859+00:00",
      "result_expired": false,
      "status_url": "/v1/completion-jobs/01a19d8b-3294-749c-a4f1-ae24ecf281f4",
      "result_url": "/v1/completion-jobs/01a19d8b-3294-749c-a4f1-ae24ecf281f4/result",
      "usage_pending": false
    }

The API stops the request that runs, and its result is then 409 job_cancelled. A cancel of a Job that has ended changes nothing, and answers the Job as it is.

If the API restarts while your Job runs, the Job runs again later, and its request still spends tokens once. When the API restarts says how.

The API reference lists every field of each route of a Job.

On this page