Requests
A request is one call of one model. It has an id (req_…), a status, its cost in API credits (1000 = 1 USD), and, when it
succeeds, the generated files. The same object comes back from every endpoint below, for every model.
Find a model
The catalog of all 1,496 models is public JSON (no key needed):
curl "https://api.doitong.ru/v1/models?category=text-to-video&lab=Google&limit=20"
| Query parameter | Meaning |
|---|---|
q |
Words that must all appear in the name, id, maker or tags, for example kling 3. |
category |
One category id, for example text-to-video or upscale. GET https://api.doitong.ru/v1/categories lists all 21 with their counts. |
lab |
The model's maker, by name or id, for example Google or black-forest-labs. |
family |
A family id from a previous row: every variant of one model (pro, fast, image-to-video, …). |
mode |
exact (the price is computed from the input), measured (from the length or size of the input files), estimate (an estimated price per run) or metered (charged by actual usage, up to a maximum per run). |
limit, cursor |
Page size (1 to 100, default 50) and the next_cursor of the previous page. |
Each row has id, name, lab, category, family, summary, tags, output_kind, docs_url and a price
object (see Pricing). GET https://api.doitong.ru/v1/models/{model_id} adds the
full input and output JSON Schema, the canonical parameter names, the price rules and an example input. An unknown
category or mode is 400 invalid_parameter.
Run a model
curl -X POST "https://api.doitong.ru/v1/run/{model_id}?wait=20" \
-H "Authorization: Bearer $DOITONG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1842-cover" \
-d '{"prompt": "..."}'
- The body is the model input, a JSON object. Each model page lists its parameters.
- The input is checked before anything is charged. An invalid input returns
422 invalid_inputwith the offendingparam, and costs nothing. ?wait=N(up to 20 seconds) holds the call until the request finishes or the time is up.?webhook_url=https://…sends a signed POST to that URL when the request finishes (see Webhooks).?storage=permanentor?storage=temporarychooses how long the output files are kept; without it the account setting applies (see Where files live).
The response is 202 Accepted while the request is still working, and 200 OK when it finished within the wait (or
when it is a replay, see below). The Location and Doitong-Request-Id headers carry the request URL and id.
Statuses
-
queuedAccepted and charged, waiting for a worker -
runningThe model is generating -
Final statuses:
succeededCharged the quote; outputs[] hold URLs on our storagefailedRefunded automatically; error says whycanceledStopped before a result; refunded
| Status | Meaning | Final |
|---|---|---|
queued |
Accepted and charged, waiting for a worker. queue_position may show the place in line. |
no |
running |
The model is generating. | no |
succeeded |
Done. outputs lists the files. |
yes |
failed |
The model or a limit stopped it. error says why. The API credits are refunded. |
yes |
canceled |
Canceled before it produced a result. The API credits are refunded. | yes |
A request that runs longer than its time limit fails with code timeout and is refunded: 15 minutes for images,
60 minutes for video, 30 minutes for audio and everything else.
The request object
{
"id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
"object": "request",
"model": "flux/schnell",
"status": "succeeded",
"created_at": "2026-10-20T12:00:00Z",
"started_at": "2026-10-20T12:00:01Z",
"completed_at": "2026-10-20T12:00:02Z",
"queue_position": null,
"cost": { "credits": 3, "usd": "0.003", "charged": 3, "refunded": 0 },
"input": { "prompt": "..." },
"output": { "images": [ { "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "width": 1024, "height": 768 } ] },
"outputs": [
{ "type": "image", "url": "https://content.doitong.com/dev-api/…/0-output.jpeg", "content_type": "image/jpeg",
"width": 1024, "height": 768, "file_size": 183920, "stored": true }
],
"error": null,
"metrics": { "inference_seconds": 1.2 },
"storage": { "mode": "permanent", "bytes": 183920, "expires_at": null, "expired": false },
"urls": {
"self": "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t",
"cancel": "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel"
}
}
outputsis the same for every model: one entry per generated file withtype(image,video,audio,fileor3d), aurlon our storage,content_typeand, when known,file_size,width,height,duration.outputis the model's own result object (its fields are listed on each model page) with the same URLs.storagesays how the files are kept:mode(permanentortemporary),bytesstored,expires_at(when a temporary file is deleted,nullfor permanent ones) andexpired(trueonce the files are gone).cost.creditsis the price of the call in API credits (cost.usdin USD),cost.chargedwhat was debited from the API balance,cost.refundedwhat came back. For ameteredmodelcost.creditsis the maximum held at the start (cost.held); once the run succeedscost.settledsays what the usage came to and the rest of the maximum is incost.refunded(see Charged by usage).- When a request fails,
erroris{ "type": …, "code": …, "message": … }.
Read a request
curl "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20" \
-H "Authorization: Bearer $DOITONG_API_KEY"
?wait=N(up to 20 seconds) long-polls: the call returns as soon as the request is final, or after N seconds with the current state. Looping on this is the simplest way to wait for a result.?logs=1adds the model's progress log lines (the last 50) aslogs.
You can only read requests of your own account; any other id is 404 request_not_found.
List requests
curl "https://api.doitong.ru/v1/requests?limit=20&status=succeeded&model=flux/schnell" \
-H "Authorization: Bearer $DOITONG_API_KEY"
Newest first. limit is 1 to 100 (default 20), status and model filter. The response is
{ "object": "list", "data": [ … ], "has_more": true, "next_cursor": "req_…" }; pass cursor=<next_cursor> for
the next page.
Cancel a request
curl -X POST "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel" \
-H "Authorization: Bearer $DOITONG_API_KEY"
- Canceled before the model started: the status becomes
canceled, the API credits are refunded, and you get200. - Already generating: the cancel is passed on and you get
202with"cancel_requested": true. If the model finishes anyway, the request succeeds normally and is charged. - Already final:
409 not_cancelable(a request that is alreadycanceledsimply returns200).
Delete the files of a request
curl -X DELETE "https://api.doitong.ru/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/files" \
-H "Authorization: Bearer $DOITONG_API_KEY"
Deletes our copies of the request's output files now and stops their storage charge. The response is the request
object with storage.expired: true; its URLs stay in the record but no longer work. Calling it again changes nothing
(200). A request that is still queued or running has no files yet: 409 request_active. Needs models:run.
Idempotency: retry without paying twice
Networks fail. To retry a run safely, send an Idempotency-Key header (any string up to 255 characters, unique per
operation, for example your own order id):
- Same key and the same body as before: you get the original request back (
200, headerIdempotent-Replayed: true), and nothing is charged again. - Same key and body after a run that failed before it reached the model (
upstream_errororinternal_erroron submit,insufficient_credits,budget_exceeded,too_many_inflight): nothing was produced and nothing is charged, so the retry runs the model again instead of returning the failure. - Same key with a different body:
409 idempotency_mismatch. - Keys are scoped to the API key that sent them.
Where files live
Every output file is copied to our storage before the request is marked succeeded, so the URLs in outputs are on
our own hosts and you can link to them directly. How long they stay is your choice:
| Mode | How long | Price |
|---|---|---|
permanent (default) |
Until you delete them | 100 API credits ($0.10) per GB per month, charged daily from the API balance |
temporary |
7 days, then deleted | Free |
- The account setting decides for new requests: the "Keep files forever" switch in the
dashboard, or
PATCH https://api.doitong.ru/v1/account/storagewith{"mode": "temporary"}.GET https://api.doitong.ru/v1/accountshows it understorage, with the bytes you keep and the monthly estimate. ?storage=permanent|temporaryon a run (thestorageargument of the MCPruntool) overrides it for that run.- Changing the setting never touches files you already have; delete those with
DELETE /requests/{id}/files. - When the API balance cannot pay for storage, nothing is charged and you get an email: top up within 30 days, or the kept files become temporary and are deleted 7 days later.
In the rare case that our copy fails, the entry has "stored": false and the URL is the original, short-lived one:
download it soon.