Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: CraterView.ai Models API
description: 'CraterView.ai image enhancement and restoration API. ## How a job works
Three calls — upload to a presigned URL, run a job, and download the result. 1. `POST /v1/uploads` — get a presigned URL and an `input_key`
2. `PUT` your bytes to that URL
3. `POST /v1/jobs` — submit `input_key` plus parameters
Inference is asynchronous, because a job can occupy a GPU for minutes. Add `?wait=30` to
step 3 and a fast job comes back finished in the same response; otherwise poll
`GET /v1/jobs/{id}` or supply a `webhook_url`. ## Collecting the result
A finished job carries two links to the same object under `result.output`, both presigned
and both expiring. `url` serves it inline, for displaying a result in a page. `download_url` carries `Content-Disposition: attachment`, so a browser saves it under a
filename instead. Pick by what you are doing with it; a client cannot convert one into the
other, because the disposition is signed into the URL. ## Output format
By default the result comes back in the format you sent — a JPEG upload returns a JPEG —
except that a HEIC or HEIF photograph prefers JPEG and a TIFF scan comes back as PNG: the
platform reads those formats and does not write them. `output_format` names another. Either is a preference: kept wherever that format holds the
picture, and PNG where it does not — an alpha channel asked for as JPEG, more than 8 bits asked
for as JPEG or WebP, a result wider than the format holds. `result.output.content_type`
says which format came back. ## When a job fails
A failed job carries two fields. `error` is a sentence meant for a person and is reworded
whenever it can be made clearer, so show it rather than match on it. `error_code` is the
stable half, and it is what to branch on. **A job that produces no output is not charged**, whoever''s fault it was, so a failed job
always leaves your balance where it started. What the code tells you is whether retrying is
worth anything. `inference_failed` means the fault was ours and the same request is worth
sending again. Every other code refuses the request as you sent it, so an identical retry
fails identically — change something or tell the person who made it. A job that succeeded
with a result that is *wrong* is not a failure the API can see; report it with the job''s
`id` (see *Reporting a problem*). This list is not closed. 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 client that rejects a code it does not know breaks on the next one added. | `error_code` | What it means |
| --- | --- |
| `inference_failed` | Something on our side failed. Nothing was charged — a job is charged only when it succeeds — and the same request is worth sending again. |
| `unreadable_image` | The file is not an image the service can read: it may be damaged, in a format the service does not take, or not an image at all. Re-save or re-export it as PNG or JPEG; the same request will not succeed as it stands. |
| `undecodable_image` | The file starts as an image but its pixels cannot be read to the end: it is damaged, or was cut short, for instance by an upload that did not finish. Re-save or re-export it; the same request will not succeed as it stands. |
| `unsupported_tiff` | The TIFF stores its pixels in a form the service does not read: floating-point, signed or 32-bit samples; 16-bit CMYK, Lab or white-is-zero greyscale; colour planes stored separately; premultiplied transparency; or a compression other than LZW, ZIP or PackBits at 16 bits. Re-save it as 8- or 16-bit RGB or greyscale; the same request will not succeed as it stands. |
| `format_not_accepted` | The file is in a format the model does not take, whatever type it was uploaded as. The model''s `accepts` lists the formats it takes; convert the file to one of them. The same request will not succeed as it stands. |
| `no_face_found` | The model frames a headshot around a face and found none.'
version: 0.1.0
servers:
- url: https://api.craterview.ai
tags:
- name: Models
description: What is available and what it accepts.
paths:
/v1/models:
get:
tags:
- Models
summary: List Models
description: 'Every available model: what it takes, what it answers with, prices, limits, and your queue.
The one route that answers without a key. Called with none, it answers as it does for an
account that has not paid: `community` true and the community queue''s limits.
**`community` and the limits are about the queue your key would use**, not about the
platform as a whole. Jobs are routed at submit by what the account has paid for: an
account that has paid runs on paid compute, which scales with demand, and one that has
not goes to the community queue, which runs on shared, free
capacity and can wait longer at busy times. So this endpoint looks up where your account stands
and answers for the queue you would land in — `community` tells you which that is, and
the limits are the ones that apply there.
How long a wait will be is answered on the job, once you have one: `eta_seconds` on
every read of it. `community` means the same thing there as here. Paying moves you
between queues, so it can change for reasons other than other people''s jobs.
A model that is available is not always listed here — a model in trial, or being
retired, stays usable by name while absent from this catalogue.
The reverse also happens: a model with `coming_soon` set is listed before it can be
used, and submitting to it is refused with 409 until it launches. Everything else
published about it is final, so an integration can be written against it in advance.'
operationId: list_models_v1_models_get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ModelInfo'
title: Response List Models V1 Models Get
'401':
description: missing, malformed or unrecognized API key
headers:
WWW-Authenticate:
schema:
type: string
description: '`Bearer`'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'400':
description: 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
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'413':
description: the request body is larger than 1 MiB. The request was not read
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- {}
- bearerAuth: []
components:
schemas:
Error:
type: object
required:
- detail
- code
properties:
detail:
description: What went wrong, for a person. It may change; do not match on it. A validation refusal lists each field.
anyOf:
- type: string
- type: array
items:
type: object
required:
- loc
- msg
- type
properties:
loc:
type: array
items:
anyOf:
- type: string
- type: integer
description: 'Where: `body`, `query` or `header`, then the field, then an index where the field is a list.'
msg:
type: string
description: What is wrong there, for a person.
type:
type: string
description: The kind of failure, for a program.
code:
type: string
description: 'What went wrong, for a program: stable, so branch on this. Handle a code you do not know by its status.
- `missing_api_key`: no `Authorization: Bearer` header
- `invalid_api_key`: the key is not recognized, or was revoked
- `rate_limited`: too many requests a minute on this key; wait for `Retry-After`
- `too_many_uploads`: this account''s upload URLs a minute; wait for `Retry-After`
- `too_many_jobs`: this account''s jobs queued or running; wait for one to finish
- `queue_full`: the queue this job would join is full for now; wait for `Retry-After`
- `too_many_keys`: new keys this hour; wait for `Retry-After`
- `gallery_allowance_spent`: gallery submissions in the last 24 hours
- `sign_in_required`: a guest account cannot do this
- `consent_required`: `consent` was not true
- `model_not_found`: no model by that name
- `model_coming_soon`: the model is listed and not yet in service
- `video_coming_soon`: video is not taken yet
- `type_not_accepted`: the content type is not one taken here
- `upload_too_large`: the declared length is over the upload limit
- `upload_not_found`: nothing was uploaded under `input_key`
- `invalid_params`: `params` did not validate against the model''s schema
- `invalid_request`: the request did not validate; `detail` lists where
- `idempotency_conflict`: the `Idempotency-Key` was used with a different body
- `job_not_found`: no job of yours with that id
- `no_finished_job`: no finished job of yours with that id
- `job_not_settled`: the job is still queued or running
- `no_image`: the model returns no image
- `media_deleted`: the job''s pictures are gone
- `already_submitted`: the job is already awaiting moderation or published
- `submission_not_found`: no submission of yours with that id
- `key_not_found`: no active key of yours with that id
- `current_key`: that is the key you are calling with
- `only_key`: that is the account''s only way in
- `jobs_in_flight`: jobs still queued or running stop the account closing
- `account_not_found`: the key no longer reaches an account
- `unavailable`: briefly unavailable and nothing was done; send the same request again after a pause
- `malformed_request`: the request could not be read
- `body_too_large`: the request body is over the limit
- `internal_error`: a fault of ours; it may or may not have taken effect, so check before sending it again; quote `X-Trace-Id`
- `bad_request`: the request was refused
- `not_found`: nothing is at that address
- `method_not_allowed`: the address does not take that method'
ModelInfo:
properties:
id:
type: string
title: Id
description: 'The model''s id: the value to send as `model` when submitting a job. Not for showing to a person; that is `title`.'
revision:
type: string
title: Revision
description: Which revision of this model is in service. It changes when the same input gives a different result, so record it if you need to explain an old result later. The model's id stays the same.
title:
type: string
title: Title
description: What to call this model when talking to a person.
description:
type: string
title: Description
description: What this model is for and when to choose it over another, for a program or an agent choosing a model. Show `help` to a person.
help:
type: string
title: Help
description: What this model does, in one short line for a person choosing it in an app. For when to choose it over another model, read `description`.
accepts:
items:
type: string
type: array
title: Accepts
description: Media types this model takes. Anything else is refused at submit.
produces:
anyOf:
- type: string
- type: 'null'
title: Produces
description: 'The media type a result is usually returned as. Advisory: a model that preserves the format you sent returns what you sent. Null on a model that returns no file at all — its answer is in the job''s `result`, and there is nothing to download.'
coming_soon:
type: boolean
title: Coming Soon
description: 'This model is announced but not yet in service: submitting to it is refused with 409 until it launches. Everything else here is final.'
default: false
video_coming_soon:
type: boolean
title: Video Coming Soon
description: Video for this model is announced but not yet on, so `accepts` names no video type yet. False on a model that will not take video at all.
default: false
tenure:
type: string
enum:
- fixed
- comet
title: Tenure
description: fixed is a lasting part of the service. comet is a utility or fun experiment.
default: fixed
test_model:
type: boolean
title: Test Model
description: 'A model for testing an integration rather than for use: it is free unless a job asks to be charged, through a parameter it declares, and it returns a result of the right size without improving the picture. CraterView.ai''s apps do not offer it.'
default: false
max_input_bytes:
type: integer
title: Max Input Bytes
description: The largest file this model accepts, in bytes. Declared when you ask for somewhere to upload, and enforced by storage on the way in.
max_frame_megapixels:
anyOf:
- type: number
exclusiveMinimum: 0.0
- type: 'null'
title: Max Frame Megapixels
description: Largest single frame this key may send, in megapixels. A still is one frame, so it applies to images too. Null when nothing bounds it, which no model publishes today.
max_frames:
anyOf:
- type: number
exclusiveMinimum: 0.0
- type: 'null'
title: Max Frames
description: Most frames this key may send in one job. A still is one frame. Divide by reference_fps for the equivalent in seconds. Null when nothing bounds it.
max_output_megapixels:
anyOf:
- type: number
exclusiveMinimum: 0.0
- type: 'null'
title: Max Output Megapixels
description: 'Largest single frame this key may receive back, in megapixels. For a model that enlarges, the result is the image or region you send at the size you ask for — so this and `max_input_megapixels`, and not the frame ceiling, decide whether a job is accepted: send a smaller region, or ask for less enlargement. For a model that returns the picture at its own size, the result is the image or region you send. Null when nothing bounds it.'
max_input_megapixels:
anyOf:
- type: number
exclusiveMinimum: 0.0
- type: 'null'
title: Max Input Megapixels
description: 'Largest part of an image this key may have worked on in one job, in megapixels: the region you name with `roi`, or the whole image. A job over it is refused, whatever size of result it asks for: name a smaller region, or send a smaller image. For a model that enlarges, it is `max_output_megapixels` divided by the square of the largest scale you may ask for, whatever scale you ask for; with `binning`, see `max_input_megapixels_by_binning`. Null when nothing bounds it.'
max_input_megapixels_by_binning:
anyOf:
- additionalProperties:
type: number
exclusiveMinimum: 0.0
type: object
- type: 'null'
title: Max Input Megapixels By Binning
description: 'Largest part of an image this key may have worked on in one job at each `binning` value, in megapixels, keyed by the value: 2x2, 3x3 and so on. Each is `max_output_megapixels` divided by the square of the largest scale that binning allows. `max_input_megapixels` applies with binning off. Null for a model that takes no `binning`, or when nothing bounds it.'
min_input_side:
type: integer
minimum: 1.0
title: Min Input Side
description: 'Smallest side, in pixels, of the part of an image a job works on: the region you name with `roi`, or the whole image. A job with a shorter side fails as `input_too_small` and costs nothing: send a larger image, or name a larger region.'
credits_per_image:
type: integer
title: Credits Per Image
description: Credits charged for one still image. Charged when the job succeeds; a job that does not succeed costs nothing.
credits_per_video_second:
anyOf:
- type: integer
- type: 'null'
title: Credits Per Video Second
description: Credits charged per second of video, or null for a model that takes stills only. Charged when the job succeeds, from the clip's length.
reference_fps:
type: number
title: Reference Fps
description: The frame rate `max_frames` is expressed in, so a limit in frames can be read as a limit in seconds.
params_schema:
additionalProperties: true
type: object
title: Params Schema
description: 'JSON Schema for this model''s `params`: everything a job may be given, with defaults and permitted values. Validate against it rather than discovering a refusal.'
params_order:
items:
type: string
type: array
title: Params Order
description: Every name in `params_schema`'s `properties`, in the order a form presents them. JSON does not order an object's keys, so read the order from here.
params_help:
additionalProperties:
type: string
type: object
title: Params Help
description: 'A short line for a person choosing each setting in a form, keyed by parameter name: every parameter in `params_schema` that one control can set. Show it under the control. For what a value may be and what may be sent together, read `params_schema`.'
result_schema:
additionalProperties: true
type: object
title: Result Schema
description: JSON Schema for the fields a finished job of this model states under `result`, beside any files it links. Empty for a model whose whole answer is its file.
community:
type: boolean
title: Community
description: Whether your key's work goes to the community queue, which runs on shared, free capacity and can wait longer at busy times. False while the account is paying, by holding credit or by a subscription with work left this period, which puts its work on paid compute that scales with demand. The limits above are the ones that apply on that queue.
image_only_params:
items:
type: string
type: array
title: Image Only Params
description: Parameters that apply to stills only. A video job that sets one is refused, so a client offering video hides them.
type: object
required:
- id
- revision
- title
- description
- help
- accepts
- produces
- max_input_bytes
- max_frame_megapixels
- max_frames
- max_output_megapixels
- max_input_megapixels
- max_input_megapixels_by_binning
- min_input_side
- credits_per_image
- credits_per_video_second
- reference_fps
- params_schema
- params_order
- params_help
- result_schema
- community
- image_only_params
title: ModelInfo
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'Your API key, as `Authorization: Bearer cv_...`. Every `/v1` route needs one except the model catalogue, which answers without a key as it would for an account that has not paid.'