Story0.API

Reference

SDKs

Install and configure the TypeScript and Python libraries, and find every method, helper and error class they provide.

Story0 has official SDKs for TypeScript and JavaScript (@story0/sdk) and for Python (story0). Both cover the whole API, add helpers for resumable uploads, waiting and webhook verification, retry safely with idempotency keys, and raise typed errors. They follow the same design, so a method you know in one has the same name in the other: camelCase in TypeScript, snake_case in Python.

Install

npm install https://dev.story0.com/sdk/story0-sdk-0.1.0.tgz
pip install https://dev.story0.com/sdk/story0-0.1.0-py3-none-any.whl
TypeScript Python
Package @story0/sdk story0
Version 0.1.0 0.1.0
Runtime Node.js 18 or later; other runtimes with fetch and Web Crypto work, except for uploading by file path Python 3.9 or later
Modules ES modules and CommonJS Synchronous Story0 and asynchronous AsyncStory0 clients
Return values Plain objects, typed from the OpenAPI document Plain dicts, described by TypedDict classes in story0.types

Create a client

import Story0 from "@story0/sdk";

const story0 = new Story0({
  apiKey: process.env.STORY0_API_KEY, // the default
  timeoutMs: 30_000,
  maxRetries: 3,
});
import os

from story0 import Story0

client = Story0(
    api_key=os.environ["STORY0_API_KEY"],  # the default
    timeout=30,
    max_retries=3,
)
TypeScript Python Default Description
apiKey api_key The STORY0_API_KEY environment variable Your API key. A live or test key decides the mode.
baseUrl base_url STORY0_BASE_URL, then https://api.story0.com The API's base URL.
timeoutMs timeout 60 seconds (60000 ms; 60.0 s) The network timeout for each request.
maxRetries max_retries 2 Automatic retries for network errors, 429 and 5xx.
fetch http_client The global fetch; a new httpx.Client A custom transport, for proxies, custom TLS or tests.

Creating a client without a key raises an error with the code missing_api_key. Both clients send Authorization, Story0-Version: 2026-11-01 and an SDK identifier with every request.

The Python client can be used as a context manager (with Story0() as client:) so its HTTP connections are closed when you're done; client.close() does the same. AsyncStory0 takes the same arguments, with an httpx.AsyncClient for http_client, and every method is a coroutine:

python
import asyncio

from story0 import AsyncStory0


async def main() -> None:
    async with AsyncStory0() as client:
        asset = await client.assets.upload_and_wait("clip.mp4")
        result = await client.segments.create_and_wait(
            input={"asset_id": asset["id"]},
            mode="extract",
            instruction="Moments where a dog is visible",
        )
        print(len(result["segments"]))


asyncio.run(main())

Methods

Task TypeScript Python HTTP
Upload a file assets.upload({ file }) assets.upload(file) POST /v1/uploads, the bytes, POST /v1/uploads/{id}/complete
Upload and wait until ready assets.uploadAndWait({ file }) assets.upload_and_wait(file) The above, then GET /v1/assets/{id}
Open an upload session uploads.create() uploads.create() POST /v1/uploads
Complete an upload uploads.complete(id) uploads.complete(id) POST /v1/uploads/{id}/complete
Create an asset from a URL assets.create({ url }) assets.create(url) POST /v1/assets
Retrieve an asset assets.retrieve(id) assets.retrieve(id) GET /v1/assets/{id}
List assets assets.list({ limit, cursor }) assets.list(limit=, cursor=) GET /v1/assets
Delete an asset's media assets.delete(id) assets.delete(id) DELETE /v1/assets/{id}
Wait for an asset assets.wait(id) assets.wait(id) Polls GET /v1/assets/{id}
Request a segmentation segments.create(params) segments.create(**params) POST /v1/segments
Segment and wait for the result segments.createAndWait(params) segments.create_and_wait(**params) The above, a wait, then GET /v1/segments/{id}
Retrieve a segmentation segments.retrieve(id) segments.retrieve(id) GET /v1/segments/{id}
Request a screening screenings.create(params) screenings.create(**params) POST /v1/screenings
Screen and wait for the result screenings.createAndWait(params) screenings.create_and_wait(**params) The above, a wait, then GET /v1/screenings/{id}
Retrieve a screening screenings.retrieve(id) screenings.retrieve(id) GET /v1/screenings/{id}
Retrieve a job jobs.retrieve(id, { wait }) jobs.retrieve(id, wait=) GET /v1/jobs/{id}
List jobs jobs.list({ status, limit, cursor }) jobs.list(status=, limit=, cursor=) GET /v1/jobs
Wait for a job jobs.wait(id) jobs.wait(id) Long-polls GET /v1/jobs/{id}
Cancel a job jobs.cancel(id) jobs.cancel(id) POST /v1/jobs/{id}/cancel
Estimate a charge estimates.create({ operation, input }) estimates.create(operation=, input=) POST /v1/estimates
Retrieve the balance balance.retrieve() balance.retrieve() GET /v1/balance
Summarize usage usage.retrieve({ days }) usage.retrieve(days) GET /v1/usage
Create a webhook endpoint webhookEndpoints.create({ url, events }) webhook_endpoints.create(url, events=) POST /v1/webhook_endpoints
List webhook endpoints webhookEndpoints.list() webhook_endpoints.list() GET /v1/webhook_endpoints
Disable a webhook endpoint webhookEndpoints.delete(id) webhook_endpoints.delete(id) DELETE /v1/webhook_endpoints/{id}
Send a test event webhookEndpoints.test(id) webhook_endpoints.test(id) POST /v1/webhook_endpoints/{id}/test
List recent deliveries webhookEndpoints.deliveries(id) webhook_endpoints.deliveries(id) GET /v1/webhook_endpoints/{id}/deliveries
List events events.list({ type, limit, cursor }) events.list(type=, limit=, cursor=) GET /v1/events
Record reviewer feedback feedback.create(params) feedback.create(**params) POST /v1/feedback
Verify a webhook delivery Story0.webhooks.verify(body, headers, secret) story0.verify_webhook(body, headers, secret) None: runs locally

In TypeScript, request parameters are a plain object matching the JSON body, and per-call options go in a second argument: idempotencyKey, signal (an AbortSignal) and timeoutMs, plus waitTimeoutMs, pollIntervalMs and onProgress on the waiting helpers. In Python, everything is a keyword argument: the body fields, plus idempotency_key, timeout, and wait_timeout, poll_interval and on_progress on the waiting helpers. Python's create methods also accept extra_body, a dictionary merged into the JSON body, for fields added to the API after your SDK version.

Uploads

assets.upload and assets.uploadAndWait open an upload session, send the bytes and complete the upload, so you never handle the protocol yourself.

TypeScript Python
Sources A file path (Node), Blob or File, ArrayBuffer, Uint8Array, ReadableStream, or an async iterable of Uint8Array A path (str or pathlib.Path), bytes, a binary file object, or an iterable of bytes
Options filename, contentType (default video/mp4), chunkSize, onProgress(sent, total) filename, content_type (default video/mp4), chunk_size, on_progress(sent, total)

Files are sent in 8 MiB chunks by default, rounded down to a multiple of 256 KiB, and no more than a chunk or two is held in memory. Each chunk is tried up to four times. For a source that can be read again, such as a path, a buffer or a seekable file, a retry first asks the session how much it stored and resumes from there; a one-shot stream retries the chunk it holds. Upload requests go to the session's own URL with only the session's headers: the SDKs never send your API key there. For the protocol itself, see Uploading media.

Waiting

Helper Returns Raises Default limit
segments.createAndWait, screenings.createAndWait (create_and_wait) The segmentation or screening JobFailedError, JobCancelledError, WaitTimeoutError 10 minutes
jobs.wait(id) The job in its final state, whatever that is WaitTimeoutError 10 minutes
assets.uploadAndWait, assets.wait(id) (upload_and_wait) The ready asset A Story0Error with the asset's error code if it failed, or wait_timeout 5 minutes

The job helpers long-poll, holding each request for up to 20 seconds, or 2 seconds when you pass a progress callback so that it sees every stage. They never hold one connection open for the whole wait. Change the limit with waitTimeoutMs in TypeScript or wait_timeout (in seconds) in Python.

Stopping a wait stops only your local waiting: aborting a TypeScript signal, or the wait timing out, leaves the job running. WaitTimeoutError carries the job's ID (jobId, job_id), so you can resume with jobs.wait instead of submitting again. To stop the work itself, call jobs.cancel. See Jobs and results.

Retries and idempotency

The SDKs retry network errors, 429 and 5xx responses up to maxRetries (max_retries) times, with exponential backoff and jitter (about 0.5, 1 and 2 seconds, up to 8), and they honor a Retry-After header of up to 20 seconds. They only retry reads and requests that carry an idempotency key.

Processing requests, upload sessions and completions, asset and webhook-endpoint creation, cancellations and test events all get an idempotency key automatically, a random UUID unless you pass idempotencyKey or idempotency_key, and the key stays the same across that call's retries. Feedback is sent with a key, and retried, only when you pass one; the API then records it once. Pass your own keys for processing requests whenever a retry might come from a new process. See Idempotency and retries.

Errors

Every error the SDKs raise extends Story0Error and carries status (0 when no response arrived), code, the request ID (requestId, request_id), detail and raw, the full problem document. Branch on the class or on code, never on the message.

Situation TypeScript Python
401 AuthenticationError AuthenticationError
403 PermissionError PermissionDeniedError
404 or 410 NotFoundError NotFoundError
400 or 422 InvalidRequestError, with errors InvalidRequestError, with errors
402 insufficient_credits InsufficientCreditsError InsufficientCreditsError
402 charge_cap_exceeded ChargeCapExceededError ChargeCapExceededError
429 RateLimitError RateLimitError
5xx APIError APIError
No response ConnectionError APIConnectionError
A waited-for job failed JobFailedError, with job JobFailedError, with job
A waited-for job was cancelled JobCancelledError, with job JobCancelledError, with job
A job wait ran out of time WaitTimeoutError, with jobId WaitTimeoutError, with job_id
A webhook failed verification WebhookVerificationError WebhookVerificationError

API error codes are listed in Errors. These codes come from the SDKs themselves:

Code Raised when
missing_api_key No key was passed and STORY0_API_KEY isn't set.
timeout, connection_error No response arrived (ConnectionError or APIConnectionError).
upload_failed The upload session refused a chunk.
upload_connection_failed The network failed while sending upload bytes.
wait_timeout A waiting helper ran out of time.
job_cancelled The job a createAndWait call was waiting for was cancelled.
result_missing A job succeeded without a result reference. This shouldn't happen; report the job ID if it does.
webhook_headers_missing, webhook_timestamp_invalid, webhook_signature_invalid Webhook verification failed.

Webhook verification

import Story0 from "@story0/sdk";

const event = await Story0.webhooks.verify(rawBody, request.headers, process.env.STORY0_WEBHOOK_SECRET!);
import os

import story0

event = story0.verify_webhook(raw_body, request.headers, os.environ["STORY0_WEBHOOK_SECRET"])

Pass the raw body exactly as it arrived, the request headers and the endpoint's whsec_… secret. Both helpers check the webhook-id, webhook-timestamp and webhook-signature headers, reject timestamps more than 5 minutes from now (change it with { toleranceSeconds } or tolerance=), and return the event as { id, type, timestamp, data }. TypeScript also exports the helper as verifyWebhook, and it's available on a client instance as story0.webhooks.verify. Python also offers Story0.webhooks.verify, and story0.sign_webhook(secret, msg_id, timestamp, payload) for building signed test deliveries for your own handler. See Webhooks.

Types

The TypeScript package exports types for every object, including Asset, Upload, Job, JobStatus, Segmentation, Segment, Screening, ScreeningCategory, Estimate, Balance, Usage, WebhookEndpoint, WebhookDelivery, Event, WebhookEvent, Problem, SegmentCreateParams and ScreenCreateParams. They're generated from the OpenAPI document.

The Python package describes the same shapes as TypedDict classes in story0.types, for editors and type checkers; at run time every value is a plain dict or list. Within an API version, fields can be added, so write code that tolerates keys it doesn't know.

Keep keys on the server

The SDKs are for servers, scripts, workers and command-line tools. Never ship an API key to a browser or a mobile app: anyone who has it can spend your credits and read your results. Keep it in an environment variable or a secret manager, and pass results to your front end through your own API.