videoseed API

Use the HTTP API to reverse a video into prompts you can paste into Dreamina / Seedance. It shares credits, queue, and history with the web app. We charge actual duration only on success.

Base URL·https://www.videoseed.ccAuth·Bearer API Key (sk_…)Response·JSON · async jobsRate limit·60 req / min / key

5-minute quick start

This Node.js sample creates a link analysis job, polls every 3 seconds, and prints the result. Requires Node.js 18+.

1

Sign in and create an API Key

Open Account → API keys, name the key and create it. The secret is shown once — save it immediately.

2

Export an environment variable

export VIDEOSEED_API_KEY='YOUR_API_KEY'
3

Run the sample

Replace the Douyin URL with a real video, save as analyze.mjs, then run node analyze.mjs.

const API_KEY = process.env.VIDEOSEED_API_KEY;
const BASE_URL = "https://www.videoseed.cc";

const created = await fetch(`${BASE_URL}/api/v1/analyze`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://www.douyin.com/video/VIDEO_ID",
  }),
});

if (!created.ok) throw new Error(await created.text());
const job = await created.json();

while (true) {
  await new Promise((resolve) => setTimeout(resolve, 3000));
  const response = await fetch(`${BASE_URL}/api/v1/jobs/${job.id}`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  if (!response.ok) throw new Error(await response.text());

  const current = await response.json();
  if (current.status === "done") {
    console.log(current.result);
    break;
  }
  if (current.status === "error" || current.status === "cancelled") {
    throw new Error(current.error_code ?? "ANALYSIS_FAILED");
  }
}

Create and manage API Keys

An API Key represents your account. Do not put it in front-end code, public repos, or logs. Revoke and mint a new one if it leaks. Web session cookies cannot call /api/v1/*; API Keys cannot call web /api/*. Key management lives on the Account page.

  1. Sign in to the web app.
  2. Create a key under Account → API keys and narrow scopes if needed.
  3. Copy the secret once and store it in server env.

You will be asked to sign in if needed; create and revoke happen on the Account page.

HTTP API

All business endpoints use Authorization: Bearer YOUR_API_KEY. Requests and responses are JSON except the presigned upload URL.

Analyze a video link

Supports Douyin, Xiaohongshu, and Bilibili pages/short links. For other platforms, download first and use the upload flow.

curl -X POST 'https://www.videoseed.cc/api/v1/analyze' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://www.douyin.com/video/VIDEO_ID"}'

Response: 202 Accepted

{
  "id": "c9aa82c5-79ac-4cb8-9a91-9223dc83b6b8",
  "status": "queued",
  "queued": true
}

Upload and analyze a local file

MP4, MOV, WebM, M4V. Max 500 MB and ≤ 30 minutes. Bytes go straight to object storage.

# 1. Presigned upload URL
curl -X POST 'https://www.videoseed.cc/api/v1/uploads' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"filename":"video.mp4","size_bytes":12345678}'

# 2. PUT the file to upload_url
curl -X PUT --upload-file './video.mp4' 'UPLOAD_URL'

# 3. Analyze with upload_key
curl -X POST 'https://www.videoseed.cc/api/v1/analyze' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"upload_key":"UPLOAD_KEY","title":"video.mp4"}'

Upload address response: 201 Created

{
  "upload_key": "uploads/USER_ID/FILE_ID.mp4",
  "upload_url": "https://storage.example.com/...",
  "method": "PUT",
  "expires_in": 1800,
  "max_bytes": 524288000
}

Optional dims

Defaults apply when omitted. Pass a full dims object only when you need to steer output.

FieldValuesDefault
modelflagship, gemini, doubao, gptgemini
videoTypegeneral, short, dance, ecommerce, script, drama, animegeneral
targetModelallRef, firstFrame, firstFrameRefallRef
beatModedialogue, actiondialogue
maxSegmentSecinteger seconds 5–120 (web UI: 15 / 30 / custom)15

Endpoint list

MethodPath
POST/api/v1/analyzesubmit url or upload_key
POST/api/v1/uploadspresigned PUT
GET/api/v1/jobs/{id}status and result
GET/api/v1/jobs/{id}/video-linkplay / download
GET/api/v1/jobshistory (cursor)
POST/api/v1/analyses/batchbatch ≤8
GET/api/v1/capabilitieslimits
GET/api/v1/creditscredits
GET/api/v1/usageusage ledger
POST/api/v1/jobs/{id}/retryretry
POST/api/v1/jobs/{id}/cancelcancel
POST / GET / DELETE/api/v1/webhooks…async notifications

Credits

curl 'https://www.videoseed.cc/api/v1/credits' \
  -H 'Authorization: Bearer YOUR_API_KEY'
{
  "balanceSec": 1800,
  "isPro": true,
  "activePacks": 1
}

Async jobs and results

Keep the returned id. Poll every 3 seconds; stop on done, error, or cancelled. Client-side wait of ~30 minutes is a reasonable upper bound.

curl 'https://www.videoseed.cc/api/v1/jobs/JOB_ID' \
  -H 'Authorization: Bearer YOUR_API_KEY'
statusMeaning
queuedIn queue
analyzingParsing, ASR, generating result
doneSuccess; result set; charged actual seconds
errorFailed; no charge; error_code set
cancelledCancelled; unused reservation released

Success body

{
  "id": "c9aa82c5-79ac-4cb8-9a91-9223dc83b6b8",
  "status": "done",
  "source_kind": "link",
  "title": "Sample",
  "result": { "text": "Full analysis text" },
  "error_code": null,
  "charged_seconds": 15,
  "created_at": "2026-07-29T08:00:00.000Z",
  "updated_at": "2026-07-29T08:02:10.000Z"
}

Error body

{
  "id": "c9aa82c5-79ac-4cb8-9a91-9223dc83b6b8",
  "status": "error",
  "source_kind": "file",
  "title": "video.mp4",
  "result": null,
  "error_code": "ASR_FAILED",
  "charged_seconds": null,
  "created_at": "2026-07-29T08:00:00.000Z",
  "updated_at": "2026-07-29T08:01:20.000Z"
}

On-demand video link

done jobs do not embed a URL in history or the result. Call on demand. play returns a durable R2 custom-domain URL (https://r2.videoseed.cc/…?e=<expires>; expires_in is kept for compatibility, e busts cache after overwrite) and skips S3 HEAD; download is still a short-lived presigned URL with Content-Disposition (10 minutes) after HEAD verifies a non-empty video/mp4.

curl 'https://www.videoseed.cc/api/v1/jobs/JOB_ID/video-link?mode=play' \
  -H 'Authorization: Bearer YOUR_API_KEY'

curl 'https://www.videoseed.cc/api/v1/jobs/JOB_ID/video-link?mode=download' \
  -H 'Authorization: Bearer YOUR_API_KEY'
{
  "url": "https://r2.videoseed.cc/upload/<sha>/video/source.mp4?e=1757673064",
  "mode": "play",
  "expires_in": 1800,
  "expires_at": "2026-08-10T00:30:00.000Z",
  "content_type": "video/mp4",
  "filename": "sample.mp4"
}

History, batch, and usage

History uses a stable keyset cursor and returns card fields only. Treat cursor as opaque.

History

GET https://www.videoseed.cc/api/v1/jobs?limit=20&status=done
Authorization: Bearer YOUR_API_KEY

Response: items / has_more / next_cursor

Batch + idempotency

POST https://www.videoseed.cc/api/v1/analyses/batch
Idempotency-Key: batch-20260805-01
body: { items: [{ client_reference_id, upload_key | url, title, dims }] }

Max 8 items. Same key + same body replays the first response; different body → 409 IDEMPOTENCY_CONFLICT.

Capabilities, usage, retry, cancel

GET /api/v1/capabilities (video_links: play 1800s, download 600s)
GET /api/v1/usage?limit=20
POST /api/v1/jobs/{id}/retry
POST /api/v1/jobs/{id}/cancel

usage is an append-only ledger: analysis_charge negative; credit_purchase / reservation_release positive. error / cancelled are retryable; only queued / analyzing are cancellable.

Webhook

On completion or failure, events go to a DB outbox first, then an independent dispatcher delivers them. Delivery failures do not change job status.

Requires scope webhooks:manage.

POST /api/v1/webhooks
{ "url": "https://your-server.example/hook", "events": ["analysis.completed", "analysis.failed"] }
→ 201; secret is returned once (whsec_…)

GET /api/v1/webhooks → list (no secret)
DELETE /api/v1/webhooks/{id} → { "ok": true }

URL must be HTTPS and public. localhost, private, link-local, and metadata addresses are rejected (400 WEBHOOK_INVALID_URL).
The dispatcher does not follow redirects.

Headers: X-Videoseed-Event-Id, X-Videoseed-Delivery-Id, X-Videoseed-Timestamp, X-Videoseed-Signature
Signature: sha256=HMAC_SHA256(secret, timestamp + "." + rawBody)
Non-2xx / timeouts retry with exponential backoff, up to 8 attempts then failed.
// Receiver: verify before JSON.parse; reject timestamps >5 minutes old; dedupe by event_id
const ts = req.headers['x-videoseed-timestamp'];
const sig = req.headers['x-videoseed-signature'];
const raw = await readRawBody(req);
if (!verifyHmac(secret, ts, raw, sig)) return res.status(401).end();
const payload = JSON.parse(raw);

Errors

HTTP errors return code, message, and request_id. When contacting support, include request_id, job id, and time — never the full API Key.

{
  "code": "QUOTA_INSUFFICIENT",
  "message": "额度不足,请充值",
  "request_id": "82d64113-09b0-473b-a7b5-1f11bbb08f64"
}
HTTPcodeWhat to do
400VALIDATIONBad fields, or both url and upload_key
400LINK_UNSUPPORTEDUnsupported link platform
400VIDEO_INVALIDUnrecognized video
400WEBHOOK_INVALID_URLWebhook URL is not HTTPS public
401API_KEY_INVALIDBad/revoked key or credential mismatch
402QUOTA_INSUFFICIENTOut of credits
403FORBIDDENForeign upload_key or missing scope
404NOT_FOUNDJob missing or not yours
404VIDEO_NOT_AVAILABLENo playable object
409IDEMPOTENCY_CONFLICTSame Idempotency-Key, different body
409JOB_NOT_RETRYABLE / JOB_NOT_CANCELLABLEWrong status
422CONTENT_REJECTEDRejected by safety policy
—LINK_NO_VIDEOJob is created first; worker then marks error and charges 3s when the link has no playable video. Create no longer returns 422 for this
—UPLOAD_NO_VIDEOJob is queued; worker then errors if the object is not a processable video (image / audio / PDF / damaged) and releases the hold. Create no longer 422s. sts still 422s on extension/MIME
429RATE_LIMITEDToo fast (>60/min)
502LINK_FETCH_FAILED / ASR_FAILED / AI_FAILEDUpstream failure
500 / 503INTERNAL / PROVIDER_DOWN / WEBHOOK_SECRET_UNAVAILABLETemporary outage

Safe to retry automatically: 429, 500, 502, 503 (2s / 4s / 8s, max 3). Do not blind-retry: 400, 401, 402, 404, 422.