Back

Blog details

Chunked Video Upload Tutorial: 50-200MB Parts Explained

AIOZ Network
8 min readAugust 31, 2026
aioz-streamguide

A chunked video upload splits a large file into parts and sends them one request at a time. On AIOZ Stream that takes three calls: create the video object, upload each 50-200 MB part, then call complete so transcoding can start.

If you work in Node.js, the AIOZ Stream Node.js SDK handles the parts and the complete call in one uploadVideo call. This tutorial is for every other stack, and for anyone who wants to see the HTTP contract that call is built on.

TL;DR:

  • The sequence is create, part, complete. The create body must include "type": "video" and a title.
  • Each part needs a zero-based index, an MD5 hash of that part, the bytes as file, and a Content-Range header with its position in the whole file.
  • The docs give a 50-200 MB chunk size and say to upload parts one after another. Splitting the file into near-equal parts avoids a tiny last part.
  • The docs don't describe retries, resume, or a way to ask which parts arrived, so keep your own record of confirmed parts.

Updated Nov 4, 2026: rechecked every endpoint and limit against the Video API docs, and added a tested Python loop, the Node.js client's own part settings, a sequence diagram and question headings.

Why chunk a video upload?

A single upload request for a multi-gigabyte file is fragile, because if your connection drops at 90% you start again from byte zero. With parts, a failure costs you only the part that was in flight, and you can report progress part by part.

Chunking also keeps each request a predictable size for proxies and timeouts on your side. Don't confuse it with the segments a player fetches during playback. Our explainer on how video segments and M3U8 playlists work covers that other kind of chunking, and the video streaming glossary separates the two terms as well.

The three-call sequence

Every upload follows the same three steps from the AIOZ Stream Video API reference. You can authenticate with a bearer token or with the stream-public-key and stream-secret-key headers.

  • Create: POST https://api.aiozstream.network/api/media/create with a JSON body such as {"title": "My Video Title", "type": "video"}. The docs say requests without type are rejected. The response's data.id is the video ID you use from here on.
  • Part: POST https://api.aiozstream.network/api/media/:video_id/part, once per part, as multipart form data with file, index and hash, plus the Content-Range header.
  • Complete: GET https://api.aiozstream.network/api/media/:video_id/complete after every part has succeeded. This call tells AIOZ Stream the file is whole.
Sequence diagram of an AIOZ Stream chunked upload: create, three parts with index, hash and Content-Range, then complete
A 250 MB file as three near-equal parts, sent in order, then completed.

If you skip the complete call, your parts sit on the server and nothing starts encoding. Once it succeeds, you can track progress with the file.received, encoding.started and encoding.finished webhook events.

How big should each part be?

The docs set the chunk size at 50 MB to 200 MB. If your file is larger than that, you split it and upload each part in order. The docs' own example sends a 50,000,000-byte first part of a 100,000,000-byte file.

The docs don't say what happens to a last part under 50 MB, so the safest plan is to never create one. Pick a target such as 100 MB, work out how many parts that gives you, then divide the file evenly. A 250 MB file becomes three parts of about 83 MB instead of 100, 100 and 50.

A 100 MB target also leaves room on both sides of the range, whether the server reads MB as 1,000,000 or 1,048,576 bytes. A file of 50 MB or less goes up as a single part with index 0.

The official Node.js client does it differently, which we found by reading the source of @aiozstream/nodejs-client 1.0.16 from npm. Its uploadVideo cuts 50 MiB parts by default (you can set 5 to 128 MiB), numbers them from 1, sends up to five at once and leaves whatever remains as the last part. The API docs show index 0 and say to upload parts in order, so if you write your own loop, follow the docs.

For comparison, Amazon S3 multipart upload accepts parts from 5 MiB to 5 GiB, up to 10,000 of them, and states there's no minimum for the last part. AIOZ Storage's S3 API follows the same pattern, explained in how S3 multipart upload works.

A working upload loop in Python

This version uses the requests library and reads your keys from environment variables. It plans near-equal parts, hashes each one with MD5, sends them in order, then calls complete:

import hashlib, math, os
import requests

API = "https://api.aiozstream.network/api"
KEYS = {"stream-public-key": os.environ["STREAM_PUBLIC_KEY"],
        "stream-secret-key": os.environ["STREAM_SECRET_KEY"]}
TARGET = 100_000_000  # aim for 100 MB parts, inside the documented 50-200 MB range


def plan_parts(size, target=TARGET):
    """Split size into near-equal parts so no part is a tiny remainder."""
    n = max(1, math.ceil(size / target))
    step = math.ceil(size / n)
    return [(i, i * step, min(size, (i + 1) * step) - 1) for i in range(n)]


def upload(path, title):
    size = os.path.getsize(path)
    r = requests.post(f"{API}/media/create", headers=KEYS,
                      json={"type": "video", "title": title})
    r.raise_for_status()
    video_id = r.json()["data"]["id"]
    with open(path, "rb") as f:
        for index, start, end in plan_parts(size):
            f.seek(start)
            chunk = f.read(end - start + 1)
            r = requests.post(
                f"{API}/media/{video_id}/part",
                headers={**KEYS, "Content-Range": f"bytes {start}-{end}/{size}"},
                data={"index": index, "hash": hashlib.md5(chunk).hexdigest()},
                files={"file": chunk},
            )
            r.raise_for_status()  # parts go up one at a time, in order
    requests.get(f"{API}/media/{video_id}/complete", headers=KEYS).raise_for_status()
    return video_id

We compiled this with Python 3 and requests 2.31 and ran plan_parts on Nov 4, 2026. We didn't call the API, because this machine has no AIOZ Stream keys. The part plan for three file sizes, as printed:

40000000 1 [(0, 0, 39999999)] ... (0, 0, 39999999) sizes MB: [40.0]
250000000 3 [(0, 0, 83333333), (1, 83333334, 166666667)] ... (2, 166666668, 249999999) sizes MB: [83.3]
1234567890 13 [(0, 0, 94966760), (1, 94966761, 189933521)] ... (12, 1139601132, 1234567889) sizes MB: [95.0]

Each tuple is the index, first byte and last byte, which go straight into Content-Range. The 1.23 GB file comes out as 13 parts of about 95 MB, all well inside the range. The hash covers only that part's bytes, so the server can check each part on its own.

What happens if a part fails?

The docs don't say, so plan for it in your own code. There's no documented retry advice, no rule for re-sending an index that already arrived, and no endpoint that lists the parts the server holds.

Protocols built for resuming handle this differently: in the tus resumable upload protocol, a client sends a HEAD request and the server answers with an Upload-Offset header. That header says exactly how many bytes it already has, so the client can continue from there.

On AIOZ Stream you keep that state yourself, so write each confirmed index to disk or a database as its 200 response comes back. After a crash, re-send only the missing indexes before you call complete.

Watch the rate limit too, because the rate limits page allows 100 requests per minute on the uploads API, and a 13-part file uses 13 of them. Our guide to handling 429s and rate-limit headers shows how to back off cleanly.

How AIOZ Stream handles it

We ship two official clients that wrap this sequence, Node.js and Go. In Node.js, you create the video with client.media.create, then client.uploadVideo(videoId, path) uploads the parts and calls complete. The client also exposes media.uploadPart and media.uploadMediaComplete as separate methods, and a Go client is available too.

The part request below is copied from the Video API reference. We didn't run it here, and you'd fill in your own token, range, hash and file:

curl --location 'https://api.aiozstream.network/api/media/video_id/part' \
--header 'Authorization: Bearer your_access_token_here' \
--header 'Content-Range: bytes 0-49999999/100000000' \
--form 'hash="part_content_md5_hash"' \
--form 'index="0"' \
--form 'file=@"/path/to/file"'

What the docs don't cover yet:

  • retry or resume behavior for a failed part, or what happens when you re-send an index;
  • a status call that lists received parts;
  • how a final part under 50 MB is treated;
  • whether part indexes start at 0, as in the API example, or at 1, as in the Node.js client.

Once the upload completes, webhook-driven pipelines pick up the encoding events. For the full API surface, see the AIOZ Stream developer guide, or go straight to the Video API docs.

Frequently Asked Questions

What chunk size does AIOZ Stream accept for part uploads?
The docs give 50 MB to 200 MB per part. A 100 MB target with near-equal parts keeps every part inside that range.

Do I need to create the video object before uploading parts?
Yes. The create call returns the video ID that every part and complete call needs, and its body must include "type": "video".

Can I upload parts in parallel?
The API docs say to upload each part sequentially, so a hand-written loop should send them in order. The Node.js client 1.0.16 sends up to five parts at once, which suggests the server accepts some parallelism, but the docs don't promise it.

What happens if I forget to call the complete endpoint?
The parts stay unprocessed. The complete call is what tells AIOZ Stream the file is whole and lets encoding start.

Does AIOZ Stream support resuming an interrupted upload?
Not in any documented way. There's no endpoint that lists received parts, so track confirmed indexes yourself and re-send the missing ones.

What does the MD5 hash check?
The bytes of that one part as the server receives them, so each part gets its own hash, computed before you send it.

References

We only send updates when meaningful changes ship, and you can unsubscribe anytime

Related Content

blog thumbnail

Video Streaming Protocols: HLS, DASH, WebRTC, RTMP and SRT

Video streaming protocols move video from encoder to viewer. See how RTMP, SRT, HLS, DASH and WebRTC differ in latency, transport and where each one fits.

aioz-streamguide
7 min readNovember 04, 2026
blog thumbnail

What Is Decentralized Live Streaming and How Does It Work?

Decentralized live streaming sends a real-time broadcast through independently run nodes instead of one company's servers. Here's how each stage of it works.

aioz-streamguide
6 min readNovember 03, 2026
blog thumbnail

Video Streaming Glossary: Codecs, Protocols and Delivery A-Z

A video streaming glossary of 114 terms, from AAC and ABR to WebRTC and Widevine, each defined in plain English with sources and links to the deep posts.

aioz-stream
23 min readOctober 31, 2026
blog thumbnail

Monitor Livestream Health With Bitrate, FPS and Viewer Count

Read the AIOZ Stream Statistics panel: bitrate in and out, FPS in and out and current viewers, plus the documented rules for each and what the docs leave out.

aioz-streamguide
6 min readOctober 30, 2026
blog thumbnail

How to Embed a Livestream on Your Website (Link or iFrame)

Embed an AIOZ Stream livestream with the Share button's iframe or send the Public Playback URL as a link. Covers API fields, public access limits and doc gaps.

aioz-streamguide
6 min readOctober 27, 2026
blog thumbnail

Stream Key vs Streaming Session: Preconfiguring a Livestream

Learn how a stream key differs from a streaming session on AIOZ Stream, what each one stores, and what to preconfigure before you go live. Docs-based guide.

aioz-streamguide
7 min readOctober 23, 2026