picx-ai
Official Python client for the PicX image and video generation API.
- Sync (
PicX) and async (AsyncPicX) clients with the same surface - Typed results and a typed exception hierarchy; ships
py.typed, passesmypy --strict - Automatic retries with exponential backoff + jitter,
Retry-Afteraware Idempotency-Keysupport on the calls that spend credits- One runtime dependency:
httpx
This is not the CLI. This package is a library you install into your own application. The
admin-cli/project in this organisation builds the internalpicx-admincommand line binary and is unrelated to this SDK.
Install
pip install picx-ai
# or
uv add picx-ai
Requires Python 3.9+.
Quickstart
import os
from picx import PicX
picx = PicX(os.environ["PICX_API_KEY"])
job = picx.video.create(prompt="sneaker on marble, slow orbit", duration=12)
asset = job.wait()
print(asset.url)
PicX() falls back to the PICX_API_KEY environment variable when no key is
passed, so PicX() works once the variable is set. Use it as a context manager
to close the connection pool deterministically:
with PicX() as picx:
asset = picx.images.generate("a red sneaker on white marble", size="2K", aspect_ratio="1:1")
print(asset.url, asset.credits_used)
Configuration
picx = PicX(
api_key="pxsk_...", # defaults to os.environ["PICX_API_KEY"]
base_url="https://api.picxstudio.com/v1", # or the PICX_BASE_URL env var
timeout=60.0, # seconds, per request
max_retries=2, # retries for 429 / 5xx / connection errors
)
API
| Call | Endpoint | Notes |
|---|---|---|
picx.images.generate(prompt, model=…, size=…, aspect_ratio=…) |
POST /images/generate |
scope images:generate; size is 1K/2K/4K, aspect_ratio like 16:9 |
picx.images.edit(instruction, image_urls, model=…, size=…) |
POST /images/edit |
scope images:edit; 1-5 image URLs |
picx.video.create(prompt=…, duration=…, resolution=…, sound=…, …) |
POST /videos/generate |
scope videos:generate; returns 202 and a job to poll |
picx.generations.get(id) |
GET /generations/{id} |
raises NotFoundError on 404 |
picx.models.list(type="image") |
GET /models |
public, no API key needed |
picx.account.usage(period=30) |
GET /account/usage |
|
picx.account.me() |
GET /account/me |
picx.images is also available as picx.image, and picx.video as picx.videos.
Images
asset = picx.images.generate("a red sneaker on white marble", size="2K", aspect_ratio="1:1")
asset.id, asset.url, asset.model, asset.size, asset.aspect_ratio, asset.credits_used
edited = picx.images.edit("put it on a wet street at night", [asset.url], size="4K")
Videos (202 Accepted, then poll)
POST /videos/generate is asynchronous server-side, so create() returns a job:
job = picx.video.create(
prompt="sneaker on marble, slow orbit",
duration=12, # server default 5
resolution="720p", # server default "720p"; must be priced for the model
sound=True, # server default True
aspect_ratio="16:9",
mode="image", # "text" | "image" | "reference"
image_url="https://…/ref.png",
callback_url="https://example.com/webhook",
)
job.id, job.status # "gen_…", "queued"
generation = job.wait(timeout=900, poll_interval=5)
print(generation.url) # alias for .output_url
wait() polls GET /generations/{id} until a terminal status
(succeeded, completed, failed, error, cancelled, canceled).
It raises JobFailedError on a failed generation — pass
raise_on_failure=False to get the Generation back instead — and
JobTimeoutError if the timeout elapses first. job.refresh() polls once.
Anything left unset is omitted from the request body so the server applies its
own defaults (model fal-ai/bytedance/seedance/v2, duration 5, resolution
720p, sound on).
Models, usage, account
for model in picx.models.list(type="video"):
print(model.id, model.name, model.credits)
usage = picx.account.usage(period=30)
print(usage.total_requests, usage.credits_used, usage.total_cost_usd, usage.model_breakdown)
me = picx.account.me()
print(me.email, me.is_active, me.credits)
GET /models is the only public endpoint, so PicX(api_key=None).models.list()
works without credentials.
Every result object also keeps the untouched response on .raw, so new API
fields are reachable before the SDK models them.
Async
import asyncio, os
from picx import AsyncPicX
async def main() -> None:
async with AsyncPicX(os.environ["PICX_API_KEY"]) as picx:
job = await picx.video.create(prompt="sneaker on marble, slow orbit", duration=12)
asset = await job.wait()
print(asset.url)
image = await picx.images.generate("a red sneaker on white marble")
print(image.url)
asyncio.run(main())
The async surface mirrors the sync one method for method — only await and
aclose()/async with differ. A parity test enforces this.
Error handling
from picx import (
PicXError, # base class: .status_code, .request_id, .body, .message
ValidationError, # 400 / 422, and invalid arguments caught locally
AuthenticationError, # 401, or no API key configured
PermissionDeniedError, # 403, the key lacks the required scope
NotFoundError, # 404
RateLimitError, # 429, exposes .retry_after
ServerError, # 5xx
APIConnectionError, # DNS/TLS/socket failure
APITimeoutError, # subclass of APIConnectionError
JobFailedError, # a generation ended failed/cancelled
JobTimeoutError, # wait() timed out, generation still running
)
try:
asset = picx.images.generate("a red sneaker")
except RateLimitError as exc:
print("slow down for", exc.retry_after, "seconds")
except PermissionDeniedError:
print("this key is missing the images:generate scope")
except PicXError as exc:
print(exc.status_code, exc.request_id, exc)
Errors are parsed from both response shapes the API uses — FastAPI's
{"detail": …} (including the 422 list form) and {"error": …, "detail": …}.
Retries and idempotency
Retries apply to 429, 5xx and connection/timeout failures only — never to
other 4xx. Backoff is exponential with full jitter and honours Retry-After
(capped at 60s). max_retries defaults to 2; set max_retries=0 to disable.
A POST is not replayed unless you supply an idempotency key, because
replaying it could charge twice:
import uuid
asset = picx.images.generate("a red sneaker", idempotency_key=str(uuid.uuid4()))
The key is sent as the Idempotency-Key header, which the backend honours on
calls that spend credits. idempotency_key is available on
images.generate, images.edit and video.create.
API keys are never logged
The key is redacted from repr()/str() of the client and from every exception
message and response body (anything matching pxsk_… becomes
pxsk_***REDACTED***). Read it back deliberately with picx.api_key if you
really need it. This is covered by tests.
Examples
export PICX_API_KEY=pxsk_...
python examples/generate_image.py "a red sneaker on white marble"
python examples/generate_video.py "sneaker on marble, slow orbit"
python examples/generate_video.py --async
Development
uv venv --python 3.13
uv pip install -e ".[dev]"
uv run mypy
uv run pytest
The test suite runs entirely against httpx.MockTransport — no network access,
no API key required.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file picx_ai-0.2.0.tar.gz.
File metadata
- Download URL: picx_ai-0.2.0.tar.gz
- Upload date:
- Size: 31.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dca64fb09f9370b1119078229920177824bf1821a98bd8b67bae405ba422d778
|
|
| MD5 |
ba8ef3a0794f4ebb000e89868128a782
|
|
| BLAKE2b-256 |
7326eb3f230de109cc87e01c440998e6df2be3a4fb946f4179ed8024556ede69
|
Provenance
The following attestation bundles were made for picx_ai-0.2.0.tar.gz:
Publisher:
publish.yml on Type-Think-AI/picx-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
picx_ai-0.2.0.tar.gz -
Subject digest:
dca64fb09f9370b1119078229920177824bf1821a98bd8b67bae405ba422d778 - Sigstore transparency entry: 2495898928
- Sigstore integration time:
-
Permalink:
Type-Think-AI/picx-sdk-python@5d785b4d719b9b3a33f0d0d645d31bd13bec7b30 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Type-Think-AI
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5d785b4d719b9b3a33f0d0d645d31bd13bec7b30 -
Trigger Event:
push
-
Statement type:
File details
Details for the file picx_ai-0.2.0-py3-none-any.whl.
File metadata
- Download URL: picx_ai-0.2.0-py3-none-any.whl
- Upload date:
- Size: 28.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca0aa8904732407a1e6b73cd41a9664df730b9600ca8acf92ca23a99b21e2d0c
|
|
| MD5 |
33fd690e01e7bb47879743f16d87bb20
|
|
| BLAKE2b-256 |
8ae517817305ce139c2e5892016e9651e5cfa324d4e5818c7efafd79fea0e540
|
Provenance
The following attestation bundles were made for picx_ai-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on Type-Think-AI/picx-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
picx_ai-0.2.0-py3-none-any.whl -
Subject digest:
ca0aa8904732407a1e6b73cd41a9664df730b9600ca8acf92ca23a99b21e2d0c - Sigstore transparency entry: 2495899074
- Sigstore integration time:
-
Permalink:
Type-Think-AI/picx-sdk-python@5d785b4d719b9b3a33f0d0d645d31bd13bec7b30 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Type-Think-AI
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5d785b4d719b9b3a33f0d0d645d31bd13bec7b30 -
Trigger Event:
push
-
Statement type: