Sign in Get started

Async Client

pictograph.AsyncClient — the asyncio twin of Client. Same resources, same auth, every I/O method a coroutine, HTTP/2 connection pooling.

View as Markdown

pictograph.AsyncClient is the asyncio counterpart of Client. It exposes the exact same resource surface — every I/O method is a coroutine you await, and every iter(...) accessor returns an async pager you consume with async for. It runs over HTTP/2 on a single shared connection pool, so concurrent requests multiplex efficiently — ideal for fanning work out with asyncio.gather.

Credential and configuration resolution is identical to the sync client (explicit kwargs, then PICTOGRAPH_* environment variables, then defaults), and retries, idempotency keys, typed errors, streaming downloads, and the poll helpers all behave the same way.

Quick start

import asyncio
from pictograph import AsyncClient


async def main() -> None:
    async with AsyncClient(api_key="pk_live_...") as client:
        # await any resource method
        datasets = await client.datasets.list(limit=5)

        # async-for the auto-paging iterators
        async for img in client.images.iter(datasets[0].id, folder_path="/train"):
            print(img.filename, img.annotation_count)


asyncio.run(main())

Use it as an async context manager to guarantee socket cleanup, or call await client.aclose() explicitly when you are done.

Concurrent requests

Because the client multiplexes over one HTTP/2 pool, asyncio.gather runs many calls concurrently without spawning threads:

import asyncio
from pictograph import AsyncClient


async def main() -> None:
    async with AsyncClient() as client:
        datasets = await client.datasets.list(limit=20)
        # Fetch health insights for every dataset at once.
        reports = await asyncio.gather(
            *(client.datasets.insights(d.name) for d in datasets)
        )
        for d, health in zip(datasets, reports):
            print(d.name, health.total_annotations)


asyncio.run(main())

Long-running jobs

The poll helpers are coroutines too — await them (or pass wait=False to fire-and-forget and poll yourself):

async with AsyncClient() as client:
    # Blocks (asynchronously) until the export finishes.
    export = await client.exports.create("road-signs", "nightly", format="coco")

    # Or start a training run and await completion.
    run = await client.training.create(
        "road-signs", "nightly", pipeline_type="yolox", name="detector", wait=True
    )
    print(run.status)

Resources

AsyncClient wires the same resources as Client:

datasets, images, annotations, exports, training, models, deployments, credits, organizations, projects, folders, batch, search, auto_annotate, video, connectors, api_keys, webhooks, workflows.

Each is the async twin of the resource documented elsewhere in these docs — the method names, arguments, and return types match one-to-one; only the await differs. Downloads stream to disk exactly as they do on the sync client, and iter(...) returns an async pager:

async with AsyncClient() as client:
    # Materialize a full page set, or async-for lazily.
    all_models = await client.models.iter().all()
    async for run in client.training.iter(status="completed"):
        print(run.name)

For CPU-bound local inference (get_model(...).predict(...)) the sync client is still the right tool — ONNX inference does not benefit from asyncio.

Copied to clipboard