Skip to main content

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, passes mypy --strict
  • Automatic retries with exponential backoff + jitter, Retry-After aware
  • Idempotency-Key support 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 internal picx-admin command 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")

Managed uploads (assets)

images.edit and the video modes need public URLs — they can't read a local file. Upload it first:

asset = picx.assets.create("portrait.jpg", metadata={"source": "onboarding"})
print(asset.id, asset.url)

# Now use the URL in an edit or video call:
avatar = picx.images.edit(
    "turn this into a naive doodle avatar",
    image_urls=[asset.url],
)

file accepts a path (string or Path), an open binary file, or raw bytes:

asset = picx.assets.create(b"\x89PNG...", filename="photo.png", content_type="image/png")

List, get, and delete:

result = picx.assets.list(kind="image", limit=50)
for a in result:
    print(a.id, a.url)

single = picx.assets.get(asset.id)
picx.assets.delete(asset.id)  # soft delete — existing URLs keep working

The async client has the same surface:

async with AsyncPicX() as picx:
    asset = await picx.assets.create("portrait.jpg")

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

Metadata

Release files for picx-ai 0.3.1

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

Source distribution (sdist)

Source distribution for picx-ai 0.3.1
File Size Uploaded
picx_ai-0.3.1.tar.gz 39.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for picx-ai 0.3.1
File Interpreter ABI Platform
picx_ai-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 73.4 kB

Release files / picx_ai-0.3.1.tar.gz

Download URL picx_ai-0.3.1.tar.gz
Size 39.5 kB
Tags Source
SHA-256 checksum
How to use checksums
80c2f0cb075c33c20aac819364db7bba9b1edb8a50bf04cefadaa9cd63e056ee
BLAKE2b-256 checksum
How to use checksums
e06ffeff49718dffd2314c690326cbb11086c878cbb2726e70c9835f8ea6ad2c
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 Aug 25, 2026.

Transparency log

Release files / picx_ai-0.3.1-py3-none-any.whl

Download URL picx_ai-0.3.1-py3-none-any.whl
Size 34.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3528fe1dcaf9336225d0aeccf4a9c9e50db94f9f6e484a13e865a5dcca4efa0
BLAKE2b-256 checksum
How to use checksums
cce051ffd06f7370832d298bee22e8a8f2258fc4042e5b0e3a9489cc713aa8fa
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 Aug 25, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.1

2 release files

0.4.0

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

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