SDKs
Python SDK
The official dhanu PyPI package, install it, generate images and text, and handle errors with DhanuError.
The dhanu package wraps the public API for Python. It has no dependencies,
it uses only the standard library, and needs Python 3.8 or newer.
Install
pip install dhanu
Set up the client
import os
from dhanu import Dhanu
# Server-side only, never ship your secret key to a client.
dhanu = Dhanu(os.environ["DHANU_API_KEY"])
The key is always passed explicitly. There is no base_url argument: the base
URL comes from DHANU_BASE_URL if set, and otherwise defaults to
https://api.dhanu.ai. Point DHANU_BASE_URL at http://localhost:3000 for
local development.
Generate an image
import base64
image = dhanu.images.generate(
prompt="a red bicycle on a white background",
aspect_ratio="1:1", # "1:1" | "3:4" | "9:16" | "4:3" | "16:9"
)
with open("out.png", "wb") as handle:
handle.write(base64.b64decode(image.b64))
Returns GeneratedImage(b64, mime_type, model, usage, cost). Nothing is stored
server-side, see Generate an image.
Generate text
reply = dhanu.text.generate(
prompt="Write a haiku about Kathmandu",
system="You are a concise poet.",
max_tokens=200,
)
print(reply.text)
Returns GeneratedText(text, model, usage, cost).
max_tokens caps total output tokens and the model’s own reasoning counts against
it, so a very small value can return empty text. Leave it unset or generous unless
you want a hard limit.
Verify a key
me = dhanu.me()
me.app # App(id, name) when the key covers exactly one project, else None
me.projects # [App(id, name), ...], every project the key can reach
me.key.name # the key's name
me.scope # ApiScope(level, packages, operations)
Keys that cover several projects
When a key covers more than one project, pass project_id on each generation to
say which project to meter against. Without it the call fails with
project_id_required (422):
project = dhanu.me().projects[0]
image = dhanu.images.generate(prompt="…", project_id=project.id)
reply = dhanu.text.generate(prompt="…", project_id=project.id)
A key is also scoped to part of the API. me.scope reports what it may call;
anything outside that fails with api_key_operation_scope (403) before it runs,
so a narrowed key never spends credits on a call it was not meant to make.
Typed requests and responses
dhanu.api_types holds a TypedDict for every request body, query and response,
generated from the API contract, so a type checker can read the wire shape while the
resource methods keep returning dataclasses:
from dhanu import api_types
body: api_types.TasksCreateRequest = {"input": "Summarise the week", "fileIds": file_ids}
task = dhanu.tasks.create(**{"input": body["input"]})
Retries and timeouts
Every call is retried up to twice on a network error, a timeout, 408, 429 or a
5xx, waiting for Retry-After when the server sends one and backing off
exponentially (0.5 s, 1 s, 2 s… capped at 8 s) otherwise. JSON POSTs carry a
generated Idempotency-Key on every attempt, so a retry can never run the same call
twice. Each attempt times out after 60 s. Tune or switch it off per client:
dhanu = Dhanu(os.environ["DHANU_API_KEY"], max_retries=0, timeout_seconds=10)
A request that never reached the server raises DhanuError with status == 0 and
code == "network_error" or "timeout".
Handle errors
Every failure raises DhanuError with a machine-readable code, the HTTP
status, and the request_id to quote in support reports. The human-readable
message is the exception’s own string.
from dhanu import DhanuError
try:
dhanu.images.generate(prompt="…")
except DhanuError as err:
if err.code == "rate_limited":
... # back off and retry, see /developer/reference/rate-limits
print(err.code, err.status, str(err), err.request_id)
The complete list of codes is on Errors.