Python SDK
Official WAHooks SDK for Python
Installation
pip install wahooksRequires Python 3.8+. Uses httpx for HTTP requests.
Quick start
from wahooks import WAHooks
client = WAHooks(api_key="wh_...")
# List connections
connections = client.list_connections()
# Send a message
client.send_message(connection_id, chat_id="1234@s.whatsapp.net", text="Hello!")Configuration
client = WAHooks(
api_key="wh_...",
base_url="https://api.wahooks.com", # optional, defaults to production
)The client can be used as a context manager:
with WAHooks(api_key="wh_...") as client:
connections = client.list_connections()
# HTTP connection closed automaticallyAPI Reference
Connections
# List all active connections
connections = client.list_connections()
# Create a new connection
connection = client.create_connection()
# Get a scannable connection (reuses idle or creates new)
result = client.get_or_create_scannable_connection()
# result["id"], result["status"], result["qr"] (base64 PNG)
# Get connection details
connection = client.get_connection(connection_id)
# Delete a connection
client.delete_connection(connection_id)
# Restart a connection
connection = client.restart_connection(connection_id)
# Get QR code for linking
qr = client.get_qr(connection_id)
# qr["value"] = base64 PNG
# Get recent chats
chats = client.get_chats(connection_id)
# Get WhatsApp profile info
profile = client.get_profile(connection_id)
# Send a text message
result = client.send_message(connection_id, chat_id="1234@s.whatsapp.net", text="Hello!")
# Send an image (by URL)
client.send_image(connection_id, chat_id="1234@s.whatsapp.net", url="https://example.com/photo.jpg", caption="Check this out")
# Send an image (by base64)
client.send_image(connection_id, chat_id="1234@s.whatsapp.net", data="iVBORw0KGgo...", mimetype="image/png")
# Send a document
client.send_document(connection_id, chat_id="1234@s.whatsapp.net", url="https://example.com/report.pdf", filename="report.pdf")
# Send a video
client.send_video(connection_id, chat_id="1234@s.whatsapp.net", url="https://example.com/clip.mp4")
# Send audio
client.send_audio(connection_id, chat_id="1234@s.whatsapp.net", url="https://example.com/voice.ogg")
# Send a location
client.send_location(connection_id, chat_id="1234@s.whatsapp.net", latitude=37.7749, longitude=-122.4194, name="San Francisco")
# Send a contact card
client.send_contact(connection_id, chat_id="1234@s.whatsapp.net", contact_name="John Doe", contact_phone="1234567890")All send methods include human-like presence (read receipt, typing, delay) by default. To disable:
client.send_message(connection_id, chat_id="...", text="Hi", skip_presence=True)
client.send_image(connection_id, chat_id="...", url="...", skip_presence=True)Replies and Reactions
# Quote a specific message
client.send_message(connection_id, chat_id="...", text="Thanks!", reply_to="msg_id_here")
# React to a message with an emoji
client.react(connection_id, chat_id="...", message_id="msg_id_here", reaction="👍")
# Remove a reaction
client.react(connection_id, chat_id="...", message_id="msg_id_here", reaction="")Presence
# Manual presence control
client.mark_read(connection_id, chat_id="...") # send read receipt
client.start_typing(connection_id, chat_id="...") # show "typing..."
client.stop_typing(connection_id, chat_id="...") # clear typing indicatorWebhooks
# List webhooks for a connection
webhooks = client.list_webhooks(connection_id)
# Create a webhook
webhook = client.create_webhook(
connection_id,
url="https://example.com/hook",
events=["message", "message.any"], # optional, defaults to ["*"]
)
# Update a webhook
updated = client.update_webhook(webhook_id, url="https://example.com/new", active=False)
# Delete a webhook
client.delete_webhook(webhook_id)
# Get delivery logs
logs = client.get_webhook_logs(webhook_id)
# Send a test event
result = client.test_webhook(webhook_id)Real-time Events
Listen for WhatsApp events in real-time via WebSocket:
pip install wahooks[realtime] # includes websocket-clientdef on_event(event):
print(event["event"]) # "message"
print(event["connectionId"]) # connection UUID
print(event["payload"]) # WAHA event payload
stream = client.listen(on_event=on_event)
stream.run_forever() # blocks, auto-reconnects
# To stop from another thread:
# stream.close()Handling media messages
When a message includes media (image, video, document, audio), the payload contains hasMedia: True and a media dict with a proxied URL:
import httpx
def on_event(event):
if event["event"] != "message":
return
payload = event["payload"]
print(payload.get("body")) # caption or text
if payload.get("hasMedia"):
media = payload["media"]
print(media["mimetype"]) # e.g. "image/jpeg"
print(media["url"]) # proxied URL
# Download the media file (requires auth)
resp = httpx.get(
media["url"],
headers={"Authorization": f"Bearer {api_key}"},
)
resp.raise_for_status()
with open("downloaded_file", "wb") as f:
f.write(resp.content)The media["url"] points to the WAHooks media proxy (/connections/:id/media/:filename), which streams the file from the internal WAHA worker. It requires the same Authorization header as other API calls.
API Tokens
# List active tokens
tokens = client.list_tokens()
# Create a new token (raw token shown once)
token = client.create_token("my-token")
print(token["token"]) # "wh_a1b2c3..."
# Revoke a token
client.revoke_token(token_id)Billing
# Get billing status (subscription, slots)
status = client.get_billing_status()
print(status["slots"]) # {"paid": 5, "used": 3, "available": 2}
# Programmatically scale connection slots (charges immediately)
result = client.set_slots(10)
print(result)
# {"slots": 10, "status": "upgraded", "proratedAmount": 1.25, "currency": "usd"}set_slots charges (or credits) the prorated difference immediately against the card on file. Requires an active subscription — call checkout first to set up billing. Maximum 100 slots per account by default; contact support for higher limits.
Error handling
The SDK raises WAHooksError for non-2xx responses:
from wahooks import WAHooks, WAHooksError
try:
client.get_connection("nonexistent-id")
except WAHooksError as e:
print(e) # "Connection not found"
print(e.status_code) # 404
print(e.body) # full error response dict