The Python framework for building WhatsApp bots.
Fast. Typed. Production-ready. From prototype to production in minutes.
PyWa is a comprehensive, fully-typed Python framework for the WhatsApp Cloud API. It handles everything — sending messages, receiving webhooks, building interactive flows, managing templates, handling calls — so you can focus on building your bot, not wrestling with the API.
pip install -U "pywa[server]"| 💬 Rich Messaging | Text, images, videos, documents, audio, stickers, locations, contacts, buttons, lists & more |
| 📩 Real-Time Webhooks | Messages, button clicks, delivery receipts, read receipts, reactions & account updates |
| 🔔 Listeners | Chain conversations naturally — wait for replies, clicks, or reactions inline |
| 📄 Templates | Create, send, and manage WhatsApp message templates with full parameter support |
| ♻️ Flows | Build rich, multi-screen interactive WhatsApp Flows in pure Python |
| 📞 Calls | Handle incoming and outgoing WhatsApp call events |
| ⚡ Async Support | Full async/await API via pywa_async — same interface, zero friction |
| 🔌 Server Integrations | Built-in webhook server, or plug into your existing FastAPI / Flask app |
| 🛡️ Type Safety | Fully typed - full autocomplete and static analysis everywhere |
| 🔬 Smart Filters | Composable filters with logical operators for precise update routing |
| 🧰 CLI Tools | pywa dev for local development, pywa run for production |
# main.py
from pywa import WhatsApp, filters, types
wa = WhatsApp(
phone_id="1234567890",
token="EAA...",
app_id=1234567890,
app_secret="********",
callback_url="https://your-domain.ngrok-free.app",
verify_token="my-verify-token",
)
@wa.on_message(filters.text)
def echo(_: WhatsApp, msg: types.Message):
msg.reply(f"You said: {msg.text}")Start the webhook server:
pywa dev # Local development
pywa run # Productionfrom pywa import WhatsApp, types
wa = WhatsApp(phone_id="1234567890", token="EAA...")
# Text with interactive buttons
wa.send_message(
to="9876543210",
text="How can I help you today?",
buttons=[
types.Button(title="📋 Menu", callback_data="menu"),
types.Button(title="💬 Support", callback_data="help"),
],
)
# Images, documents, audio — one-liners
wa.send_image(to="9876543210", image="https://example.com/photo.jpg", caption="Check this out!")
wa.send_document(to="9876543210", document="report.pdf")Handlers define entry points. Listeners let you continue the conversation naturally:
@wa.on_message(filters.command("start"))
def start(_: WhatsApp, msg: types.Message):
name = msg.reply("What's your name?").wait_for_reply(filters=filters.text).text
msg.reply(f"Nice to meet you, {name}!")from fastapi import FastAPI
from pywa import WhatsApp, filters, types
app = FastAPI()
wa = WhatsApp(..., server=app, webhook_endpoint="/webhook")
@wa.on_message(filters.text)
def echo(_: WhatsApp, msg: types.Message):
msg.reply(msg.text)
@app.get("/")
def health():
return {"status": "ok"}Same API. Just swap the import and add await:
from pywa_async import WhatsApp, filters, types
wa = WhatsApp(...)
@wa.on_message(filters.text)
async def hello(_: WhatsApp, msg: types.Message):
await msg.react("👋")
await msg.reply("Hello from async PyWa!")Create and send WhatsApp message templates with full parameter support:
from pywa import WhatsApp
from pywa.types.templates import *
wa = WhatsApp(..., waba_id=123456)
wa.create_template(
template=Template(
name="order_update",
category=TemplateCategory.MARKETING,
language=TemplateLanguage.ENGLISH_US,
parameter_format=ParamFormat.NAMED,
components=[
ht := HeaderText("Your order #{{order_id}} has shipped!", order_id="12345"),
bt := BodyText("Track it with code {{code}}.", code="ABC123"),
FooterText(text="Powered by PyWa"),
Buttons(buttons=[
url := URLButton(text="Track Order", url="https://example.com/track/{{1}}", example="12345"),
QuickReplyButton(text="Unsubscribe"),
]),
],
),
)
# Send it
wa.send_template(
to="9876543210",
name="order_update",
language=TemplateLanguage.ENGLISH_US,
params=[
ht.params(order_id="67890"),
bt.params(code="XYZ789"),
url.params(url_variable="67890", index=0),
],
)Build WhatsApp Flows — multi-screen interactive experiences — entirely in Python:
from pywa import WhatsApp, types
from pywa.types.flows import *
wa = WhatsApp(..., waba_id=123456)
my_flow = FlowJSON(
screens=[
Screen(
id="SIGNUP",
title="Join Our Newsletter",
layout=Layout(children=[
TextHeading(text="Subscribe for updates"),
name := TextInput(name="name", label="Name", input_type=InputType.TEXT),
email := TextInput(name="email", label="Email", input_type=InputType.EMAIL, required=True),
Footer(
label="Subscribe",
on_click_action=CompleteAction(
payload={"name": name.ref, "email": email.ref}
),
),
]),
)
]
)
wa.create_flow(name="newsletter_signup", categories=[FlowCategory.SIGN_UP], flow_json=my_flow, publish=True)
@wa.on_flow_completion
def on_signup(_: WhatsApp, flow: types.FlowCompletion):
flow.reply(text=f"Welcome, {flow.response['name']}! You're subscribed at {flow.response['email']}.")Beyond messaging, PyWa gives you full control over your WhatsApp Business resources:
from pywa import WhatsApp
wa = WhatsApp(phone_id="1234567890", token="EAA...", waba_id=123456)
# Business profile
profile = wa.get_business_profile()
wa.update_business_profile(about="Powered by PyWa", description="We build bots!", profile_picture="profile.jpg")
# Media management
media = wa.upload_media(media="photo.jpg")
media.stream(), media.download(), media.reupload(), media.delete()
# QR codes
qr = wa.create_qr_code(prefilled_message="Hi! I saw your QR code")
qr.qr_image_url, qr.code, qr.update(prefilled_message="Hello"), qr.delete()
# Usernames
wa.get_reserved_usernames()
wa.set_username(username="mybusiness")
wa.get_current_username()
# Groups
wa.create_group(subject="VIP Customers")
wa.get_groups()
wa.get_group_join_requests()
# Commerce
wa.get_commerce_settings()
wa.update_commerce_settings(is_catalog_visible=True, is_cart_enabled=
True)
# User management
wa.block_users(users=["9876543210"])
blocked = wa.get_blocked_users()See the Client guide for the full resource management API — templates, flows, media, QR codes, commerce, groups, calls, and more.
Building a platform on top of WhatsApp? PyWa supports multi-WABA management, phone number provisioning, and callback routing for Solution Partners and Tech Providers:
from pywa import WhatsApp, types, filters
wa = WhatsApp(phone_id="1234567890", token="EAA...", waba_id=123456)
@wa.on_message(filters.sent_to(phone_number_id=...))
def handle_message_for_specific_phone_number(wa: WhatsApp, msg: types.Message): ...
@wa.on_account_update(filters.account_restriction)
def handle_account_restriction(wa: WhatsApp, update: types.AccountUpdate): ...
@wa.on_message(filters.account_deleted)
def handle_account_deletion(wa: WhatsApp, update: types.AccountUpdate): ...
# Get all WABAs you manage
shared_wabas = wa.get_shared_business_accounts()
owned_wabas = wa.get_owned_business_accounts()
# Provision phone numbers on a WABA
phone = wa.create_phone_number(country_calling_code="1", phone_number="5551234567", verified_name="John Doe")
wa.request_verification_code(phone_id=phone.id, code_method="SMS")
wa.verify_phone_number(code="123456", phone_id=phone.id)
wa.register_phone_number(phone_id=phone.id)
# Route webhooks per-WABA or per-phone
wa.override_waba_callback_url(callback_url="https://your-platform.com/waba/123")
wa.override_phone_callback_url(callback_url="https://your-platform.com/phone/456")
# Migrate templates & flows between WABAs
wa.migrate_templates(source_waba_id=111111, destination_waba_id=222222)
wa.migrate_flows(source_waba_id=111111, destination_waba_id=222222, source_flow_names=["flow_1"])# Core (sending messages, no webhook server)
pip install -U pywa
# With built-in webhook server (recommended)
pip install -U "pywa[server]"
# For Flow encryption support
pip install -U "pywa[cryptography]"
# Everything
pip install -U "pywa[server,cryptography]"Requirements: Python 3.10+
Full documentation is available at pywa.readthedocs.io.
| Topic | Description |
|---|---|
| Getting Started | Setup, first bot, and key concepts |
| Client | Sending messages, media, and managing resources |
| Handlers | Decorators, filters, and webhook routing |
| Listeners | Conversational flows and inline waiting |
| Updates | Message, callback, status, and system updates |
| Filters | Composable, reusable update filtering |
| Templates | Create, send, and manage message templates |
| Flows | Build interactive multi-screen flows |
| Calls | Handle voice call events |
Contributions are welcome! Check out the Contributing Guide to get started.
Questions? Ideas? Join the conversation:
- 💬 Telegram Chat — Community discussions & support
- 📢 Telegram Channel — Announcements & updates
PyWa is open-source software licensed under the MIT License.