---
title: "Create Job"
method: POST
path: "/v1/jobs"
tags: ["jobs"]
---

# Create Job

`POST /v1/jobs`

## Request

- Base URL: `https://api.craterview.ai`
- URL: `https://api.craterview.ai/v1/jobs`
- Auth: HTTP bearer

Queue an inference job.

Answers 202 with the job, which by default is still queued: enhancement runs on the
fleet rather than in this request, and a large job can take minutes. There are three ways
to collect the result, and the first is usually the one you want:

- Set `wait` and this call holds the response open for that many seconds, up to 30. A job
  that finishes inside the window comes back finished, with its output, and needs no
  second call at all. One that does not comes back queued or running, exactly as if you
  had not asked.
- Poll GET /v1/jobs/{id}.
- Supply a `webhook_url` and let the result come to you.

The status code is 202 either way, including when the job is already finished: it says the
submission was accepted, not that the work is still outstanding. Read `status` for that.

Send an `Idempotency-Key` header to make retries safe: a repeat with the same key
returns the original job instead of running inference again.

Send a `custom_id` to have your own identifier come back on every read of this job and
on the webhook, so a result can be matched to your record without keeping a table of
ours. It is a label: not interpreted, not required to be unique, and not a key jobs can
be looked up by. It is not the `Idempotency-Key` — that one decides whether a job is
created at all.

Send `input_megapixels` — the size of the file you uploaded — and `eta_seconds` is
estimated for your file rather than for a typical one. It is advisory: the price, the
queue and the size limits are decided from the file itself.

## Query parameters

- `wait` number: Seconds to hold the response open waiting for the job to finish. A fast job then needs no polling at all; if it is still running when the time is up you get the queued/running job back as usual.

## Headers

- `Idempotency-Key` string: Makes a retry safe: a later request with the same key and the same body answers with the job the first one made, instead of making another; the same key with a different body is refused. Any value of your own, up to 255 printable ASCII characters — a UUID is a good choice. Keys are your API key's own, so another caller's cannot collide with yours.

## Request body

- JobSubmission: The public request body for POST /v1/jobs.
  - `model` string, required: Which model to run, by the `name` from the model catalog.
  - `input_key` string, required: Object key returned by POST /v1/uploads.
  - `params` object: Options for the model, validated against its published `params_schema`.
  - `webhook_url` string, nullable: Absolute http or https URL to receive a signed POST once the job settles. See the webhooks section for the body and its signature.
  - `custom_id` string, nullable: Your own identifier for this job, returned on every read of it and on the webhook. Not interpreted, not required to be unique, and not something jobs can be searched by.
  - `input_megapixels` number, nullable: The size of the file you uploaded, in megapixels (width × height ÷ 1,000,000; for a clip, one frame times the number of frames). Optional. Used only to make `eta_seconds` an estimate for your file rather than for a typical one — it does not affect the price, the queue or whether the job is accepted, all of which read the file itself.

## Response `202`

Successful Response

- JobResponse: A job as the API states it. An absent field means it does not apply to this job — a queued one has no result, a finished one has no estimate — rather than that it is unknown.
  - `id` string, required
  - `custom_id` string, nullable: Your own identifier for the job, if you sent one.
  - `model` string, nullable: The model this job was sent to.
  - `status` 'queued' | 'running' | 'succeeded' | 'failed', required
  - `flagged` boolean, nullable: Whether an automated check thought this job's images may fall outside what the service allows: the part of your image the job worked on (all of it, unless you named a region), and for some jobs the one that came back. `null` means they were not all checked.
  - `error` string, nullable: Why a failed job failed, in words meant for a person.
  - `error_code` string, nullable: The same fact as `error`, as a stable identifier to branch on. New codes appear as new things become worth telling apart, so treat an unfamiliar one as a failure you have no special handling for rather than as an error in itself. A failed job is never charged, whatever the code; `inference_failed` is the one worth singling out, because it means the fault was ours and the same request is worth retrying.
  - `community` boolean: Whether the job runs on the community queue, which runs on shared, free capacity and can wait longer at busy times. Explains a long wait that is not a fault.
  - `credits` integer, nullable: What this job was billed. A job is charged when it succeeds — a still at the model's image price, a video by its length — so this reads 0 until then, and stays 0 for a job that fails.
  - `created_at` string, date-time, nullable: When the job was submitted. ISO-8601, UTC.
  - `finished_at` string, date-time, nullable: When the job settled. ISO-8601, UTC. Not stated while a job is still queued or running.
  - `result` JobResult: A job's whole answer: the model's own fields, and the files it produced. A model that answers only in fields carries no `output`; one that produces only a file carries `output` and nothing else; one doing both needs nothing new. The model's fields are its own, and the model's `result_schema` in the catalog describes them.
    - `output` JobOutput: Where a finished job's file is, and what it is.
      - `url` string, required: A temporary link that serves the file inline, for showing a result in a page. Expires; fetch the file rather than keeping the link.
      - `download_url` string, required: The same file, served as a download so a browser saves it under a filename. A separate link because the disposition is signed in; neither can be turned into the other.
      - `content_type` string, nullable: The file's media type.
      - `bytes` integer, nullable: The file's size, in bytes.
    - `input` JobFile: A file a job produced, other than the principal output.
      - `url` string, required: A temporary link that serves the file inline. Expires; fetch the file rather than keeping the link.
      - `content_type` string, nullable: The file's media type.
      - `bytes` integer, nullable: The file's size, in bytes.
  - `thumbnail_url` string, nullable: A temporary link to a small preview of the job's picture — the result, or the image the model worked from when the job produced no file — for showing a list of jobs without fetching each one whole. Absent until the job succeeds, where no preview could be made, and once the job's pictures are deleted.
  - `kept` boolean: Whether this result was asked to outlive the ordinary retention schedule.
  - `gallery` GalleryState: A job's live gallery submission, as a read of the job reports it.
    - `status` 'pending' | 'approved', required: `pending` while the submission is being reviewed, `approved` once it is on the gallery.
    - `post_id` string, required: The submission's id — what `DELETE /v1/gallery/{post_id}` withdraws.
  - `progress` JobProgress: How far a running job has got.
    - `stage` 'starting' | 'processing' | 'saving', required: `starting` while your file is fetched and read, `processing` while the model runs, `saving` while the result is stored.
    - `fraction` number, nullable: Roughly how much of the run is done, from 0 up to but never reaching 1, estimated from how long recent jobs like this one have taken. Null when no estimate holds: none is available yet, or the job has run past it.
  - `eta_seconds` number, nullable: Rough seconds until the job finishes — the wait for capacity plus the time running. Not stated once a job has settled, so a callback never carries one.

## Other responses

- `400`: input_key was never uploaded; or the path, query or a JSON string in the body contains a NUL character or an unpaired surrogate; the body is not JSON encoded as UTF-8, or carries a number JSON cannot represent (NaN, Infinity, or one too large) or nesting too deep to read; or an Idempotency-Key header is longer than 255 characters or not printable ASCII. The request was not read
- `401`: missing, malformed or unrecognised API key
- `404`: unknown model
- `409`: Idempotency-Key reused with a different request body; or the model is in the catalog but not yet in service, per its `coming_soon` flag
- `413`: the request body is larger than 1 MiB. The request was not read
- `415`: the declared content type is not in the model's `accepts`
- `422`: params did not validate against the model's schema, or webhook_url is not an absolute http(s) URL with a valid host and port
- `429`: the calling key's request rate is exceeded; the account already has as many jobs queued or running as it may; or the queue this job would join is full and taking no work for the moment. Nothing sent was wrong in any of the three.
- `503`: storage could not be reached to find the upload; nothing was queued or charged, and the same request is worth sending again

---

[API](https://skmtc.dev/craterview/apis/craterview-ai-api.md) · [All operations](https://skmtc.dev/craterview/apis/craterview-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/craterview/apis/craterview-ai-api/revisions/9419a5cd27cd?raw)
