YouTube Resumable Upload API
How YouTube’s resumable upload protocol works, and why it matters for large video files.
Initiate a session with POST videos.insert?uploadType=resumable, carrying the metadata as JSON plus X-Upload-Content-Type and X-Upload-Content-Length headers; the Location header returns a session URI. PUT the file to that URI in 256 KiB-aligned chunks with Content-Range headers, and after an interruption query progress with an empty PUT (Content-Range: bytes */TOTAL) to resume.
A single-request upload of a multi-hundred-megabyte video file is a bad bet — one dropped connection and you restart from zero. YouTube's resumable upload protocol exists to fix exactly that: the file transfer becomes a session you can pause, resume, and query, independent of the API call that describes the video's metadata.
Step 1: Initiate the session
Send a POST to videos.insert with uploadType=resumable, plus two headers describing the file you are about to send: X-Upload-Content-Type and X-Upload-Content-Length. The metadata (title, privacy, audience declaration) goes in this same request as JSON — no file bytes yet. A successful response is a 200 OK with an empty body and a Location header containing the session URI you upload the actual file to, per Google's resumable upload protocol guide.
Step 2: Upload the file in chunks
Send the video bytes as one or more PUT requests to the session URI, each with a Content-Range header stating exactly which byte range that chunk covers and the total file size — for example, bytes 0-2097151/52428800. Every chunk except the last must be a multiple of 256 KiB (262,144 bytes); YouTube rejects chunk sizes that do not align to that boundary. You can send the whole file as one chunk for small videos, or split it for large ones and reliability.
PUT {session-uri} HTTP/1.1
Content-Length: 2097152
Content-Range: bytes 0-2097151/52428800
<binary chunk data>Step 3: Resume after an interruption
While an upload is incomplete, the server responds 308 Resume Incomplete to each accepted chunk, with a Range header reporting the bytes it has received so far. If a connection drops mid-transfer, query current progress by sending an empty-body PUT with Content-Range: bytes */TOTAL — the response tells you exactly where to resume, so you never have to re-send bytes YouTube already has.
PUT {session-uri} HTTP/1.1
Content-Length: 0
Content-Range: bytes */52428800Why does this matter specifically for video?
Video files are large and mobile/unreliable networks are common upload sources — the exact situation where a plain single-shot POST is most likely to fail partway through. The resumable protocol turns that failure mode from "start over" into "resume from the last acknowledged byte," which is why Google effectively requires it for anything beyond a tiny test file, even though the metadata call underneath (videos.insert) is unchanged either way — see YouTube Data API v3: Upload a Video for that call.
How does PostGate handle this for you?
PostGate's media pipeline performs the resumable transfer on your behalf — pre-upload a file to POST /api/media, and PostGate manages chunking, retries, and progress against YouTube internally before your post request ever runs. Your integration only deals with a media ID, not session URIs or Content-Range headers. See How to Upload to YouTube via API for the full flow.
Frequently asked questions
What chunk size does YouTube’s resumable upload require?
Every chunk except the last must be a multiple of 256 KiB (262,144 bytes) — YouTube rejects chunk sizes that do not align to that boundary. You can send the whole file as one chunk for small videos, or split it for large ones and reliability.
What does 308 Resume Incomplete mean?
It is the normal response while an upload is incomplete — the server replies 308 to each accepted chunk, with a Range header reporting the bytes it has received so far. It is progress confirmation, not an error.
How do I resume an interrupted YouTube upload?
Query current progress by sending an empty-body PUT to the session URI with Content-Range: bytes */TOTAL — the response tells you exactly where to resume, so you never have to re-send bytes YouTube already has.
Does PostGate implement the resumable protocol for me?
Yes. Pre-upload a file to POST /api/media and PostGate manages chunking, retries, and progress against YouTube internally before your post request ever runs — your integration only deals with a media ID, not session URIs or Content-Range headers.
