WhatsApp Automation with Python
In this guide, we explain how to build a production-safe Python WhatsApp bot using Flask and ChatGPT. While Meta demands complex business verification, Whapi.Cloud delivers instant QR-code WhatsApp bot deployment. Developers will learn to handle webhooks, automate standard groups up to 1024 members, broadcast to channels, prevent infinite loops, and implement message deduplication for predictable API costs.
- How to work with WhatsApp API using Python;
- Integration with Whapi.Cloud for automating various tasks;
- Using webhooks to handle and auto-respond to messages;
- Connecting your WhatsApp bot to ChatGPT for AI-powered conversations;
To make your work easier, you can use our ready-made Python bot scripts published on GitHub. These projects provide detailed setup and testing instructions, and the code contains useful comments. It’s an excellent starting point for beginner developers.
Why Developers Avoid Meta's Official Cloud API for WhatsApp Bots
Getting Ready to Develop a Bot
- Python. Install Python if you haven’t already. Download the latest version (3.6 or above recommended) from the official site and follow the installation instructions.
- Flask. Flask is a lightweight Python web framework that we’ll use to set up a server and handle webhooks. Install Flask using the command: pip install Flask.
- API Token. Register on Whapi.Cloud to get an API token. This token allows your bot to interact with messenger through the API. After registration, you’ll receive a free channel with limited features, sufficient for testing your development. Whapi.Cloud stands out for its stability, low cost, and wide range of features. We’ll provide instructions on obtaining the token below.
- Configured Webhook. To enable your bot to process incoming updates and events from WhatsApp, you’ll need a server URL (local or external) to handle notifications from messenger. This article will explain in detail how to set it up and where to get such a link.
Obtaining an API Token
Registration and Connecting a Number
- 1. Go to the dashboard and open the Default Channel page that’s already created for you.
- 2. At the first step, you’ll see a QR code with instructions.
- 3. Open WhatsApp on your device, go to Settings → Linked Devices → Link a Device → Scan QR Code.
- 4. After successful connection, name the channel (e.g., "My Chatbot") for easier management in the future.
Getting an API Token
Your API Key
Tools for Working with the API
- User-Friendly Developer Hub: A specialized platform with documentation and examples provides code snippets for all endpoints in various programming languages.
- Postman Collection: Ready-made requests for testing the API through Postman.
- Swagger File: A detailed description of all API methods, with testing capabilities directly from the channel page.
-
WhatsApp MCP Server: Integrate your WhatsApp bot with AI agents and developer tools like Cursor using our official WhatsApp MCP Server.
What is a Webhook and How to Set It Up?
What is a Webhook?
- Instant notifications. All events are processed almost in real-time.
- High throughput. Notification speed is only limited by your server's performance.
- Flexibility. You can receive only the events you truly need, such as: Personal and Group messages; Message status changes; Group participant changes; Missed call notifications; Channel statuses, and much more.
How and Where to Get a Webhook URL?
- 1) Download Ngrok from the official website and extract it. Open the terminal and navigate to the folder where Ngrok is stored.
- 2) Run ./ngrok http PORT_NUMBER, replacing PORT_NUMBER with the port your Flask server is running on locally (e.g., 80).
- 3) You should now have a public URL that you can use as the webhook URL. Copy it for further use.
Setting Up a Hook on a Channel
- Go to the channel settings. On the channel page, click the settings button (top right corner).
- Configure the webhook. Enter your URL in the webhook section using preselected settings. You can set up multiple webhooks for different events if necessary. These settings can also be adjusted via the API.
- Save the changes. From now on, all notifications about WhatsApp events will be sent to the server you specified.
Creating the Basics of a WhatsApp Bot in Python
Flask==3.0.0
requests==2.27.1
requests-toolbelt==0.9.1
python-dotenv==0.20.0
pip install -r requirements.txt
API_TOKEN=8FjpwmyFUulh7emXOprrET3xKrwJ984O # API token from your channel
API_URL=https://gate.whapi.cloud # API endpoint URL
PORT=80 # example, 80 or 443
#URL_LINK= The Webhook link to your server {link to server}/hook.
from dotenv import load_dotenv
import os
load_dotenv()
api_token = os.getenv("API_TOKEN")
Sending a Text Message
import requests
# URL for sending text messages. Can be pulled from .env
url = "https://gate.whapi.cloud/messages/text"
# Data for sending a message
payload = {
"to": "919984351847", # Enter the recipient's number in international format
"body": "Hello! This is a test message." # Text of message
}
# Headers, including authorization token
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": f"Bearer {api_token}" # Use the token from the .env file
}
# Sending a POST request
response = requests.post(url, json=payload, headers=headers)
# Output server response
print(response.status_code)
print(response.text)
Receiving Messages via Flask Webhook
from flask import Flask, request, jsonify
app = Flask(__name__)
# Route for processing incoming messages
@app.route('/hook', methods=['POST'])
def webhook():
# Retrieving data from a request
data = request.json
# Logging an incoming message
print("Received message:", data)
# Example of incoming message processing
if "messages" in data:
for message in data["messages"]:
sender = message["from"] # Sender's number
text = message.get("body", "") # Text of message
print(f"Message from {sender}: {text}")
# Logic for replying to a message
# Check out our examples on GitHub, where we use more branching on commands for the bot
if text.lower() == "hello":
send_response(sender, "Hi there! How can I help you?")
elif text.lower() == "bye":
send_response(sender, "Goodbye!")
return jsonify({"status": "success"}), 200
# Function for sending a reply
def send_response(to, body):
import requests
url = "https://gate.whapi.cloud/messages/text"
payload = {
"to": to,
"body": body
}
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": f"Bearer {api_token}" # Use the token from the .env file
}
response = requests.post(url, json=payload, headers=headers)
print(f"Response to {to}: {response.status_code}, {response.text}")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
Advanced Features
Sending an Image
import requests
url = "https://gate.whapi.cloud/messages/image"
payload = {
"to": "919984351847",
"media": "https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg",
"caption": "An example image"
}
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": "Bearer Your_Token"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
Sending a File
import requests
url = "https://gate.whapi.cloud/messages/document"
payload = {
"to": "919984351847",
"media": "data:application/pdf;base64,JVBERi0xLjQKJdPr6eEKMSAwIG9iago8PC9UaXRsZSAoVGVybXMgb2YgU2VydmljZSBXaGFwaS5DbG91ZCkKL0NyZWF0b3IgKE1vemlsbGEvNS4wIFwoV2luZG93cyBOVCAxMC4wOyBXaW42NDsgeDY0XCkgQXBwbGVXZWJLaXQvNTM3LjM2IFwoS0h............",
"filename": "Terms of Service Whapi.Cloud.pdf",
"caption": "Hello, I am attaching an important file to my message"
}
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": "Bearer Your_Token"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
Creating a WhatsApp Group in Python
import requests
url = "https://gate.whapi.cloud/groups"
payload = {
"participants": ["919984351847", "919984351848", "919984351849"],
"subject": "Group Subject 3"
}
headers = {
"accept": "application/json",
"content-type": "application/json",
"authorization": "Bearer Your_Token"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
Automate WhatsApp Groups and Channels Programmatically
Integration with ChatGPT: AI for your WhatsApp bot
Step 1: Add ChatGPT Dependencies
openai>=1.91.0,<2.0.0
httpx>=0.28.1,<1.0.0
httpcore>=1.0.9,<2.0.0
- openai - the official SDK to send requests to OpenAI and get ChatGPT responses.
- httpx - a modern, asynchronous HTTP client library that openai depends on internally for making API calls.
- httpcore - a low-level network transport library used by httpx. It needs to be pinned explicitly to avoid version conflicts.
pip install -r requirements.txt
Step 2: Get Your OpenAI API Key
OPENAI_API_KEY=your_api_key_here
Step 3: Add the AI Logic to Your Bot
- - receives incoming webhook events from Whapi.Cloud,
- - detects whether the message starts with /ai,
- - sends the user's request to ChatGPT via the OpenAI API,
- - returns the AI-generated response to the user in WhatsApp.
How to Prevent Infinite Loops and Ensure Production Safety
from_me flag in the incoming JSON payload and immediately skipping those events, you eliminate this risk entirely. For additional best practices on maintaining a healthy sender reputation, refer to how to avoid WhatsApp bans.
from flask import Flask, request, jsonify
import requests
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
API_TOKEN = os.getenv("API_TOKEN")
API_URL = os.getenv("API_URL")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
BACKUP_ADMIN_NUMBER = os.getenv("BACKUP_ADMIN_NUMBER")
if not API_TOKEN or not API_URL or not OPENAI_API_KEY:
raise RuntimeError("Missing required environment variables in .env")
openai_client = OpenAI(api_key=OPENAI_API_KEY)
# Simple in-memory message deduplication to prevent duplicate processing
processed_messages = set()
MAX_DEDUPLICATION_SIZE = 1000
def send_message(to, body):
"""Sends a text message using the Whapi.Cloud HTTP REST API."""
headers = {
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json"
}
payload = {"to": to, "body": body}
response = requests.post(f"{API_URL}/messages/text", json=payload, headers=headers)
print("Whapi response:", response.status_code, response.text)
def send_backup_alert(error_message):
"""Sends an alert to the backup admin when a critical failure occurs."""
if BACKUP_ADMIN_NUMBER:
send_message(BACKUP_ADMIN_NUMBER, f"⚠ CRITICAL BOT ALERT: {error_message}")
def ask_openai(prompt):
"""Sends the user prompt to ChatGPT and returns the text response."""
response = openai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content.strip()
@app.route("/hook/messages", methods=["POST"])
def webhook():
data = request.json
print("Incoming webhook:", data)
for msg in data.get("messages", []):
# 1. The loop prevention filter: ignore messages sent by the bot itself
if msg.get("from_me"):
continue
msg_id = msg.get("id")
# 2. Webhook message deduplication
if msg_id in processed_messages:
print(f"Duplicate message ignored: {msg_id}")
continue
processed_messages.add(msg_id)
if len(processed_messages) > MAX_DEDUPLICATION_SIZE:
processed_messages.pop() # Keep memory footprint bounded
sender = msg.get("chat_id")
text = (msg.get("text") or {}).get("body", "").strip()
if text.lower().startswith("/ai "):
prompt = text[4:].strip()
if not prompt:
send_message(sender, "Please provide a prompt after /ai.")
else:
try:
reply = ask_openai(prompt)
send_message(sender, reply)
except Exception as e:
error_msg = f"Failed to process ChatGPT request: {e}"
print(error_msg)
send_message(sender, "Sorry, I encountered an error processing your request.")
send_backup_alert(error_msg)
else:
send_message(sender, "Hi! To ask me something, type:\n/ai your question")
return jsonify({"status": "received"}), 200
def register_webhook():
"""Registers the webhook URL with Whapi.Cloud on startup."""
bot_url = os.getenv("BOT_URL")
if bot_url:
headers = {"Authorization": f"Bearer {API_TOKEN}"}
payload = {
"webhooks": [
{
"url": bot_url,
"events": [{"type": "messages", "method": "post"}],
"mode": "method"
}
]
}
response = requests.patch(f"{API_URL}/settings", json=payload, headers=headers)
print("Webhook registration:", response.status_code, response.text)
if __name__ == "__main__":
register_webhook()
port = int(os.getenv("PORT", 8080))
app.run(host="0.0.0.0", port=port)
Run Your Python Bot in Production
gunicorn --workers 3 --bind 0.0.0.0:8080 index:app
Dockerfile and docker-compose.yml configuration. This setup mounts your local directory, automatically loads your environment variables, and manages automatic container restarts during server reboots.
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["gunicorn", "--workers", "3", "--bind", "0.0.0.0:8080", "index:app"]
version: '3.8'
services:
whatsapp-bot:
build: .
ports:
- "8080:8080"
env_file:
- .env
restart: always
docker-compose up -d --build. For complete ready-made containerized structures and more options, you can explore the official Python bot samples on GitHub, which include standardized deployment scripts and configuration templates.