Client¶
Client is one method per endpoint, plus the plumbing: authentication, retries, rate limiting, local validation and typed responses.
from whagent import Client
with Client("<ACCESS_TOKEN>") as client:
client.send_text("Hello!") # an agent has exactly one recipient
Construction¶
Client(
token,
base_url="https://api.whatsapp.com/agent/v1",
timeout=30.0,
max_retries=3,
backoff_base=0.5,
backoff_max=30.0,
rate_limit=True,
retry_send_on_server_error=False,
validate=True,
auto_discover=True,
session=None,
user_agent=None,
)
| Argument | What it does |
|---|---|
token |
The agent's API token. Sent as Authorization: Bearer <token>. |
base_url |
Override the API root — useful for tests and proxies. |
timeout |
Read timeout in seconds. Polls extend it past the long-poll window automatically, so an ordinary delay never looks like a failure. |
max_retries |
Retries after a retryable failure. See Errors → retries. |
backoff_base, backoff_max |
Exponential backoff bounds, in seconds. Jitter is added. |
rate_limit |
Pace requests to stay inside the documented caps instead of collecting 429s. See Rate limits. |
retry_send_on_server_error |
Retry a send after a 500 or a dropped connection. Off by default — those leave delivery unknown, so a retry may send twice. |
validate |
Local checks on recipients, length caps, MIME types and media sizes before a request leaves. |
auto_discover |
Let the first send find the recipient by itself, on a client that has never polled. Never runs once the client has polled, so it cannot replace a running poll loop. |
session |
Bring your own requests.Session for connection reuse, proxies or custom TLS. |
user_agent |
Override the User-Agent header. |
Methods¶
Messages — POST /messages¶
send_text([to,] body, preview_url=False, reply_to=None) |
→ SendResult |
send_image(to=None, media_id=\| file=, mime_type=None, caption=None, reply_to=None) |
|
send_video(...), send_audio(...), send_sticker(...) |
|
send_document(..., filename=None) |
|
send_message(to, type, payload, reply_to=None) |
any payload shape; to=None uses the known recipient |
reply(message, body, **kwargs) |
text back to a Message's sender, quoting it |
The recipient¶
recipient |
the creator's identifier, as last seen |
discover_recipient(refresh=False) |
look it up explicitly, with one poll |
Because an agent may only message its creator, to is optional on every send — see the recipient.
Updates — GET /updates¶
get_updates(offset=None, limit=50, timeout=15) |
→ Update or None on a 204 |
poll_updates(offset=None, limit=50, timeout=15, on_offset=None, max_polls=None, stop=None) |
generator of non-empty Updates, tracking the offset |
Receipts — POST /statuses¶
mark_read(message_id, typing=False) |
→ bool |
send_typing(message_id) |
mark read and show the indicator |
Media — POST / GET / DELETE /media¶
upload_media(file, mime_type=None, filename=None) |
→ media id |
get_media(media_id) |
→ Media metadata |
download_media(media_id, dest=None) |
→ bytes, or the destination it wrote to |
download_url(url, dest=None) |
fetch a media URL directly, authenticated |
delete_media(media_id) |
→ bool |
Lifecycle¶
Client opens a requests.Session and reuses the connection. Close it when you're done — or use the context manager, which closes only a session the client created:
A session you passed in is left open for you to manage.
Threads¶
A Client is safe to share across threads: requests.Session is thread-safe for this usage, and the rate limiter takes a lock. Two rules from the platform still apply:
- One poll at a time per agent — a second concurrent
get_updatesmakes the first fail withPollReplacedError. - No concurrent sends to the same recipient — ordering between them is not guaranteed.
See Recipes → handling messages in a worker thread.
Turning the helpers off¶
The library validates before sending because a round trip to learn that a caption is 1025 characters is a slow way to find out. If you would rather see exactly what the API says: