cd /blog
GET/blog/send-whatsapp-messages-python-2026200OK
pythontutorialapi

How to Send WhatsApp Messages with Python (2026 Guide)

April 1, 2026|8 min read

Sending WhatsApp messages programmatically opens up powerful automation possibilities — from customer notifications to full chatbot flows. This guide shows you how to send WhatsApp messages using Python and the WSAPI REST API, with no per-message fees and no Meta Business verification required.

Prerequisites

  • Python 3.8+ installed
  • A WSAPI Cloud account (or self-hosted instance) — start a free 14-day trial
  • Your API key and instance ID from the WSAPI dashboard
  • A WhatsApp account connected via QR code

Quick Start: Send a Text Message

The fastest way to send a WhatsApp message with Python is a single POST request using the requests library:

send_message.py
import requests

API_URL = "https://api.wsapi.chat"
HEADERS = {
    "x-api-key": "your-api-key",
    "x-instance-id": "your-instance-id",
    "Content-Type": "application/json"
}

response = requests.post(f"{API_URL}/message/text", headers=HEADERS, json={
    "to": "1234567890",
    "text": "Hello from Python! 🐍"
})

print(response.json())

That's it — a working WhatsApp message in 10 lines of Python. The to field accepts a phone number in international format (no + prefix), and text is the message body.

Sending Different Message Types

WSAPI supports 8 media types beyond plain text. Here are the most common ones:

Images

send_image.py
requests.post(f"{API_URL}/message/image", headers=HEADERS, json={
    "to": "1234567890",
    "url": "https://example.com/product.jpg",
    "caption": "Check out our new product!"
})

Documents

send_document.py
requests.post(f"{API_URL}/message/document", headers=HEADERS, json={
    "to": "1234567890",
    "url": "https://example.com/invoice.pdf",
    "filename": "Invoice-2026-001.pdf",
    "caption": "Your invoice for March 2026"
})

Location

send_location.py
requests.post(f"{API_URL}/message/location", headers=HEADERS, json={
    "to": "1234567890",
    "latitude": 37.7749,
    "longitude": -122.4194,
    "description": "Our San Francisco Office"
})

Receiving Messages with Webhooks

Sending messages is only half the story. To build interactive flows, you need to receive incoming messages via webhooks. Here's a minimal Flask receiver:

webhook_server.py
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def webhook():
    payload = request.json
    event = payload["event"]
    data = payload["data"]

    if event == "message" and not data.get("fromMe"):
        print(f"New message from {data['from']}: {data['body']}")

    return jsonify({"status": "ok"})

if __name__ == "__main__":
    app.run(port=3000)

Or with FastAPI for async support:

webhook_fastapi.py
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    payload = await request.json()
    event = payload["event"]
    data = payload["data"]

    if event == "message" and not data.get("fromMe"):
        print(f"New message from {data['from']}: {data['body']}")

    return {"status": "ok"}

Building an Auto-Reply Bot

Combining sending and receiving, here's a complete auto-reply bot that responds to keywords:

auto_reply_bot.py
import requests
from fastapi import FastAPI, Request

app = FastAPI()

API_URL = "https://api.wsapi.chat"
HEADERS = {
    "x-api-key": "your-api-key",
    "x-instance-id": "your-instance-id",
    "Content-Type": "application/json"
}

REPLIES = {
    "pricing": "Check our plans at https://wsapi.chat/pricing",
    "help": "Available commands: pricing, docs, demo",
    "docs": "Read our docs at https://docs.wsapi.chat",
    "demo": "Schedule a demo at https://wsapi.chat/contact",
}

def send_reply(to: str, text: str):
    requests.post(f"{API_URL}/message/text", headers=HEADERS, json={
        "to": to, "text": text
    })

@app.post("/webhook")
async def webhook(request: Request):
    payload = await request.json()

    if payload["event"] != "message":
        return {"status": "ok"}

    data = payload["data"]
    if data.get("fromMe"):
        return {"status": "ok"}

    body = data["body"].lower().strip()
    reply = REPLIES.get(body, "Type 'help' to see available commands.")
    send_reply(data["from"], reply)

    return {"status": "ok"}

Error Handling

Production code should handle API errors gracefully. WSAPI returns standard HTTP status codes with JSON error bodies:

error_handling.py
import requests

def send_message(to: str, text: str) -> dict:
    try:
        response = requests.post(
            f"{API_URL}/message/text",
            headers=HEADERS,
            json={"to": to, "text": text},
            timeout=10
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.Timeout:
        print("Request timed out — retrying...")
        return send_message(to, text)
    except requests.exceptions.HTTPError as e:
        error = e.response.json()
        print(f"API error {e.response.status_code}: {error.get('detail', 'Unknown')}")
        raise

Next Steps