Skip to main content

ads_dev

The ads.dev API for Python: image, video and audio generation with 34 models on your brand, the brand kit and library, and the ads your competitors run. It has a sync client and an async one, and every answer is a typed pydantic model.

pip install ads_dev
from ads_dev import Sandstorm

sandstorm = Sandstorm()

generation = sandstorm.generations.run("nano-banana-pro", {"prompt": "A ceramic mug on a sunlit bench, steam rising", "aspect_ratio": "4:5"})
print(generation.output[0].url)
from ads_dev import AsyncSandstorm

async with AsyncSandstorm() as sandstorm:
    generation = await sandstorm.generations.run("nano-banana-2-lite", {"prompt": "Two lemons in a blue bowl"})

A key

Sandstorm() reads SANDSTORM_API_KEY. Make a key in Sandstorm under Settings → API keys, or in one command, which signs you in in the browser and writes the key to .env:

curl -fsSL https://ads.dev/login.mjs -o sandstorm-login.mjs && node sandstorm-login.mjs
Sandstorm(api_key="sk_live_…", brand="6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f", base_url="https://ads.dev", max_retries=2, timeout=60)

A key acts on the brand it was made on; brand names another brand of the same organization. base_url defaults to SANDSTORM_API_URL, then https://ads.dev. Pass http= an httpx.Client (or httpx.AsyncClient) of your own to set proxies or limits. Use the client as a context manager, or call close(), to release its connections.

Generations

run makes a generation and answers it once it has finished, holding each request on the API with Prefer: wait rather than polling. A generation that fails or is canceled raises GenerationFailed, which carries it.

still = sandstorm.generations.run("nano-banana-2-lite", {"prompt": "Two lemons in a blue bowl"})

clip = sandstorm.generations.run(
    "seedance-2.5/image-to-video",
    {"start_image": still.output[0].id, "prompt": "Slow push in as the light warms", "resolution": "720p"},
)

Every media input takes an asset id (asset_…) or a public https URL, so the output of one generation goes straight into the next. ads_dev.types has a model for every model's input, such as CreateNanoBananaProInput, if you want an input checked before it is sent.

For work that runs long, queue it and come back to it, or take a webhook:

queued = sandstorm.generations.create("kling-3-pro/image-to-video", input, webhook_url="https://yourapp.com/hooks/sandstorm")
finished = sandstorm.generations.wait(queued.id)

sandstorm.generations.get(queued.id)
sandstorm.generations.cancel(queued.id)

A generation is queued, then running, then succeeded, failed or canceled; credits is what it was charged once it finished, and a failed one is free. Cancelling a queued generation costs nothing; one the model has already started stops where the model allows it, and otherwise finishes and is charged.

create sends an Idempotency-Key with every call and keeps it across its retries, so a retry never makes a second generation. Pass your own to make a call safe across process restarts: create(..., idempotency_key="campaign-42-hero").

Lists

A list walks every page when iterated, and first_page() answers one:

for asset in sandstorm.assets.list(type="image"):
    print(asset.id, asset.url)

page = sandstorm.generations.list(status="succeeded", limit=50).first_page()

With the async client, iterate with async for and await the first page.

Everything else

sandstorm.models.list(type="video")
sandstorm.models.get("nano-banana-pro/edit")

sandstorm.brand.get()
sandstorm.assets.import_url("https://yourcdn.com/hero.png", name="Hero shot")
sandstorm.assets.get("asset_9c1e77aa")
sandstorm.offers.list(query="cold brew")
sandstorm.offers.get("offer_0f8e2b6a4c1d4e7f9a3b5c7d9e1f2a4b")
sandstorm.characters.list(kind="subject")
sandstorm.voices.list(use_case="advertisement", limit=5)

advertiser = next(iter(sandstorm.advertisers.list(query="Liquid Death")))
sandstorm.ads.list(advertiser=advertiser.id, status="active")
sandstorm.ads.get("ad_5e1c9a3b")

Webhooks

A generation made with webhook_url is POSTed to it once it finishes, signed in Sandstorm-Signature. Verify the raw body before you trust it:

from ads_dev import verify_webhook

secret = sandstorm.webhooks.secret()

event = verify_webhook(secret, request.body, request.headers.get("sandstorm-signature"))
if event.type == "generation.succeeded":
    print(event.data.output)

It raises ValueError for a body that was changed, a signature from another secret and a delivery older than five minutes. An event's id is the same on every retry of it, so a handler can skip one it has seen.

Errors

A refusal raises SandstormError with the HTTP status, its type and code, the param at fault and the request_id to quote to support. Requests Sandstorm could not take (408, 429, 500, 502, 503, 504) and network failures are retried twice with backoff, after the Retry-After the API asks for.

from ads_dev import SandstormError

try:
    sandstorm.generations.create("nano-banana-pro", {"prompt": "A mug on a bench"})
except SandstormError as error:
    if error.code == "insufficient_credits":
        print("Add credits in Sandstorm")

Developing

uv sync
uv run pytest
uv run python scripts/generate.py
uv build

ads_dev/types.py is generated from ../sdk/openapi.json, the API's own document, and ads_dev/_sync/client.py from the async client with unasync; scripts/generate.py writes both, and the tests fail when either is stale. Build with uv 0.13 or newer.

Metadata

Release files for ads_dev 1.0.0

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

Source distribution (sdist)

Source distribution for ads_dev 1.0.0
File Size Uploaded Provenance
ads_dev-1.0.0.tar.gz 18.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ads_dev 1.0.0
File Interpreter ABI Platform
ads_dev-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 40.7 kB

Release files / ads_dev-1.0.0.tar.gz

Download URL ads_dev-1.0.0.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ab3363801045007246809205aaee0e9d6473ffb4c6e642d5a3f30c544504ca6c
BLAKE2b-256 checksum
How to use checksums
845ae155970f6e822efabd233b6d408c7f41c8fa026954c820a823c4333161ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / ads_dev-1.0.0-py3-none-any.whl

Download URL ads_dev-1.0.0-py3-none-any.whl
Size 22.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4dc3135b9302bac9ef33c3aec790ef979c042aadf03f356461a48c9590521b6
BLAKE2b-256 checksum
How to use checksums
12fd50cc3f4b4d9b53185745666e37f8ea16524c9c1056681c05ba21ab463e38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for ads_dev 1.0.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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