YouTube videos.insert resumable upload for a large MP4 (Sume file)

Use uploadType=resumable for a big Sume-made MP4: start a session, send 256 KB-multiple chunks, and resume after a 308 instead of re-sending the file.

6 min readSume
All posts

For a large MP4 from Sume, use uploadType=resumable on videos.insert: one request opens a session and returns an upload URL in the Location header, then you PUT the file to that URL, in one piece or in chunks, and ask the server how much arrived if the connection drops. Resumable is the option Google describes for large files with progress tracking and recovery; multipart and media are the other two uploadType values.

The endpoint is POST https://www.googleapis.com/upload/youtube/v3/videos, and the videos.insert reference accepts video/* and application/octet-stream up to 256 GB. A Sume output is far below that, so the choice is about reliability, not size limits.

Which uploadType should I pick?

The reference describes three, and the pick is mostly about what you need to survive a failed network call.

videos.insert uploadType options (YouTube Data API videos.insert page, read 2026-10-03)
uploadTypeWhat the page saysUse it for
resumableFor large files with progress tracking and recovery capabilityA long or high-bitrate Sume export, batch uploads on a flaky link
multipartCombines metadata and media in a single requestA small clip when you want one call
mediaUploads video content only, without metadataRarely useful for a Short: you want the title and status set

How does the resumable protocol work?

Per the resumable upload guide, step one is POST ...?uploadType=resumable&part=snippet,status with Authorization, a JSON body holding the video resource, and two headers: X-Upload-Content-Length (the file size in bytes) and X-Upload-Content-Type. A 200 OK answers with the session URL in Location.

Step two is a PUT of the file to that URL. A finished single request returns 201 Created with the video resource. If you chunk, every chunk except the last must be a multiple of 256 KB; intermediate chunks answer 308, the last answers 201.

To recover, send PUT with Content-Length: 0 and Content-Range: bytes */TOTAL. The server answers 308 Resume Incomplete and a Range header showing the bytes it has, such as bytes=0-999999. Then send only what is left with a matching Content-Range.

import http.client
import os
from urllib.parse import urlsplit

def put_range(session_url, token, data, start, total):
    u = urlsplit(session_url)
    conn = http.client.HTTPSConnection(u.netloc)
    end = start + len(data) - 1
    conn.request("PUT", u.path + "?" + u.query, body=data, headers={
        "Authorization": "Bearer " + token,
        "Content-Range": f"bytes {start}-{end}/{total}",
    })
    resp = conn.getresponse()
    return resp.status, resp.read()

def upload(path, session_url, token, chunk=40 * 256 * 1024):
    total = os.path.getsize(path)
    sent = 0
    with open(path, "rb") as f:
        while sent < total:
            data = f.read(chunk)
            status, body = put_range(session_url, token, data, sent, total)
            sent += len(data)
            if status == 201:
                return body
            if status != 308:
                raise RuntimeError(f"upload stopped at {status}")
    raise RuntimeError("no 201 after the last chunk")

Where does the Sume file come from?

A generated video is a job: poll it until completed, then fetch GET /v1/videos/{jobId}/content?index=0 with your API key and save the bytes to disk. A trimmed clip is a new MP4 whose video_url comes from the job result.

Run the file through video inspect first. Its probe reports size_bytes, which is the number you send as X-Upload-Content-Length, along with the container and codec, and it is a quick way to catch a bad export before a session is opened. The failure-reason post lists what YouTube rejects after the fact.

What to watch for

The sketch above assumes the server took each full chunk. In real use, when a PUT fails or the connection drops, run the status query and restart from the byte the Range header reports instead of trusting your own counter. Uploads also count against the videos.insert bucket, which the page lists as 100 calls per day, so a batch plan matters more than speed; see the quota bucket post.

  • Open one session per video; do not reuse a session URL for a different file.
  • Set part=snippet,status so the title and privacy status go up with the file.
  • Chunk sizes must be multiples of 256 KB except the last chunk.
  • Sume produces and probes the file; it does not upload to YouTube for you.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume