Skip to content

Seedance 2.0 MiniAPI reference

Everything you need to call Seedance 2.0 Mini from code: a three-step quick start, every field with its limits, the pricing formula, webhooks and idempotency. The MCP tools run on the same /v1 API; signed in over MCP you spend the creative balance, with an API key the developer API balance.

EndpointPOST /v1/videos/generationsModel versiondreamina-seedance-2-0-mini-260615OpenAPIllms.txtFor AI agents
Request API access

Developer API access is by application.

Quick startin three calls

Set a key, send the job with a price cap, then read the result.

  1. 01

    Set your key

    Developer API access is by application: request it in the studio under API keys. A key is shown once, so keep it in an environment variable.

    Shell
    export API_KEY="sk_test_…"
  2. 02

    Send the job

    POST the clip with an Idempotency-Key and a max_credits cap (or a quote_id). You get 202 and a queued job straight away, and the credits are held.

    POST /videos/generations
    curl https://api.pershot.app/v1/videos/generations \
      -H "Authorization: Bearer $API_KEY" \
      -H "Idempotency-Key: $(uuidgen)" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "seedance-2.0-mini",
        "prompt": "Creator holds the bottle to camera and explains one benefit, handheld, natural room light.",
        "duration": 10,
        "resolution": "720p",
        "aspect_ratio": "16:9",
        "generate_audio": true,
        "max_credits": 42
      }'
    You should see
    {
      "id": "job_01J9ZK4T8W",
      "object": "job",
      "kind": "video",
      "status": "queued",
      "money_status": "reserved",
      "queue_position": 2,
      "requested_model": "seedance-2.0-mini",
      "credits_quoted": 42,
      "credits_charged": null
    }
  3. 03

    Read the result

    GET the job until its status is succeeded. outputs[].url is signed and expires after 24 hours, so download what you keep.

    GET /jobs/{id}
    curl https://api.pershot.app/v1/jobs/job_01J9ZK4T8W \
      -H "Authorization: Bearer $API_KEY"

Parametersand limits

Read from the OpenAPI document, which is generated from the same schemas the API validates against. Unknown fields are refused.

Seedance 2.0 Mini request fields
FieldTypeAllowedNotes
modelRequiredstringseedance-2.0-fast · seedance-2.0-mini · autoThe model id.
promptRequiredstring2–2,000 charactersWhat to render: the subject, the camera and the light.
durationDefault 5integer4–15Output length in whole seconds, up to 15.
resolutionDefault "720p"string480p · 720pOutput resolution. 480p for drafts, 720p for delivery; each is its own render.
aspect_ratioDefault "16:9"string16:9 · 9:16 · 1:1Frame shape.
generate_audioDefault trueboolean–Native audio. The price is the same on or off.
image_urlOptionalstringhttps URLFirst frame for video; the source image for the edit models. A public https URL or an upload URL.
reference_image_urlsOptionalstring[]Up to 9 · https URLReference images for subject and style. Refer to them in the prompt as @image1, @image2.
seedOptionalinteger0–2,147,483,647Accepted and ignored: Seedance 2.0 takes no seed.
quote_idOptionalstringqt_…Run at a quote from POST /v1/quotes, valid for 10 minutes.
max_creditsOptionalnumber> 0The most this job may cost. Above it the job is refused, never charged.
webhook_urlOptionalstringhttps URLAn https URL that receives the job's events.
end_user_idOptionalstring1–128 charactersFor approved developer accounts (access is by application): your own user id for this request, shown in usage.
project_idOptionalstringprj_…
shot_idOptionalstringsht_…
routing_preferenceOptionalstringstability · speed
enhanceOptionalobject–

Send exactly one of quote_id or max_credits. This endpoint refuses model "auto": for video, call POST /v1/auto/plan first, then send the model it picks with max_credits.

Rate and concurrency
60 requests a minute per key, and 3 jobs at a time per account by default. More keys do not add concurrency.

Limits

Read from the same table the API checks against. Each row says who checks the limit and what happens past it.

  • EnforcedWe check it and answer 400 before anything is quoted or held.
  • SupplierThe supplier checks it. A job it refuses fails, and nothing is charged.
  • RecommendedNobody checks it. Staying inside it gives better results.
Limits for seedance-2.0-mini
LimitValueUnitWho checksPast the limit
Request
Prompt lengthprompt2 to 2,000charactersEnforced400 invalid_request on prompt, before any quote or hold.
Prompt in Englishpromptup to 1,000wordsRecommendedAccepted. The model may follow a longer prompt less closely.
Prompt in Chinesepromptup to 500Chinese charactersRecommendedAccepted. The model may follow a longer prompt less closely.
Durationduration4 to 15Default 5secondsEnforced400 invalid_request on duration. Whole seconds only.
Resolutionresolution480p, 720pDefault 720p–Enforced400 invalid_request on resolution.
Aspect ratioaspect_ratio16:9, 9:16, 1:1Default 16:9–Enforced400 invalid_request on aspect_ratio.
SeedseedIgnored–SupplierAccepted and not sent: Seedance 2.0 takes no seed, so a seed does not repeat a result.
Input video lengthinput_video_seconds2 to 15secondsEnforced400 invalid_request on POST /v1/quotes. API generations take no input video.
Images you send
Reference imagesreference_image_urlsup to 9imagesEnforced400 invalid_request on reference_image_urls.
First frame or referencesreference_image_urlsimage_url or reference_image_urls, not both–Enforced400 invalid_request on reference_image_urls. Seedance 2.0 runs one of the two modes per job.
Image sideimage_urlreference_image_urls300 to 6,000px per sideEnforced on uploadsUploads: 400 invalid_request on image_url or reference_image_urls.N, before any quote or hold. Other links: the supplier refuses the job, and nothing is charged.
Image shapeimage_urlreference_image_urls0.4 to 2.5width ÷ heightEnforced on uploadsUploads: 400 invalid_request on image_url or reference_image_urls.N, before any quote or hold. Other links: the supplier refuses the job, and nothing is charged.
Upload sizefileup to 10MBEnforcedPOST /v1/uploads answers 400 invalid_request on file.
Upload formatsfileJPEG, PNG, WebP–EnforcedRead from the file itself, not its name. Anything else: 400 invalid_request on file.
Linked image sizeimage_urlreference_image_urlsup to 30MBSupplierFor images linked from other hosts. The supplier refuses the job, and nothing is charged.
Linked image formatsimage_urlreference_image_urlsJPEG, PNG, WebP, BMP, TIFF, GIF, HEIC, HEIF–SupplierFor images linked from other hosts. The supplier refuses the job, and nothing is charged.
Output
Output pixels480p16:9864×4969:16496×8641:1640×640720p16:91280×7209:16720×12801:1960×960pxSupplierSet by the model for each resolution and aspect ratio.
Frame rate24fpsSupplierThe same for every video.
Output formatMP4–SupplierEvery video is an MP4 with sound when generate_audio is on.
Links
Upload link7daysEnforcedAfter that the link answers 403. Upload the file again.
Output link24hoursEnforcedAfter that the link answers 403. GET the job again for a fresh link. The file itself is kept for 12 months (the job's outputs_expire_at gives the date), longer if the result is saved to Assets or used in a project.

We never fetch images from other hosts, so for those links the supplier checks the size, and a job it refuses is not charged. The same limits come back as JSON: GET /v1/models returns a limits object for every model.

Pricingworked out in the open

Every job is quoted in credits before it runs (US$1 = 100 credits). The per-second rate is for budgeting; POST /v1/quotes returns the exact figure.

credits = ceil(BytePlus list price × 0.55 × 100), where list price = (input s + output s) × width × height × 24 ÷ 1024 × list rate
Seedance 2.0 Mini prices
SpecPer second (API)5 s10 s15 s
480pNo video input$0.020With an input video: $0.012/s≈ 500 s per $1010 cr≈ $0.1020 cr≈ $0.2030 cr≈ $0.30
720pNo video input$0.042With an input video: $0.025/s≈ 238 s per $1021 cr≈ $0.2142 cr≈ $0.4263 cr≈ $0.63

Refused or failed jobs are not charged.

Webhooksand idempotency

Retries never charge twice, and you do not have to poll.

Idempotency
Every generation POST needs an Idempotency-Key (1 to 255 visible ASCII characters). The same key within 24 hours returns the same job with HTTP 200, so a retry after a timeout never creates or charges a second job.
Refused prompts
A prompt refused by the pre-check (422 content_rejected) creates nothing, so the same key can be used again with a new prompt.
Events
job.queuedjob.processingjob.succeededjob.failedjob.cancelled
Signature
X-Signature carries t, the unix time, and v1, an HMAC-SHA256 of t, a dot and the raw body, keyed with your whsec_ secret. Refuse timestamps older than five minutes and dedupe on X-Event-Id.
Delivery
A 2xx within 10 seconds counts as delivered. Failed deliveries retry after 1 min, 5 min, 30 min, 2 h and 6 h. Redirects are not followed.
Webhook body
{
  "id": "evt_01J9ZKB1QX",
  "type": "job.succeeded",
  "created_at": "2026-09-29T14:02:31Z",
  "data": {
    "job": {
      "id": "job_01J9ZK4T8W",
      "object": "job",
      "kind": "video",
      "status": "succeeded",
      "money_status": "settled",
      "requested_model": "seedance-2.0-mini",
      "credits_charged": 42,
      "outputs": [
        {
          "kind": "video",
          "url": "https://…/job_01J9ZK4T8W.mp4?sig=…"
        }
      ]
    }
  }
}
Verify X-Signature
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received, before any JSON parsing.
export function verifyWebhook(header, rawBody, secret, toleranceSec = 300) {
  let t = null;
  const signatures = [];
  for (const item of (header ?? "").split(",")) {
    const [key, value] = item.split("=", 2);
    if (key === "t") t = Number(value);
    if (key === "v1") signatures.push(Buffer.from(value, "hex"));
  }
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return signatures.some((s) => s.length === expected.length && timingSafeEqual(s, expected));
}

// verifyWebhook(req.headers["x-signature"], rawBody, process.env.WEBHOOK_SECRET)
// Answer 2xx quickly, and skip an X-Event-Id you have already handled.

Errorsand what to do

Every error has the same shape: an error object with code, message, param and request_id. The ones you are most likely to meet:

CodeHTTPWhat to do
invalid_request400Fix the field named in param.
unauthorized401Check the key and the Bearer header.
insufficient_credits402Top up, then retry with the same Idempotency-Key.
forbidden_model403Add the model or tool to the key's allowlist in the studio, or use another key. Tools such as Enhance need their own entry.
quote_expired409Get a new quote; quotes last 10 minutes.
price_changed409The price rose above your cap. Quote again or raise max_credits.
content_rejected422Nothing was charged. Change the prompt or the images.
rate_limited429Wait for Retry-After, then retry.
upstream_unavailable503The model is unavailable. Retry later; a job that never starts is not charged.