How to Send WhatsApp Messages with Python (2026 Guide)
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:
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
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
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
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:
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:
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:
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:
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')}")
raiseNext Steps
- Build a full chatbot with conversation state and media responses
- Set up webhooks for real-time message processing
- Explore the Quickstart guide to connect your first WhatsApp account
- Read the full API documentation for all available endpoints