Python SDK
transcodely is the official Python SDK for the Transcodely API. It runs on Python 3.10+, uses httpx under the hood, and ships a hand-rolled JSON codec so requests and responses round-trip the API’s snake_case + lowercase-enum wire format transparently. The SDK is pre-1.0 — breaking changes are possible on minor bumps until 1.0.0.
The current release is synchronous-only. An async client is planned for a future release; until it ships, drive concurrent calls with concurrent.futures or threads.
Install
pip install transcodely
# or
uv add transcodely
# or
poetry add transcodelyhttpx (the HTTP transport) and protobuf (generated message types) are installed automatically as dependencies. For faster JSON encoding, add the optional transcodely[fast] extra, which pulls in orjson.
Authenticate
The SDK takes the API key as an explicit constructor argument. The conventional environment variable is TRANSCODELY_API_KEY. Use the context-manager form so the underlying httpx.Client is closed cleanly on exit:
import os
from transcodely import Transcodely
with Transcodely(api_key=os.environ["TRANSCODELY_API_KEY"]) as client:
...If you need a long-lived client (e.g. a web server), construct it once at startup and call client.close() on shutdown.
Create your first job
Resource methods live under attribute namespaces (client.jobs, client.videos, etc.) and accept keyword arguments matching the API’s snake_case JSON field names.
import os
from transcodely import Transcodely
with Transcodely(api_key=os.environ["TRANSCODELY_API_KEY"]) as client:
job = client.jobs.create(
input_url="https://storage.example.com/source.mp4",
output_origin_id="ori_x9y8z7w6v5",
outputs=[
{
"type": "hls",
"video": [
{"codec": "h264", "resolution": "1080p"},
{"codec": "h264", "resolution": "720p"},
],
}
],
)
print(job.id, job.status)The simplified-string form ("hls", "h264", "1080p") is what the API emits over the wire — the SDK round-trips it transparently to and from the proto enum integers.
Watch a job to completion
client.jobs.watch(job.id) returns an iterator that yields one event per state change, auto-reconnects on transient network failures, and filters HEARTBEAT events by default.
from transcodely.v1 import job_pb2
terminal = {
job_pb2.JOB_STATUS_COMPLETED,
job_pb2.JOB_STATUS_PARTIAL,
job_pb2.JOB_STATUS_FAILED,
job_pb2.JOB_STATUS_CANCELED,
}
for event in client.jobs.watch(job.id):
print(event.event, event.job.status, event.job.progress)
if event.job.status in terminal:
print("terminal:", job_pb2.JobStatus.Name(event.job.status))
breakJOB_STATUS_PARTIAL (some outputs succeeded, others failed) is also terminal — include it in the set so the stream’s final state is handled explicitly. JOB_STATUS_AWAITING_CONFIRMATION is not terminal: it pauses a delayed-start job for a ConfirmJob call, so watchers should not treat it as an end state.
Watch is read-only and idempotent: every reconnect re-emits a SNAPSHOT event so the consumer never misses the current state. Wrap the loop in try / except KeyboardInterrupt if you want clean Ctrl-C handling.
List with auto-pagination
list methods return a Page you can read directly for one page, or call .auto_paging_iter() to walk every result.
# Single page
page = client.jobs.list(pagination={"limit": 50})
print(page.items, page.next_cursor)
# All items, automatically across pages
for job in client.jobs.list(pagination={"limit": 50}).auto_paging_iter():
print(job.id, job.status)auto_paging_iter stops when the API returns an empty next_cursor. See Pagination for cursor semantics.
Handle typed errors
Every exception inherits from TranscodelyError. Match on concrete classes with isinstance (or use except clauses directly):
import time
from transcodely import (
Transcodely,
TranscodelyError,
AuthenticationError,
NotFoundError,
RateLimitError,
InvalidRequestError,
)
try:
client.jobs.create(input_url="...", outputs=[])
except InvalidRequestError as err:
for v in err.errors:
print(f"{v.field}: {v.description}")
except RateLimitError as err:
time.sleep((err.retry_after_ms or 1000) / 1000)
except NotFoundError as err:
print(f"not found: {err}")
except AuthenticationError:
print("bad API key")
except TranscodelyError as err:
print(f"[{err.request_id}] {err.code}: {err}")Every error carries code, http_status, request_id, and raw (the original response body) for debugging. See Errors for the full hierarchy.
Override idempotency
client.jobs.create auto-generates a UUID v4 Idempotency-Key so retrying within the same process is safe. For cross-process safety (queue workers, cron jobs), pass your own key:
client.jobs.create(
input_url="...",
output_origin_id="ori_x9y8z7w6v5",
outputs=[...],
idempotency_key=f"transcode_asset_{asset_id}_v1",
)All other write methods ship an Idempotency-Key automatically. See Idempotency for replay semantics.
Request IDs
The most recent request ID is exposed for log correlation:
client.jobs.get("job_a1b2c3d4e5f6")
print(client.last_request_id) # most recent request id, for supportErrors also carry err.request_id directly.
Configuration
Pass any of these to the constructor:
| Option | Default | Notes |
|---|---|---|
api_key | — required — | Your Transcodely API key |
base_url | "https://api.transcodely.com" | Override for staging or self-hosted |
timeout | 30.0 | Seconds; passed to httpx.Client |
max_retries | 3 | Retries on network errors, 5xx, 429, 503 with jittered backoff |
api_version | calendar version baked at SDK build time | Sent as Transcodely-Version |
default_headers | — | dict[str, str] — sent on every request |
http_client | — | Custom httpx.Client (testing, custom transports) |
logger | — | Callable receiving structured LogEvent objects |
client = Transcodely(
api_key=os.environ["TRANSCODELY_API_KEY"],
timeout=60.0,
max_retries=5,
default_headers={"X-Caller": "ingest-worker"},
logger=lambda event: print(event.kind, event.request_id, event.duration_ms),
)Where to go next
- API Reference — the full RPC surface.
- Webhook Integration — verify signed deliveries and handle events with
construct_event. - Errors — the typed error hierarchy in one place.
- Pagination — cursor model and auto-paging idioms.
- Idempotency — when and how to set your own key.
- SDK source on GitHub — full source and runnable examples in
examples/(creating jobs, watching, pagination, error handling, verifying webhooks, and configuring R2 / S3-compatible origins).