
Most video API clients leave chunked uploads, pagination, and token refresh as homework for whoever integrates them. AIOZ Stream's Node.js client does that work itself. This is a walk through installing it, creating and uploading a video, the CRUD operations you'll reach for right after, and the handful of things worth knowing once you're past the first request.
TL;DR:
npm install @aiozstream/nodejs-client, then construct a client with your public and secret key.client.uploadVideo()) or three explicit ones (video.create(), video.uploadPart(), video.uploadVideoComplete()) depending on how much control you want.getDetail(), getVideoPlayerInfo(), getVideoList(), update(), and delete().listWithResponseHeaders() variants of list methods, if you need to read them directly.Install the package with npm or yarn:
npm install @aiozstream/nodejs-client
# or
yarn add @aiozstream/nodejs-clientThen construct a client with the public and secret key from an API key you've generated in the dashboard:
const client = new StreamClient({
publicKey: 'YOUR_PUBLIC_KEY',
secretKey: 'YOUR_SECRET_KEY'
})Creating a video object comes first, and it doesn't need a file yet, just metadata:
const videoCreationPayload = {
title: 'First video',
description: 'A new video.'
}
const video = await client.video.create(videoCreationPayload)From there you have two paths. The explicit, three-call version uploads the file and then confirms completion separately:
const uploadResult = await client.video.uploadPart(
video.data.id,
'./path/to/video.mp4'
)
const checkResult = await client.video.uploadVideoComplete(video.data.id)Or skip straight to a single call that does both steps for you:
await client.uploadVideo(video.data.id, './path/to/video.mp4')The three-call version is worth keeping around if you want to do something between the upload finishing and marking it complete, like logging progress or triggering a separate process. Otherwise, the single-call version is less code for the same result.
Upload is only one corner of what the client does. The rest of the video lifecycle is a small, predictable set of calls:
client.video.getDetail(id), which maps to GET /videos/{id}, for fetching a single video's current state.client.video.getVideoPlayerInfo(id), which maps to GET /videos/{id}/player.json, for the data a player needs to embed and play it back.client.video.getVideoList(), which maps to POST /videos, for listing and searching videos on the account.client.video.update(id, payload), which maps to PATCH /videos/{id}, for changing title, description, or other metadata after the fact.client.video.delete(id), which maps to DELETE /videos/{id}.None of these need a different client or a different credential; they're the same video sub-client the upload calls live on. The pagination handling mentioned in the SDK's own description shows up specifically in getVideoList(): rather than manually tracking page tokens or offset parameters across repeated calls the way you'd need to against the raw REST endpoint, the client manages that state internally, so paging through a large video library reads as a normal loop over results instead of hand-rolled cursor bookkeeping.
AIOZ Stream's own SDK documentation is specific about this: "chunking files is handled for you, as is pagination and refreshing your tokens." That's the actual value of using the SDK over calling the REST API directly. Uploading a large file as one request isn't reliable; a dropped connection partway through a single-request upload of a multi-gigabyte file means starting over from byte zero, which is exactly the failure mode chunked, resumable upload protocols like tus exist to solve industry-wide, not just for AIOZ Stream specifically. Splitting the file into parts and reassembling them is the kind of logic that's easy to get subtly wrong the first time you write it yourself, and here it's already done.
The SDK's own documentation shows error handling as a plain try/catch around an async call:
try {
const video = await client.video.create(videoCreationPayload)
} catch (e) {
console.error(e)
}There's no special error type or wrapper to learn beyond that. A failed call throws, and whatever the underlying REST API returned as an error is what you'll see in the caught exception. Worth building in from the start: a 429 (rate limit) or a 5xx server error is generally worth retrying with backoff, while a 4xx validation error on something like video.create() almost never resolves itself on retry, since the request itself is what's wrong, not a transient condition. Treating every caught error the same way, retry blindly or fail immediately, works until it doesn't; distinguishing those two categories early avoids either hammering a rate limit or giving up on something a short delay would have fixed. A minimal version of that split looks like checking the caught error's status before deciding what to do next, rather than wrapping every call in the same blanket retry loop regardless of what actually failed:
try {
await client.video.create(videoCreationPayload)
} catch (e) {
if (e.status === 429 || e.status >= 500) {
// back off and retry
} else {
// log and surface the error, retrying won't help
}
}That's a small amount of extra code for a meaningfully more resilient integration than either extreme.
List methods return just the response body by default, but a listWithResponseHeaders() variant of the same method returns both. The documented example calls it on the webhook client specifically:
const { headers, body } = await client.webhook.listWithResponseHeaders()That gives you X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Retry-After alongside the actual data. Most integrations won't need this on day one, but it's there once you're running enough volume to actually care about backing off correctly instead of guessing.
Putting the pieces together, a common real pattern is registering a webhook once instead of polling getDetail() in a loop after every upload:
const webhook = await client.webhook.create({
url: 'https://your-app.example.com/webhooks/aioz',
events: ['encoding_finished', 'encoding_failed']
})
const video = await client.video.create(videoCreationPayload)
await client.uploadVideo(video.data.id, './path/to/video.mp4')
// Your webhook endpoint gets called when encoding actually finishes,
// instead of your code checking getDetail() on a timer.The webhook endpoint itself is just a normal route in whatever server framework you're using, not something the SDK generates for you, and it should treat delivery as at-least-once rather than assuming an event only arrives a single time. That's the same idempotency point covered in more depth in the Developer Guide's webhook section, worth applying here rather than treating this SDK's webhooks as a special case.
The same client exposes players, playlist, webhook, and analytics alongside video, all built from the same public/secret key pair. None of them need a separate credential or a separate client instance; if you're already authenticated to upload video, you're already authenticated to manage a playlist or register a webhook.
Do I need to handle chunked upload myself for large files?
No. uploadPart() handles chunking for you, and uploadVideo() wraps both the upload and completion check into a single call if you don't need to do anything in between.
What's the difference between video.create() and uploadVideoComplete()?create() registers a video object with metadata before any file exists. uploadVideoComplete() confirms the file upload is finished so transcoding can start.
How do I fetch a list of videos instead of one at a time?client.video.getVideoList(), which maps to POST /videos under the hood, since a list call typically needs a filter or search payload.
Does the SDK handle API key expiration or token refresh automatically?
Yes, per AIOZ Stream's own SDK documentation, token refresh is handled by the client rather than something you implement yourself.
How do I check if I'm close to hitting a rate limit?
Use the listWithResponseHeaders() version of a list method instead of the default one; it returns the response body plus the rate-limit headers.
Should I retry every failed SDK call the same way?
No. A rate-limit (429) or server error (5xx) is usually worth retrying with backoff. A validation error (4xx) on a call like video.create() means the request itself needs fixing, not a delay, so retrying it unchanged will just fail again.

AIOZ's Audio API stores, transcodes, and streams audio well. It has no RSS feed generation, the actual mechanism Apple Podcasts and Spotify require.

Three codecs solve the same problem. Which one wins in practice depends on patent licensing and hardware decode support as much as compression efficiency.

Resolution and codec live inside a qualities array. H.264 caps at 4K on AIOZ Stream, so an 8K output needs H.265, and the compute cost isn't small.

AWS states an exact 11-nines durability figure. Decentralized networks prove durability differently, and don't all use one method. Here's how each actually works.

AIOZ Stream splits uploads into 50-200MB parts across three API calls. Here's the raw chunking loop, and what the docs don't say about resuming a failed one.

A fixed ladder wastes bits on simple content and starves complex content. Here's how per-title and per-shot encoding actually build a better one, with real numbers.