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")

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

picx_ai-0.2.0.tar.gz (31.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

picx_ai-0.2.0-py3-none-any.whl (28.1 kB view details)

Uploaded Python 3

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

Hashes for picx_ai-0.2.0.tar.gz
Algorithm Hash digest
SHA256 dca64fb09f9370b1119078229920177824bf1821a98bd8b67bae405ba422d778
MD5 ba8ef3a0794f4ebb000e89868128a782
BLAKE2b-256 7326eb3f230de109cc87e01c440998e6df2be3a4fb946f4179ed8024556ede69

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for picx_ai-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ca0aa8904732407a1e6b73cd41a9664df730b9600ca8acf92ca23a99b21e2d0c
MD5 33fd690e01e7bb47879743f16d87bb20
BLAKE2b-256 8ae517817305ce139c2e5892016e9651e5cfa324d4e5818c7efafd79fea0e540

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page