Start a video upload (step 1 of 3)
Starts a direct-to-CDN video upload and returns presigned TUS credentials.
/api/external/vault/video-uploadStarts a direct-to-CDN video upload and returns presigned TUS credentials.
Videos cannot ride through POST /api/external/vault — the platform rejects request bodies over ~4MB before the app even runs. Instead: (1) call this to create the video and get credentials, (2) upload the raw bytes straight to the returned tusEndpoint with any TUS client, sending AuthorizationSignature, AuthorizationExpire, VideoId and LibraryId as TUS headers, (3) call complete with the completionToken. The full sequence with code is in the upload guide.
The CDN API key itself is never exposed — only a sha256 signature valid ~6 hours. The completion token is valid 8 hours.
Authentication
Send the creator's API key as a bearer token: Authorization: Bearer dpfn_…. See Authentication & API keys.
Request body
Content type: application/json
| Name | Type | Required | Description |
|---|---|---|---|
originalName | string | Required | The video file name, stored as fileName. |
fileSize | integer | Optional | Optional advisory byte count — obvious oversizes are rejected up front. The authoritative check runs at completion. Max 500MB. Max: 524288000 |
{
"originalName": "teaser.mp4",
"fileSize": 52428800
}Responses
| Name | Type | Description |
|---|---|---|
videoId | string | The Bunny Stream GUID created for this upload. Send it back to the complete step, and use it as the TUS VideoId metadata. |
tusEndpoint | string | The TUS upload endpoint (https://video.bunnycdn.com/tusupload). Upload the raw file bytes here with a TUS client. Format: uri |
libraryId | string | Bunny Stream library id — send as the LibraryId TUS header. |
signature | string | Presigned sha256 — send as the AuthorizationSignature TUS header. The Stream API key itself is never exposed. |
expires | integer | Unix timestamp (seconds) when the signature expires (~6 hours) — send as the AuthorizationExpire TUS header. |
completionToken | string | HMAC token proving this upload was started by your key — required by the complete step. Valid 8 hours. |
{
"videoId": "c2f7f9e2-1111-4222-b333-4d55e6f7a8b9",
"tusEndpoint": "https://video.bunnycdn.com/tusupload",
"libraryId": "572783",
"signature": "9b2f…64 hex…c1a0",
"expires": 1755640800,
"completionToken": "eyJ…"
}Errors
| Status | Body | When |
|---|---|---|
| 400 | {"error":"Missing required field: originalName"} | `originalName` absent or blank. |
| 401 | {"error":"Unauthorized","code":"unauthorized"} | Missing or invalid API key. |
| 413 | {"error":"Video too large. Max 500MB."} | The advisory fileSize exceeds 500MB. |
| 500 | {"error":"Could not start the video upload"} | CDN video creation failed — retry later. |
Rate limiting
Every response carries the X-RateLimit-Tier header and, on limited tiers, the per-minute and per-day trios — read X-RateLimit-Remaining and X-RateLimit-Reset instead of hardcoding limits. Details in Rate limits.
Code samples
curl -X POST "https://www.dropfans.io/api/external/vault/video-upload" \
-H "Authorization: Bearer $DROPFANS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"originalName": "teaser.mp4",
"fileSize": 52428800
}'const res = await fetch(`https://www.dropfans.io/api/external/vault/video-upload`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"originalName": "teaser.mp4",
"fileSize": 52428800
}),
});
console.log(await res.json());import os
import requests
res = requests.post(
"https://www.dropfans.io/api/external/vault/video-upload",
headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
json={
"originalName": "teaser.mp4",
"fileSize": 52428800,
},
)
print(res.json())Questions? [email protected]
