API reference
Media uploads: files by URL or signed upload
A media upload in the Preflight API is a file — a picture or a video — created with POST /v1/media before the post that uses it, and named in that post by its id. The bytes never pass through the API: either hand over a public URL the server fetches itself, or PUT the file straight to storage through signed links, in one piece up to 16 MB and in 16 MB parts above that.
Files
A file is uploaded first and named in a post by its id. A 600 MB video never passes through this API: either hand over a URL and the server fetches it, or upload straight to storage with signed links.
Hand us a URL
Best when your generator already produces a downloadable link. The fetch is queued immediately, because generated URLs tend to expire, and is retried three times. The address must be public (no private networks, no credentials in it; up to five redirects are followed).
POST /v1/media
{ "kind": "video",
"sourceUrl": "https://your-generator.example/render/abc.mp4",
"filename": "weekly-mix.mp4" }
→ 202 { "id": "9f3c…", "status": "ingesting", … }Poll GET /v1/media/{id} until status is ready, then create the post. A fetch that cannot be completed ends as failed with the reason in error.
Upload it yourself
Declare the file with its sizeBytes and mimeType (image/* or video/*, matching kind; SVG is not taken). Up to 16 MB you get one URL to PUT the whole file to, with the Content-Type header it names:
POST /v1/media
{ "kind": "image", "sizeBytes": 2400000, "mimeType": "image/jpeg", "filename": "cover.jpg" }
→ 200 { "id": "4a71…", "status": "awaiting_upload",
"upload": { "type": "single", "url": "https://…", "method": "PUT",
"headers": { "Content-Type": "image/jpeg" } } }PUT the bytes there, then call POST /v1/media/{id}/uploaded. Larger, and you get 16 MB parts:
POST /v1/media
{ "kind": "video", "sizeBytes": 629145600, "mimeType": "video/mp4",
"durationSeconds": 3600, "width": 1920, "height": 1080 }
→ 200 { "id": "9f3c…",
"status": "awaiting_upload",
"upload": { "type": "multipart",
"partSize": 16777216,
"parts": [ { "partNumber": 1, "url": "https://…" }, … ],
"completeUrl": "/v1/media/9f3c…/complete" } }PUT each part, keep the ETag from each response, then finish. Parts may go up in parallel. Upload links last an hour, and a file still not uploaded after a day is marked failed.
POST /v1/media/9f3c…/complete
{ "parts": [ { "partNumber": 1, "etag": "\"a1b2…\"" }, … ] }durationSeconds, width, height (and fps if you have it) it cannot warn you that a 40-minute video will be refused by TikTok before it tries.Limits and how long files are kept
The largest file is 200 MB on Free, 1 GB on Starter and 2 GB on Pro and Studio; a larger one is refused with 402 file_too_large. Once a post has gone out, its files are removed after 7, 14, 30 or 90 days by plan, and the media then reads as deleted. Creating media is limited to 60 calls a minute.
A file from Google Drive
Create the media with sourceUrl set to exactly https://www.googleapis.com/drive/v3/files/FILE_ID?alt=media and the user's Google access token as sourceAccessToken (the drive.file scope is enough for a file they picked). The token is accepted only for a Drive file address, is kept encrypted on the fetch job rather than on the media, and is not forwarded if Google redirects the download to another host.
The media object
{ "id": "9f3c…", "kind": "video", "status": "ready",
"url": "https://…", // signed preview, 1 hour; null until ready
"filename": "weekly-mix.mp4", "mimeType": "video/mp4", "sizeBytes": 629145600,
"durationSeconds": 3600, "fps": 30, "width": 1920, "height": 1080,
"error": null, "createdAt": "2026-09-21T10:00:00Z" }status is awaiting_upload, ingesting, ready, failed or deleted.
Endpoints
Files
A file used by a post that has not finished going out cannot be deleted (409 in_use).
Questions: info@preflight.social. The same API as a schema: openapi.json (OpenAPI 3.1).
Write it once. Let the rules be our problem.
Preflight checks every post against each network's rules before it leaves, then publishes it to twelve networks. Free plan, no card.