Quickstart
Run your first generation in a few minutes. All 1,496 models take the same kind of request and return the same request object, for images, video and audio: learn it once here.
1. Get an API key
Create a key at https://doitong.com/developers/dashboard#keys and keep it in an
environment variable: it is shown only once. Each key can carry its own budget, so you can hand one to an agent
without risking the whole account. GET /account returns your API balance.
export DOITONG_API_KEY="doitong_..."
curl "https://api.doitong.com/v1/account" \
-H "Authorization: Bearer $DOITONG_API_KEY"
const res = await fetch("https://api.doitong.com/v1/account", {
headers: { Authorization: `Bearer ${process.env.DOITONG_API_KEY}` },
});
console.log(await res.json());
import os
import requests
r = requests.get(
"https://api.doitong.com/v1/account",
headers={"Authorization": f"Bearer {os.environ['DOITONG_API_KEY']}"},
)
print(r.json())
Runs are paid from your account's API balance in API credits: 1,000 API credits = $1, and every model costs exactly its maker's price. Top up at https://doitong.com/developers/dashboard#balance.
2. Quote, then run
POST /quote/{model_id} takes the input JSON and returns the exact price. It needs no key and costs nothing.
Running the same input costs exactly that: the quote is the charge. (A model charged by usage is the exception: its
quote is the most the run can cost, and the unused part is refunded after the run.)
curl -X POST "https://api.doitong.com/v1/quote/kling-video/v3/pro/image-to-video" \
-H "Content-Type: application/json" \
-d '{"image": "https://example.com/fox.jpg", "duration": 5, "generate_audio": true}'
const res = await fetch("https://api.doitong.com/v1/quote/kling-video/v3/pro/image-to-video", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ image: "https://example.com/fox.jpg", duration: 5, generate_audio: true }),
});
const quote = await res.json();
console.log(quote.credits, quote.usd); // 840 "0.84"
import requests
r = requests.post(
"https://api.doitong.com/v1/quote/kling-video/v3/pro/image-to-video",
json={"image": "https://example.com/fox.jpg", "duration": 5, "generate_audio": True},
)
print(r.json()["credits"], r.json()["usd"]) # 840 0.84
{
"object": "quote",
"model": "kling-video/v3/pro/image-to-video",
"credits": 840,
"usd": "0.84",
"unit": "second",
"quantity": 5,
"unit_credits": 168,
"mode": "exact",
"breakdown": "5 s × 168 API credits"
}
Then send the same body to POST /run/{model_id}. Add ?wait=20 to hold the connection for up to 20 seconds: a fast
model returns the finished result in one round trip. Send an Idempotency-Key header so that a retry never runs (or
charges) twice.
curl -X POST "https://api.doitong.com/v1/run/flux/schnell?wait=20" \
-H "Authorization: Bearer $DOITONG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: lighthouse-001" \
-d '{"prompt": "A lighthouse on a rocky coast at dusk, warm light in the window"}'
const res = await fetch("https://api.doitong.com/v1/run/flux/schnell?wait=20", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DOITONG_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "lighthouse-001",
},
body: JSON.stringify({ prompt: "A lighthouse on a rocky coast at dusk, warm light in the window" }),
});
const request = await res.json();
console.log(request.status, request.outputs[0]?.url);
import os
import requests
r = requests.post(
"https://api.doitong.com/v1/run/flux/schnell",
params={"wait": 20},
headers={
"Authorization": f"Bearer {os.environ['DOITONG_API_KEY']}",
"Idempotency-Key": "lighthouse-001",
},
json={"prompt": "A lighthouse on a rocky coast at dusk, warm light in the window"},
)
request = r.json()
print(request["status"], request["outputs"][0]["url"] if request["outputs"] else None)
The response is a request object:
{
"id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
"object": "request",
"model": "flux/schnell",
"status": "succeeded",
"cost": { "credits": 3, "usd": "0.003", "charged": 3, "refunded": 0 },
"outputs": [
{ "type": "image", "url": "https://content.doitong.com/dev-api/2026/10/20/req_8k2m4q7x1z9c3v5b6n0p2r4t/0-output.jpeg",
"content_type": "image/jpeg", "width": 1024, "height": 768, "stored": true }
],
"error": null
}
3. Follow the status
If the model is still working when the wait ends, you get 202 Accepted with the request's id and its current
status. Every request moves through the same lifecycle:
-
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
Read the request again with its id. ?wait=20 works here too, so a simple loop is enough:
curl "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20" \
-H "Authorization: Bearer $DOITONG_API_KEY"
const headers = { Authorization: `Bearer ${process.env.DOITONG_API_KEY}` };
let request = await (await fetch("https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t?wait=20", { headers })).json();
while (request.status === "queued" || request.status === "running") {
const res = await fetch(`https://api.doitong.com/v1/requests/${request.id}?wait=20`, { headers });
request = await res.json();
}
console.log(request.status, request.outputs[0]?.url);
import os
import requests
headers = {"Authorization": f"Bearer {os.environ['DOITONG_API_KEY']}"}
request = {"id": "req_8k2m4q7x1z9c3v5b6n0p2r4t", "status": "queued"}
while request["status"] in ("queued", "running"):
request = requests.get(
f"https://api.doitong.com/v1/requests/{request['id']}",
params={"wait": 20},
headers=headers,
).json()
print(request["status"], request["outputs"][0]["url"] if request["outputs"] else None)
When status is succeeded, outputs[].url holds links to the files on our storage. If the request failed or was
canceled, its API credits are refunded automatically and error says why. Cancel anything still queued or
running:
curl -X POST "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel" \
-H "Authorization: Bearer $DOITONG_API_KEY"
For long video jobs you can skip polling and pass a webhook_url instead (see Webhooks).
4. Files
Pass any public URL as an input, or ask POST /files for an upload URL, PUT the file there and use the returned
file_url (see Files). A succeeded request lists its results in outputs[] with
type, content type and, when known, dimensions and duration.
curl -X POST "https://api.doitong.com/v1/files" \
-H "Authorization: Bearer $DOITONG_API_KEY" \
-H "Content-Type: application/json" \
-d '{"filename": "fox.jpg", "content_type": "image/jpeg", "size": 482113}'
Errors
Every model fails the same way: one envelope with a type, an exact code, the offending param and every problem
in details, so an agent can fix the request without reading prose. Nothing is charged for an invalid request.
{
"error": {
"type": "invalid_request",
"code": "invalid_input",
"message": "duration must be one of 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15.",
"param": "duration",
"details": [ { "param": "duration", "message": "duration must be one of 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15." } ],
"request_id": null,
"doc_url": "https://doitong.com/developers/docs/errors#invalid_input"
}
}
Every type and code is listed in Errors.
Pricing
Prices are in USD and API credits and shown on every model page before you run. Each request carries cost with
credits, usd, charged and refunded. A run is charged when it is accepted; failed and canceled requests are
refunded automatically. Some models are priced exactly from the input, some by the length or size of the files you
send, others charge an estimated price per run: either way the quote is what you pay. A few models (language and
vision models, voice, processing-time tools) are charged by usage: the quote is the most the run can cost, and the
part the run did not use is refunded automatically. See Pricing.
MCP and AI agents
Connect any MCP client to https://api.doitong.com/v1/mcp with the same Bearer key. Your agent can search the catalog, quote and
run models, and read every docs page as Markdown.
claude mcp add --transport http doitong-models https://api.doitong.com/v1/mcp \
--header "Authorization: Bearer YOUR_API_KEY"
Next steps
- Browse all models. Each page has a request builder that writes the code for you.
- Learn the request lifecycle: Requests.
- Use the same parameter names on every model: Parameters.
- Working with an AI coding agent? Give it https://doitong.com/developers/llms.txt or connect the MCP server.