Skip to main content

🚀 Build WhatsApp Bots in Python • Fast, Effortless, Powerful

Project description

PyWa Logo

🚀 Build WhatsApp Bots in Python • Fast. Effortless. Powerful.

🤖 Hey there! I am using PyWa.


💫 PyWa is an all-in-one Python framework for the WhatsApp Cloud API.

Send rich media messages, use interactive buttons, listen to real-time events, build and send flows, design and send template messages, and enjoy blazing-fast async support with full integration for FastAPI, Flask, and more. Fully typed, documented, and production-ready — build powerful bots in minutes.

📄 Quick Documentation Index

Get StartedClientHandlersListenersUpdatesFiltersTemplatesFlowsCalls


Why PyWa?

  • 🚀 Fast & Simple – Focus on building, not boilerplate.
  • 💬 Rich Messaging – Text, images, files, audio, locations, contacts, buttons & more.
  • 📩 Real-Time Updates – Messages, callbacks, delivery/read receipts, account updates, and more.
  • 🔔 Listeners – Wait for user replies, clicks, or reactions with ease.
  • 📄 Templates – Create and send powerful WhatsApp templates.
  • ♻️ Flows – Build interactive WhatsApp flows effortlessly.
  • 📞 Calls Support – Receive and handle call events.
  • 🔄 Webhook-Ready – Built-in server for development and production, or attach to your own FastAPI/Flask app.
  • 🔬 Filters – Advanced filtering for incoming updates.
  • ✅ Production-Ready – Typed, documented, and fully tested.

👨‍💻 How to Use

1. Handle incoming messages

Start with a WhatsApp client and register handlers for the updates you want to receive. During development, run the webhook server with pywa dev. For production, use pywa run.

# main.py
from pywa import WhatsApp, filters, types, utils

callback_url = utils.start_ngrok_tunnel(
    auth_token="NGROK_AUTH_TOKEN",
    domain="your-domain.ngrok-free.app",
)

wa = WhatsApp(
    phone_id="1234567890",
    token="EAA...",
    app_id="1234567890",
    app_secret="********",
    callback_url=callback_url,
    verify_token="my-verify-token",
)


@wa.on_message(filters.text)
def echo(client: WhatsApp, msg: types.Message):
    msg.reply(f"You said: {msg.text}")

Run it:

pywa dev

Use this when deploying:

pywa run

See the Handlers guide for decorators, filters, callback URL registration, custom servers, and handler flow.

2. Send messages, media, templates, flows, and manage resources

Use the same client to send messages, media, templates, flows, and to manage WhatsApp Business resources.

from pywa import WhatsApp, types

wa = WhatsApp(
    phone_id="1234567890",
    token="EAA...",
)

wa.send_message(
    to="9876543210",
    text="Hello from PyWa!",
    buttons=[
        types.Button(title="Menu", callback_data="menu"),
        types.Button(title="Help", callback_data="help"),
    ],
)

wa.send_image(
    to="9876543210",
    image="https://example.com/image.jpg",
    caption="Check out this image!",
)

See the Client guide for the full API.

3. Listen for the next user response

Handlers are great for entry points. When you need to continue a conversation, use listeners.

from pywa import WhatsApp, filters, types

wa = WhatsApp(...)


@wa.on_message(filters.command("start"))
def start(client: 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}!")

See the Listeners guide for timeouts, filters, callbacks, and waiting for clicks or replies.

4. Use your own server when needed

The CLI is the easiest way to run pywa, but you can also attach pywa to an existing FastAPI or Flask app.

from fastapi import FastAPI
from pywa import WhatsApp, filters, types

app = FastAPI()

wa = WhatsApp(
    ...,
    server=app,
    webhook_endpoint="/whatsapp",
)


@wa.on_message(filters.text)
def echo(client: WhatsApp, msg: types.Message):
    msg.reply(msg.text)

Run your server normally

5. Async usage

PyWa also supports async usage with the same API. Replace imports from pywa with pywa_async and use await.

from pywa_async import WhatsApp, filters, types, utils

callback_url = utils.start_ngrok_tunnel(auth_token="NGROK_AUTH_TOKEN")

wa = WhatsApp(..., callback_url=callback_url)


@wa.on_message(filters.text)
async def hello(client: WhatsApp, msg: types.Message):
    await msg.react("👋")
    await msg.reply("Hello from PyWa Async!")

6. Create and send template messages

See Templates for more details and examples.

from pywa import WhatsApp
from pywa.types.templates import *

wa = WhatsApp(..., waba_id=123456)

# Create a template
wa.create_template(
    template=Template(
        name="buy_new_iphone_x",
        category=TemplateCategory.MARKETING,
        language=TemplateLanguage.ENGLISH_US,
        parameter_format=ParamFormat.NAMED,
        components=[
            ht := HeaderText("The New iPhone {{iphone_num}} is here!", iphone_num=15),
            bt := BodyText("Buy now and use the code {{code}} to get {{per}}% off!", code="WA_IPHONE_15", per=15),
            FooterText(text="Powered by PyWa"),
            Buttons(
                buttons=[
                    url := URLButton(text="Buy Now", url="https://example.com/shop/{{1}}", example="iphone15"),
                    PhoneNumberButton(text="Call Us", phone_number="1234567890"),
                    qrb1 := QuickReplyButton(text="Unsubscribe from marketing messages"),
                    qrb2 := QuickReplyButton(text="Unsubscribe from all messages"),
                ]
            ),

        ]
    ),
)

# Send the template message
wa.send_template(
    to="9876543210",
    name="buy_new_iphone_x",
    language=TemplateLanguage.ENGLISH_US,
    params=[
        ht.params(iphone_num=30),
        bt.params(code="WA_IPHONE_30", per=30),
        url.params(url_variable="iphone30", index=0),
        qrb1.params(callback_data="unsubscribe_from_marketing_messages", index=1),
        qrb2.params(callback_data="unsubscribe_from_all_messages", index=2),
    ]
)

7. Create and send flows

See Flows for much more details and examples.

from pywa import WhatsApp, types
from pywa.types.flows import *

# Create a WhatsApp client
wa = WhatsApp(..., waba_id=123456)

# Build a flow
my_flow_json = FlowJSON(
    screens=[
        Screen(
            id="NEWSLETTER",
            title="PyWa Newsletter",
            layout=Layout(
                children=[
                    TextHeading(text="Subscribe to our newsletter"),
                    name := TextInput(
                        name="name",
                        label="Name",
                        input_type=InputType.TEXT,
                        required=False,
                    ),
                    email := TextInput(
                        name="email",
                        label="Email",
                        input_type=InputType.EMAIL,
                        required=True,
                    ),
                    Footer(
                        label="Subscribe",
                        on_click_action=CompleteAction(
                            payload={  # Payload to send to the server
                                "name": name.ref,
                                "email": email.ref,
                            }
                        )
                    )
                ]
            )
        )
    ]
)

# Create the flow
wa.create_flow(
    name="subscribe_to_newsletter",
    categories=[FlowCategory.SIGN_UP, FlowCategory.OTHER],
    flow_json=my_flow_json,
    publish=True
)

# Send the flow to a user
wa.send_text(
    to="9876543210",
    text="Hello from PyWa!",
    buttons=types.FlowButton(
        title="Subscribe to our newsletter!",
        flow_name="subscribe_to_newsletter",
    )
)


# Handle the flow response
@wa.on_flow_completion
def handle_flow_response(_: WhatsApp, flow: types.FlowCompletion):
    flow.reply(
        text=f"Thank you for subscribing to our newsletter, {flow.response['name']}! "
             f"We will send you updates to {flow.response['email']}.",
        buttons=[types.Button(title="Unsubscribe", callback_data="unsubscribe")]
    )

🎛 Installation

  • Install using pip3:
pip3 install -U pywa
  • Install the built-in webhook server for pywa dev and pywa run:
pip3 install -U "pywa[server]"
  • Install ngrok if you want to use utils.start_ngrok_tunnel() for local development:
pip3 install -U ngrok
  • Install from source (the bleeding edge):
pip3 install -U git+https://github.com/david-lev/pywa.git
  • If you use Flows and want pywa's default encryption helpers:
pip3 install -U "pywa[cryptography]"

💾 Requirements

📖 Setup and Usage

See the Documentation for detailed instructions

⚖️ License

This project is licensed under the MIT License - see the LICENSE file for details

🔱 Contributing

Contributions are welcome! Please see the Contributing Guide for more information.

🗣 Community

Join the Telegram Group to discuss, ask questions, and share your projects built with PyWa!

Project details


Release history Release notifications | RSS feed

This version

4.0.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pywa-4.0.0.tar.gz (359.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pywa-4.0.0-py3-none-any.whl (351.3 kB view details)

Uploaded Python 3

File details

Details for the file pywa-4.0.0.tar.gz.

File metadata

  • Download URL: pywa-4.0.0.tar.gz
  • Upload date:
  • Size: 359.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for pywa-4.0.0.tar.gz
Algorithm Hash digest
SHA256 095c2698eaca1cb5659a58eaeaed8e029ad4ba396baf22fe5150d86928786f6f
MD5 3e22ab1e172e83a2b67e980bb4d70eb1
BLAKE2b-256 ab39e3054c90858bf6f95dae3ee41e82fe8941db46778ec7ec103a5149f6c6fc

See more details on using hashes here.

File details

Details for the file pywa-4.0.0-py3-none-any.whl.

File metadata

  • Download URL: pywa-4.0.0-py3-none-any.whl
  • Upload date:
  • Size: 351.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for pywa-4.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e5598ff36bb4be9e9e1b34492561a7752067db53b779710d2a0a2ac0f91c875e
MD5 b80eff0615dcd651087b4fb479074dd8
BLAKE2b-256 7fdc134b640420df89392c1575d9c2f290bbba4348a3d4c587abecca068df277

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page