Image and video generation runs as jobs. A job is a generation request that Sakuranbo AI runs in the background. Videos always return a job. Images return the images, or a job when the result is not ready in time. Chat requests are not jobs. They return the result directly.
All job endpoints need an API key sent as Authorization: Bearer. See Authentication. You can read only the jobs of your own account.
Statuses
Section titled “Statuses”A job has one of these statuses: queued, in_progress, completed, failed, cancelled, expired.
queued: the job is accepted and waiting to start.in_progress: the job is running.completed: the job finished and the output files are ready.failed: the job stopped with an error. The reason is in theerrorfield.cancelled: the job was cancelled.expired: the job did not finish within its time limit.
queued and in_progress are running statuses. The other four are final, and a job never leaves a final status. A job that is failed, cancelled, or expired is not billed, with one rare exception. If Sakuranbo AI cannot confirm whether a request reached the model provider, the job is failed with upstream_error, and its reserved amount stays reserved until Sakuranbo AI confirms whether the request is billed. If it stays reserved, contact support with the job ID.
Get a job
Section titled “Get a job”GET /v1/jobs/{id} returns one job. Poll it every few seconds until the status is final.
curl "https://api.sakuranbo.ai/v1/jobs/job_0123456789abcdef0123456789abcdef" \ -H "Authorization: Bearer $SAKURANBO_API_KEY"const response = await fetch("https://api.sakuranbo.ai/v1/jobs/job_0123456789abcdef0123456789abcdef", { headers: { Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, },});const data = await response.json();console.log(data);import os
import requests
response = requests.get( "https://api.sakuranbo.ai/v1/jobs/job_0123456789abcdef0123456789abcdef", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", },)print(response.json())The response has these fields:
| Name | Type |
|---|---|
id | string |
object | string |
model | string |
status | string |
progress | integer | null |
created_at | string |
completed_at | string | null |
expires_at | string | null |
error | object | null |
outputs | object[] |
usage | object | null |
cost | object | null |
- A job that failed still returns
200. Theerrorfield has the reason in the same fields as an error response, andcostisnull. - For a short time, a job can be
completedwhilecostis stillnull. If you need the cost, wait untilcosthas a value. usagehas the number of images for an image job, and isnullfor a video job.- A video has the same ID as its job, so
GET /v1/jobs/{id}also returns a video. A video response has the extra fieldspolling_urlandeta_seconds. See Videos.
You can receive a webhook event instead of polling. See Webhooks.
Output files
Section titled “Output files”A completed job has its files in outputs. Each item has a url, a content_type, the size in bytes, and expires_at. The files are kept until expires_at, which is 7 days after the job completes. After that time outputs is empty and the files are deleted.
The url of a file is the same each time you get the job, and it works until expires_at. GET /v1/files/{token} serves the file at a signed URL. It needs no API key, because the signature in the URL is the credential. Anyone who has the URL can download the file until expires_at, so treat the URL as a secret.
403means the URL is not valid, was changed, orexpires_athas passed. Beforeexpires_at, get the job again to read a valid URL. Afterexpires_at, the files are deleted andoutputsis empty.404means the file is no longer stored.
The endpoint supports Range requests.
List jobs
Section titled “List jobs”GET /v1/jobs lists your jobs, newest first.
curl "https://api.sakuranbo.ai/v1/jobs?status=completed&limit=10" \ -H "Authorization: Bearer $SAKURANBO_API_KEY"const response = await fetch("https://api.sakuranbo.ai/v1/jobs?status=completed&limit=10", { headers: { Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, },});const data = await response.json();console.log(data);import os
import requests
response = requests.get( "https://api.sakuranbo.ai/v1/jobs?status=completed&limit=10", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", },)print(response.json())The query parameters are optional, and the filters combine:
| Name | Type | Required | Default | Constraints |
|---|---|---|---|---|
limit | integer | No | 20 | 1 to 100 |
cursor | string | No | 1 to 128 characters | |
status | string | No | One of: queued, in_progress, completed, failed, cancelled, expired | |
object | string | No | One of: image, video |
The response has object set to list, a data array of jobs, has_more, and next_cursor. When has_more is true, send next_cursor as cursor to read the next page. An empty data array means that no job matches, and it is not an error.
Cancel a job
Section titled “Cancel a job”POST /v1/jobs/{id}/cancel cancels a job that is queued or in_progress. It has no body.
curl -X POST "https://api.sakuranbo.ai/v1/jobs/job_0123456789abcdef0123456789abcdef/cancel" \ -H "Authorization: Bearer $SAKURANBO_API_KEY"const response = await fetch("https://api.sakuranbo.ai/v1/jobs/job_0123456789abcdef0123456789abcdef/cancel", { method: "POST", headers: { Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, },});const data = await response.json();console.log(data);import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/jobs/job_0123456789abcdef0123456789abcdef/cancel", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", },)print(response.json())- The response is
200with the job, whose status iscancelled. - The amount that was reserved for the job returns to your balance, and you are not charged.
- Cancelling a job that is already
cancelledreturns200again. - Cancelling a job that is
completed,failed, orexpiredreturns400withinvalid_request.error.details.statusis the current status. A completed job keeps its charge.
POST /v1/videos/{id}/cancel cancels a video in the same way.
Jobs in the MCP server
Section titled “Jobs in the MCP server”The MCP server has the tools list_jobs and get_job, which read the same data. See MCP server.
Errors
Section titled “Errors”Errors when you get a job
Section titled “Errors when you get a job”| Code | HTTP status | Type | Description |
|---|---|---|---|
unauthorized | 401 | authentication_error | The API key is missing, malformed, unknown, revoked, or expired. The response does not say which. |
not_found | 404 | not_found_error | The requested resource does not exist. |
Errors when you cancel a job
Section titled “Errors when you cancel a job”| Code | HTTP status | Type | Description |
|---|---|---|---|
invalid_request | 400 | invalid_request_error | The request is malformed or a value is out of range. details.allowed lists the valid values when they are known. |
unauthorized | 401 | authentication_error | The API key is missing, malformed, unknown, revoked, or expired. The response does not say which. |
not_found | 404 | not_found_error | The requested resource does not exist. |
A 404 response means that no job with that ID exists in your account. For the full list of codes, see Errors.
All prices are in USD. Listed prices include our margin.