Skip to Content
Jobs

Jobs

A job is the unit of work that you submit. Computalot expands it into tasks: one per input, chunk, or candidate. Computalot runs the tasks on matching capacity and returns structured results.

Submit with POST /api/v1/jobs: your project name, your command, and a payload.

Choosing a job type

I want to…Use
Run one script with JSON input/outputstructured_runner
Run code across a list of inputsstructured_runner + fan_out.by
Evaluate many tiny inputs per worker taskstructured_runner + fan_out.by / fan_out.items + batch_size
Evaluate CMA/evolutionary candidatesstructured_runner + fan_out.items
Train a model on a GPUstructured_runner + profile: "gpu"
Search a parameter gridsweep
Run simulations and reduce resultsmap_reduce
Compare named strategiesbenchmark
Submit many jobs at oncePOST /api/v1/jobs/batch

When in doubt, use structured_runner.

Submitting a job

curl -sS https://computalot.com/api/v1/jobs \ -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "structured_runner", "runner_command": ["python3", "evaluate.py"], "payload": {"model": "gpt-4", "dataset": "test_v3"}, "project": "my-project", "timeout_s": 600 }'

The response carries the job id, the status, and summary.billing_estimate, the authoritative cost quote for this run (see Pricing). timeout_s caps the runtime of each task. Computalot never charges for queue time.

The runner protocol

Your script talks to Computalot through files and exit codes. The task imports nothing:

  1. Computalot writes the JSON payload of the task to a temp file and points $COMPUTALOT_TASK_PAYLOAD at it
  2. Your script reads it, does the work, and writes JSON to $COMPUTALOT_TASK_RESULT
  3. Exit 0 marks the task completed. Any non-zero exit marks it failed

The exit code is the final word. Result JSON cannot override it. result_quality and result_warnings are reserved and return null and []. The API accepts and ignores legacy object-shaped result_schema metadata. Non-object values return 422.

To report progress, print a line to stdout:

print(f"COMPUTALOT_PROGRESS:{json.dumps({'step': 42, 'loss': 0.05})}")

Stdout/stderr streams into live_feedback.output_tail for the task and into the SSE job stream while the task runs. If your runner wraps a child process, run it unbuffered or flush explicitly. Logs then appear as they happen.

Fan-out

Three shapes, for three kinds of parallelism:

  • {"fan_out": {"by": "models"}} — split one array field in payload into one task per item
  • {"fan_out": {"items": [{...}, {...}]}} — provide the exact payload object for each task
  • {"fan_out": {"chunks": 20, "range_field": "total_seeds", "total": 10000}} — split a numeric range into chunk tasks

Select exactly one shape. A request that mixes by, items, or chunks + total returns 422. When each item is small, add batch_size (or batch_per_task). One dispatched task then processes several items locally, and batched tasks receive payload._batch metadata. For parameter grids, use the sweep job type instead of fan-out.

Watching a job

You wantUse
Poll statusGET /api/v1/jobs/:id every 2–5s
Live stream, one jobGET /api/v1/jobs/:id/stream — SSE, includes running-task output tails
Live stream, many jobsGET /api/v1/jobs/watch?ids=... — one SSE stream. Frames carry client_ref, tags, meta, variant, summary fields, and persistence flags
Live stream, whole projectGET /api/v1/projects/:name/stream
Per-task detailGET /api/v1/jobs/:id/taskslive_feedback, latest_progress, checkpoint/resume state
Final resultsGET /api/v1/results/:job_id — per-task results, aggregates, completeness, artifact IDs
Stdout/stderrGET /api/v1/jobs/:id/output — during retries, it keeps the output of the last failed attempt until the new attempt writes its own
Files your job producedGET /api/v1/artifacts, then GET /api/v1/artifacts/:id
CancelPUT /api/v1/jobs/:id/cancel
Edit requirementsPATCH /api/v1/jobs/:id/requirements

List and filter jobs

GET /api/v1/jobs lists your jobs, newest first. GET /api/v1/results lists your terminal jobs in result form. Both endpoints accept the same filters:

FilterEffect
status=completed,failedOne status or a comma-separated list. terminal expands to the four terminal statuses.
since=2026-08-06T00:00:00ZJobs with submitted_at at or after the cutoff. A date such as 2026-08-06 reads as midnight UTC.
tag=experiment_42Jobs whose tags array contains the exact value.
ids=job_a,job_bA specific set of job IDs in one call.
project=, type=, client_ref=Exact-match filters.
limit=, offset=Pagination. limit is 1–200 (default 50 on /jobs, default 20 on /results).

If a client must re-sync after a disconnect, pass the last successful sync time as since and page with limit and offset. Unknown status values and malformed since values return 422 with a recommended_action.

Lifecycle

planningqueuedrunningcompleted | partial | failed | cancelled

  • completed: every task succeeded
  • partial: at least one task succeeded and at least one failed or was cancelled
  • failed: no task succeeded and the work ended in failure
  • cancelled: the job was cancelled before all work completed

Options

  • Retries"max_retries": 0..10 re-runs failed tasks after the initial attempt. The submit-time hold covers the whole retry budget. Infrastructure failures (lost worker, OOM kill) requeue without this budget. If only your code can see that the machine is broken, write "failure_class": "infra" in the result JSON and exit non-zero. Computalot then requeues the task without a charge to the retry budget, at most 3 times for each task. Each attempt still bills for its runtime. When other capacity is available, a retry runs on a different machine than the one that failed the task.
  • Dependencies"depends_on": ["job_id", ...] accepts up to 50 of your job IDs. Dispatch waits until every dependency is completed or partial. A failed or cancelled dependency cancels the blocked job.
  • Artifact handoff between stages — read the concrete artifact ID from the upstream GET /api/v1/results/:job_id. Then pass it in payload._artifacts.download. The API validates ownership and records the reference before it creates work or a billing hold.
  • Tags"tags": ["experiment_42"] (max 20), then filter with GET /api/v1/jobs?tag=experiment_42.
  • Client label"client_ref": "batch_7" (max 255 bytes) groups related jobs. Search finished work with GET /api/v1/results?client_ref=batch_7.
  • Batch submitPOST /api/v1/jobs/batch takes up to 200 jobs in one request. Successful entries keep index, payload, meta, and variant.
  • Shared coordination values — store small project-scoped JSON with PUT /api/v1/projects/:name/kv/:key. Read it before submission, and pass the resolved value in payload. payload._shared.resolve is not supported and returns 422.
  • Webhooks — not available yet: non-empty callback_url values return 422. Use SSE, watch, or polling.

Sizing requirements

requirements are minimums, not machine picks. Computalot can place your task on anything at least that large. Size storage_gb for more than your dataset. The runtime image, writable caches, temp files, checkpoints, and sandbox overhead all share that disk. Heavy ML runtimes often need tens of GB free before any weights download.

If one project serves both light CPU jobs and heavy GPU training, split them into separate runtimes. Do not ship one oversized environment everywhere. Smaller runtimes place faster.

If a task is single-thread bound, set requirements.min_single_core_score to keep it off slow CPU cores. The score is relative single-core speed with baseline 1.0. Modern desktop CPUs score near 1.1–1.4. Older server CPUs score near 0.3–0.9. Values above 3.0 return 422. A machine without reported CPU facts never matches a scored task.

Edit requirements after submit

You can edit the requirements of a submitted job without cancel-and-resubmit:

curl -X PATCH https://computalot.com/api/v1/jobs/<job_id>/requirements \ -H "Authorization: Bearer $COMPUTALOT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"requirements": {"min_single_core_score": 0.8, "memory_mb": null}}'

A value replaces the current value. An explicit null removes the key. Keys and values use the same validation as submit-time requirements. The patch applies to every task of the job that is not terminal. If the merged requirements of any task are invalid, the API returns 422 and no task changes.

Queued tasks match against the new requirements on the next placement attempt. Running attempts finish under the old requirements. Later retries use the new requirements. Computalot does not re-estimate the admission-time balance hold. Settlement always bills the actual metered use.

A 409 means that the job is already terminal. Submit a new job with the corrected requirements instead.

What job responses expose

Public job, task, watch, and result payloads keep everything that you submitted (payload, meta, variant, aggregates, artifact IDs). They redact placement internals (node identities, provider IDs, runtime paths, image digests). Placement is the job of Computalot, not part of yours.

Each task carries an attempts array with per-attempt execution metadata. Each entry gives the attempt number, status, timestamps, duration_s, terminal_kind, and the retry decision. The worker field is a stable pseudonymous handle such as wkr_1a2b3c4d5e6f. The handle stays the same when the same machine runs another attempt, so you can see placement spread across retries. resource_profile shows the hardware of that worker: cpu_model, cpu_clock_ghz, single_core_score, cpu_count, memory_total_mb, and gpu_count. Node names never appear.

Last updated on