Skip to main content

apulodi — official Python SDK for APULODI

Typed, dependency-light Python client for the APULODI file storage API: direct-to-storage uploads, automatic multipart for big files, transformations, folders, usage, and signed webhooks. Mirrors the official TypeScript SDK (@apulodi/sdk) endpoint-for-endpoint.

Installation

pip install apulodi

Requires Python 3.9+. The only runtime dependency is httpx.

Server-side only: your API key is a secret credential — never ship it to browsers, never commit it.

Quickstart

from apulodi import Apulodi

apulodi = Apulodi(api_key="apk_test_...")  # or Apulodi(api_key=..., base_url="http://localhost:3000")

# One call performs the full flow: initiate → direct-to-storage PUT → complete.
file = apulodi.files.upload(
    open("avatar.jpg", "rb"),          # bytes / bytearray / binary file object
    file_name="avatar.jpg",
    content_type="image/jpeg",
    path="users/avatars",              # logical folder, created automatically
    metadata={"source": "my-app"},
)
print(file["id"], file["status"])      # file_... uploaded

Files

# List with filters (cursor pagination envelope).
page = apulodi.files.list({"limit": 20, "path": "users", "search": "avatar"})

# Iterate over everything matching a filter, following cursors for you.
for file in apulodi.files.iterate({"contentType": "image/png"}):
    print(file["filename"], file["size"])

# Metadata / lifecycle.
file = apulodi.files.get("file_...")
apulodi.files.update("file_...", {"filename": "renamed.png", "visibility": "public"})
apulodi.files.copy("file_...", path="backups")
apulodi.files.delete("file_...")           # soft-delete + storage removal
apulodi.files.restore("file_...")          # while the object still exists

# Versioned content replacement.
apulodi.files.replace("file_...", new_bytes, content_type="image/jpeg")

# Short-lived signed download URL — bytes go straight from storage to the user.
signed = apulodi.files.download_url("file_...", expires_in_seconds=300)

Transformations (images, video, audio)

# Idempotent: identical params return the same variant.
variant = apulodi.files.transform("file_...", width=640, format="webp", quality=80)

# Processing is async — poll until ready (or wait for the file.processed webhook).
variant = apulodi.files.wait_for_variant("file_...", variant["id"], timeout=30)

if variant["status"] == "ready":
    signed = apulodi.files.variant_download_url("file_...", variant["id"], 3600)

Multipart (explicit control)

result = apulodi.uploads.create_multipart("big.mp4", "video/mp4", size=250_000_000)
session, file = result["session"], result["file"]

parts = []
with open("big.mp4", "rb") as f:
    for part_meta in session["parts"]:
        chunk = f.read(session["partSize"])
        parts.append(apulodi.uploads.upload_part(session, part_meta["partNumber"], chunk))

file = apulodi.uploads.complete(session["id"], parts)

apulodi.files.upload() switches to this flow automatically above 8 MiB — and aborts the session cleanly if a part fails.

Webhooks

from apulodi import verify_webhook_signature

# In your Flask/FastAPI/Django route — verify over the RAW body:
ok = verify_webhook_signature(secret, raw_body, request.headers["APULODI-Signature"])
if not ok:
    return "invalid signature", 401

Constants-time comparison, t=<unix>,v1=<hex> parsing, 5-minute replay window — byte-for-byte compatible with deliveries signed by the APULODI platform (and with the TypeScript SDK's verifier).

Manage endpoints programmatically:

result = apulodi.webhooks.create("https://example.com/hooks/apulodi")
secret = result["secret"]        # shown EXACTLY ONCE — store it now

apulodi.webhooks.list()
apulodi.webhooks.deliveries("wh_...")          # why didn't I get my event?
apulodi.webhooks.redeliver("wh_...", "dl_...")

Errors

Every non-2xx response, network failure and timeout raises ApulodiError:

from apulodi import ApulodiError

try:
    apulodi.files.get("file_missing")
except ApulodiError as e:
    e.status            # 404
    e.code              # "FILE_NOT_FOUND"
    e.details           # structured extras, if the API sent any
    e.is_client_error   # True — the request was wrong, retrying won't help
    e.is_server_error   # False

Messages are scrubbed of your API key before the error ever leaves the SDK.

Folders & usage

apulodi.folders.list("users")            # subfolders of a logical path
usage = apulodi.usage.get("2026-09")     # or apulodi.usage.get() for this month
usage["storage"]["usedBytes"]

Configuration

apulodi = Apulodi(
    api_key="apk_test_...",
    base_url="http://localhost:3000",  # default: https://app.apulodi.dev
    timeout=30,                        # seconds per request (storage PUTs get 4x)
)

Idempotent retries for POST /v1/files/upload:

apulodi.files.upload(data, "report.pdf", idempotency_key="order-1234")

Development

python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
pytest

License

MIT

Release files for apulodi 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for apulodi 0.1.0
File Size Uploaded
apulodi-0.1.0.tar.gz 18.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for apulodi 0.1.0
File Interpreter ABI Platform
apulodi-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.9 kB

Release files / apulodi-0.1.0.tar.gz

Download URL apulodi-0.1.0.tar.gz
Size 18.9 kB
Tags Source
SHA-256 checksum
How to use checksums
15ce2d345556ea64a0e38518fecda9da87053d24f048eedb1b290c803b057012
BLAKE2b-256 checksum
How to use checksums
3f9c4a358a3d4fa0a1045be5f7098409de3db03a8c9e793300f8d79abd74fe0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / apulodi-0.1.0-py3-none-any.whl

Download URL apulodi-0.1.0-py3-none-any.whl
Size 18.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9b51bdb7e691f21475fc06abc6d85e0b3551559ef1217050bdf6133cf3359233
BLAKE2b-256 checksum
How to use checksums
c41772ca2f42e37442b4369b1b024a4866f13ef118a13def4771f88d37a1fc3f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page