Skip to main content

Official Python SDK for the Transcodely video transcoding API

Project description

transcodely

Official Python SDK for the Transcodely video transcoding API.

pip install transcodely

Quick start

import os
from transcodely import Transcodely

with Transcodely(api_key=os.environ["TRANSCODELY_API_KEY"]) as client:
    job = client.jobs.create(
        input_url="https://example.com/source.mp4",
        outputs=[{
            "type": "hls",
            "video": [
                {"codec": "h264", "resolution": "1080p"},
                {"codec": "h264", "resolution": "720p"},
            ],
        }],
    )
    print(job.id)  # "job_a1b2c3d4e5f6"

    for event in client.jobs.watch(job.id):
        print(event.job.status, event.job.progress)
        if event.job.status == 4:  # JOB_STATUS_COMPLETED
            break

The simplified-string form ("hls", "h264", "1080p") is what the API actually emits over the wire — the SDK round-trips it transparently to and from the proto enum integers.

Authentication

client = Transcodely(api_key=os.environ["TRANSCODELY_API_KEY"])

API keys are opaque ak_-prefixed secrets — pass the full value shown once at creation time.

Resources

client.jobs            # create / get / list / cancel / confirm / watch
client.videos          # upload helpers, multipart, get / list / update / delete / watch
client.presets         # create / get / get_by_slug / list / update / duplicate / archive
client.origins         # create / get / list / update / validate / archive
client.apps            # create / get / list / update / archive / enable_hosting
client.api_keys        # create / get / list / revoke
client.organizations   # create / get / list / update / check_slug
client.memberships     # list / get / update_role / remove
client.users           # get_me / get / list / update_me
client.health          # check
client.webhook_endpoints  # create / retrieve / update / delete / list / rotate_secret / send_test / list_deliveries / get_health
client.events          # retrieve / list / resend
client.webhooks        # construct_event / verify_signature (signature-verification helpers)

Origins

An origin tells Transcodely where to read source media from and where to write outputs. Every origin belongs to a single provider; pass exactly one provider-config field (s3, gcs, http, or r2) on create.

Create an S3 origin

origin = client.origins.create(
    name="Production S3",
    permissions=["read", "write"],
    s3={
        "bucket": "my-bucket",
        "region": "us-east-1",
        "credentials": {
            "access_key_id": os.environ["S3_ACCESS_KEY"],
            "secret_access_key": os.environ["S3_SECRET_KEY"],
        },
        # "endpoint": "https://s3.custom.example.com",  # for MinIO, Wasabi, etc.
    },
)

Create a GCS origin

origin = client.origins.create(
    name="Production GCS",
    permissions=["read", "write"],
    gcs={
        "bucket": "my-gcs-bucket",
        "credentials": {
            "service_account_json": os.environ["GCS_SERVICE_ACCOUNT_JSON"],
        },
    },
)

Create an HTTP origin

origin = client.origins.create(
    name="Public CDN",
    permissions=["read"],  # HTTP origins are read-only
    http={
        "base_url": "https://media.example.com",
        "credentials": {
            "headers": {"Authorization": f"Bearer {os.environ['MEDIA_TOKEN']}"},
        },
    },
)

Create an R2 origin

R2 supports two forms. With account_id (32-char hex) the endpoint is derived for you, optionally with a data-residency jurisdiction:

origin = client.origins.create(
    name="Production R2",
    permissions=["read", "write"],
    r2={
        "bucket": "media",
        "account_id": os.environ["R2_ACCOUNT_ID"],
        "jurisdiction": "default",  # or "eu", "fedramp"
        "credentials": {
            "access_key_id": os.environ["R2_ACCESS_KEY"],
            "secret_access_key": os.environ["R2_SECRET_KEY"],
        },
    },
)

Or, with an explicit endpoint (custom domain bound to a bucket, or a jurisdiction not yet enumerated):

r2 = {
    "bucket": "media",
    "endpoint": "https://media.example.com",
    "credentials": {
        "access_key_id": os.environ["R2_ACCESS_KEY"],
        "secret_access_key": os.environ["R2_SECRET_KEY"],
    },
}

Provide either account_id or endpoint, never both. jurisdiction only applies when account_id is set.

Errors

All exceptions inherit from TranscodelyError:

from transcodely import (
    Transcodely,
    TranscodelyError,
    InvalidRequestError,
    NotFoundError,
    RateLimitError,
)

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 TranscodelyError as err:
    print(f"[{err.request_id}] {err.code}: {err}")
Class Status When
APIConnectionError Network / DNS / TLS failure
APIError 5xx Server-side error
AuthenticationError 401 Bad / missing / revoked key
PermissionError 403 Authenticated but forbidden
NotFoundError 404 Resource doesn't exist
ConflictError 409 Idempotency conflict, slug taken
RateLimitError 429 Carries retry_after_ms
InvalidRequestError 400 Carries errors: list[FieldViolation]
PreconditionError 412 Wrong state (e.g. job not cancelable)

Every error carries request_id, code, http_status, and raw for debugging.

Webhook verification raises WebhookError (or a subclass) — see Webhooks.

Pagination

# One page
page = client.jobs.list(limit=50)
print(page.items, page.next_cursor)

# All items, automatically across pages
for job in client.jobs.list(limit=50).auto_paging_iter():
    print(job.id)

Idempotency

jobs.create accepts idempotency_key. If you don't pass one, the SDK generates a UUID v4 so retries are safe by default. For cross-process safety, pass your own:

client.jobs.create(
    input_url="...",
    outputs=[...],
    idempotency_key="create-job-for-asset-12345",
)

For all other write methods, the SDK ships an Idempotency-Key HTTP header automatically.

Streaming watch

for event in client.jobs.watch(job.id):
    print(event.event, event.job.status, event.job.progress)

The SDK auto-reconnects on transient network failures — Watch is read-only and re-emits a SNAPSHOT on every reconnect. HEARTBEAT events are filtered by default.

Webhooks

Verify a signed delivery and get back a typed Event. Pass the raw request body (bytes or str — do not re-serialize) and the Transcodely-Signature header:

from transcodely import construct_event, WebhookSignatureError

try:
    event = construct_event(
        request.body,                       # raw bytes/str, exactly as received
        request.headers["transcodely-signature"],
        "whsec_...",                        # your endpoint's signing secret
    )
except WebhookSignatureError:
    return Response(status_code=400)

# `event.data` is the decoded resource (a Job, JobOutput, Video, or App).
if event.type == "job.succeeded":
    print("job done:", event.data.id)
elif event.type == "video.uploaded":
    print("new video:", event.data.id)

construct_event also accepts a list of secrets so deliveries keep verifying during a secret rotation's overlap window:

event = construct_event(body, sig_header, ["whsec_previous", "whsec_current"])

Tuning and errors:

  • tolerance (default 300 seconds) bounds clock skew / replay; widen or narrow it per call.
  • WebhookSignatureError — header malformed or no signature matched.
  • WebhookTimestampError — timestamp outside the tolerance window.
  • WebhookPayloadError — body isn't valid JSON or doesn't match the event envelope.

All three inherit from WebhookError. client.webhooks.construct_event(...) is an alias for the module-level function.

Manage endpoints and replay events via the API:

endpoint = client.webhook_endpoints.create(
    app_id="app_123",
    url="https://example.com/hooks/transcodely",
    enabled_events=["job.succeeded", "job.failed"],   # or ["*"] for all
)
print(endpoint.secret)   # shown only on create + rotate_secret — store it now

# The same typed Event, fetched from the API instead of an HTTP delivery:
event = client.events.retrieve("evt_123")
for event in client.events.list(app_id="app_123").auto_paging_iter():
    print(event.type, event.id)

client.events.resend("evt_123")   # re-queue delivery to all subscribed endpoints

The 13 event types are job.created, job.succeeded, job.failed, job.canceled, job.progress, output.created, output.ready, output.failed, output.progress, video.uploaded, video.deleted, app.created, and app.updated. Subscribe to "*" to receive all of them (including ones added later). An unrecognized future type still verifies; its event.data is left as a plain dict.

Configuration

Transcodely(
    api_key,                    # required
    base_url=None,              # default: https://api.transcodely.com
    timeout=30.0,               # seconds
    max_retries=3,
    api_version=None,           # override the pinned API version
    default_headers=None,       # dict, sent on every request
    http_client=None,           # custom httpx.Client
    logger=None,                # callable(LogEvent)
)

Request IDs

client.jobs.get("job_x")
print(client.last_request_id)  # "req_*"

Errors also carry the request ID via err.request_id for log correlation.

Versioning

The SDK follows semver, starting at 0.1.0. Breaking changes are allowed on minor bumps until 1.0.0. Each release pins a specific calendar-versioned API (Transcodely.API_VERSION) and sends Transcodely-Version on every request.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

transcodely-0.3.1.tar.gz (112.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

transcodely-0.3.1-py3-none-any.whl (158.5 kB view details)

Uploaded Python 3

File details

Details for the file transcodely-0.3.1.tar.gz.

File metadata

  • Download URL: transcodely-0.3.1.tar.gz
  • Upload date:
  • Size: 112.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for transcodely-0.3.1.tar.gz
Algorithm Hash digest
SHA256 61896457cba2ffdf0bea2c44ae5a1b99dc04a518f022eea594503a494b5b3746
MD5 16c0a9e5007e65c679513e3a94fd6712
BLAKE2b-256 7f3de711ea2d3f8643ab831839b3987ea6c52733f030d2e6e8782ddaa90322f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for transcodely-0.3.1.tar.gz:

Publisher: release.yml on transcodely/transcodely-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file transcodely-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: transcodely-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 158.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for transcodely-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d339644857e993ab4cba6e5895ad18d0dcfa03597ea64d82d3ee80561cb17547
MD5 bb7af06727aa0e32a93cd2a770027ef1
BLAKE2b-256 844b01aadcc5c14f2032182014ed2387e92689306a5854d65c8517d104b7acb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for transcodely-0.3.1-py3-none-any.whl:

Publisher: release.yml on transcodely/transcodely-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page