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)
| File | Size | Uploaded | |
|---|---|---|---|
| apulodi-0.1.0.tar.gz | 18.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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