Python SDK 2.0: asyncio support and Python 3.12+
Version 2.0.0 of our Python SDK, pytransloadit, adds AsyncTransloadit, an asyncio client that
sits next to the synchronous one you already use. It also raises the minimum Python version to 3.12.
Below, we show what the async client is good for, and what to check before you upgrade from 1.x.
Why an async client
The 1.x client talks to our API through Requests, which blocks. In a FastAPI or aiohttp app, every call to it holds up the event loop. While one request waits for an Assembly to finish, other requests on that worker wait too, unless you move the call to a thread pool yourself.
AsyncTransloadit sends its API calls through aiohttp and polls Assembly status with
asyncio.sleep, so you can await an Assembly like any other I/O. That also makes it easy to run
several Assemblies at once from a script or a background worker.
Process files concurrently
Install 2.0 on Python 3.12 or newer:
python -m pip install --upgrade 'pytransloadit>=2,<3'
This script resizes three photos in parallel with
🤖 /image/resize, sharing one client and one HTTP session. It reads
your credentials from TRANSLOADIT_KEY and TRANSLOADIT_SECRET:
import asyncio
import os
from transloadit.async_client import AsyncTransloadit
async def resize(client, path):
assembly = client.new_assembly()
thumb = {"use": ":original", "width": 400}
assembly.add_step("thumb", "/image/resize", thumb)
with open(path, "rb") as upload:
assembly.add_file(upload)
response = await assembly.create(wait=True, resumable=False)
if "error" in response.data:
raise RuntimeError(f"{path}: {response.data['error']}")
return response.data["results"]["thumb"][0]["ssl_url"]
async def main():
paths = ["chameleon.jpg", "prinsengracht.jpg", "desert.jpg"]
key = os.environ["TRANSLOADIT_KEY"]
secret = os.environ["TRANSLOADIT_SECRET"]
async with AsyncTransloadit(key, secret) as client:
urls = await asyncio.gather(*(resize(client, p) for p in paths))
print("\n".join(urls))
asyncio.run(main())
Download the sample photos and run it:
for photo in chameleon prinsengracht desert; do
curl -fsSLo "$photo.jpg" \
"https://demos.transloadit.com/inputs/$photo.jpg"
done
python resize_many.py
The script prints one URL per thumbnail. From our machine, the three Assemblies (25 MB of photos) finished in about 3 seconds, against about 8 seconds for the same loop with the sync client.
wait=True returns once the Assembly is done. API errors still come back in the response, which is
why the script checks for error. resumable=False sends each file in the creation request. Leave
it at its default of True for large files: resumable uploads go over tus, which the SDK runs in
worker threads, so the event loop stays responsive while they upload.
Use it in FastAPI
Install FastAPI, Uvicorn, and the multipart parser FastAPI needs for uploads:
python -m pip install fastapi uvicorn python-multipart
Create the client once per app and share it between requests:
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, UploadFile
from transloadit.async_client import AsyncTransloadit
@asynccontextmanager
async def lifespan(app: FastAPI):
key = os.environ["TRANSLOADIT_KEY"]
secret = os.environ["TRANSLOADIT_SECRET"]
async with AsyncTransloadit(key, secret) as client:
app.state.transloadit = client
yield
app = FastAPI(lifespan=lifespan)
@app.post("/thumbnails")
async def create_thumbnail(file: UploadFile):
assembly = app.state.transloadit.new_assembly()
thumb = {"use": ":original", "width": 400}
assembly.add_step("thumb", "/image/resize", thumb)
assembly.add_file(file.file)
response = await assembly.create(wait=True, resumable=False)
thumbs = response.data.get("results", {}).get("thumb")
if "error" in response.data or not thumbs:
raise HTTPException(502, "Could not create a thumbnail")
return {"url": thumbs[0]["ssl_url"]}
uvicorn app:app
curl -fsSL -F file=@chameleon.jpg http://127.0.0.1:8000/thumbnails
We sent this endpoint three uploads at the same time, and one Uvicorn worker answered all three in
under 4 seconds. Waiting for the result inside the request suits quick image jobs. For video and
other long-running work, create the Assembly without wait=True and pass a notify_url to
new_assembly(), so Transloadit calls your app when the job is done.
What the async client covers
AsyncTransloadit mirrors the sync client: creating, fetching, listing, and canceling Assemblies,
managing Templates, fetching monthly billing, and signing
Smart CDN URLs. Await the methods that hit the network.
new_assembly(), new_template(), and get_signed_smart_cdn_url() stay synchronous.
Use the client with async with, or call await client.aclose() when you are done. You can pass
your own aiohttp session with session=. Resumable transfers do not use that session, though, so
its proxy and TLS settings do not apply to them. Configure those through the Requests environment
instead.
Upgrade from 1.x
Your synchronous code keeps working: from transloadit import client and its methods are unchanged
in 2.0. What you need to check:
- Python 3.12 or newer. Upgrade your development, CI, and deployment interpreters first. On
Python 3.9 to 3.11, pip keeps installing 1.0.4, the latest 1.x release. Pin
pytransloadit<2to make that explicit. - Dependency floors. 2.0 requires
requests>=2.33,<3andurllib3>=2.7,<3. Loosen older pins in your constraints and regenerate your lockfile. - Redirects. HTTP redirects are returned to you instead of followed.
- Input validation. Invalid service URLs and empty Template IDs raise
ValueError. - Templates. When you combine a
templateoption withadd_step(), pass thetemplatevalue as a dict, not as a JSON string. - Responses. A non-JSON response puts text or bytes in
Response.data, and an empty body gives an empty string. - Polling. With
wait=True, running out of retries on rate-limited status checks raisesAssemblyPollingError. The Assembly may still be running: use the error’sassembly_urlto poll again or cancel it. - Assembly URLs. Status and cancel requests only go to trusted Assembly URLs, and the default client rejects other hosts.
- Smart CDN. Boolean parameters are signed as
trueandfalse, matching our Node.js reference implementation. Workspace slugs must be DNS-safe, andauth_key,exp, andsigare reserved query keys.
The README and the 2.0.0 changelog have the full upgrade notes.
Get started
python -m pip install --upgrade 'pytransloadit>=2,<3'
Find the package on PyPI, runnable sync and async scripts in the examples folder, and the API reference on Read the Docs. Our Python SDK docs cover the basics. No account yet? Sign up for free and you can run the examples above in a few minutes. On the free Community plan, image results are watermarked; our FAQ explains why. Questions or problems with the upgrade go to the python-sdk issue tracker.
