← All guides

Featured Guide API

Your first API call, in three requests

Ask for somewhere to put the file, upload the bytes, submit the job — in curl, Python and TypeScript, with the parts that usually go wrong.

Last updated 4 September 2026

Three requests, or one if you use a client. Your API key is issued from inside the web app, and the API address is shown there and in the reference.

export CV_API_KEY="cv_..."
export CV_API_BASE="https://api.example"   # the address shown in the web app

1 — Ask for somewhere to put the file

You do not upload through the API. You ask it for a presigned URL and upload straight to storage, so nothing proxies your bytes.

LEN=$(wc -c < image.jpg)
curl -X POST "$CV_API_BASE/v1/uploads" \
  -H "Authorization: Bearer $CV_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"content_type\": \"image/jpeg\", \"content_length\": $LEN}"
# -> {"upload_url": "...", "input_key": "..."}

content_length is required. The length and the content type are both signed into the URL that comes back, which is what the next step has to match.

2 — Upload the bytes

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/jpeg" \
  --data-binary @image.jpg

The content type has to be the one you declared. It is part of the signature, so omitting the header or sending a different value is refused by storage rather than by us, and the error will not mention CraterView.

3 — Submit the job

curl -X POST "$CV_API_BASE/v1/jobs?wait=30" \
  -H "Authorization: Bearer $CV_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"cv-enhance-v3\", \"input_key\": \"$INPUT_KEY\",
       \"params\": {\"scale\": 4}}"

?wait=30 holds the response open for up to thirty seconds, so a fast job comes back finished in the same request. A job that takes longer returns immediately with an identifier to poll.

The same thing with a client

# pip install craterview
from craterview import CraterView

cv = CraterView(api_key="cv_...")

job = cv.run(
    "image.jpg", model="cv-enhance-v3",
    scale=4, wait=30,
)

job.save("image-restored.jpg")
// npm install craterview
import { CraterView } from "craterview";

const cv = new CraterView({ apiKey: "cv_..." });

const job = await cv.run(file, {
  model: "cv-enhance-v3",
  scale: 4, wait: 30,
});

const blob = await job.blob();

Four things that catch people out

-d without a JSON content type arrives form-encoded and never parses. Both curl examples above set the header for that reason.

Single quotes matter in the shell. "$INPUT_KEY" inside single quotes is not expanded, which produces a request that looks right and refers to nothing.

Do not hard-code a price or a parameter list. Every model publishes its own price and its own parameter schema from the API itself. Read them from there rather than from any page, including this one — a value copied into your code is a value that goes stale without telling you.

Model names are stable. A name means one model for as long as anything spells it, so an integration written today keeps resolving. If you are choosing between models, the catalog describes what each is for.

Next

For batching a directory rather than a single file, keep a small number of jobs in flight rather than firing the whole folder at once — you are competing with yourself for your own queue position otherwise. And if you would rather ask for the work than write the call, an assistant can do it on your account over MCP.

Try it on your own file

No signup and no card — a workspace opens for your browser the moment you do.

Open Web App