“python code to send whatsapp message automation”

Automating WhatsApp communication can significantly streamline your workflow, whether you are building a customer alert system, dispatching daily data reports, or setting up a personal server monitor. Because WhatsApp values privacy and actively combats spam, developers must approach automation strategically.

This guide covers everything required to build a robust, production-ready WhatsApp automation engine using Python. We will explore three distinct methods, progressing from a simple desktop scripting approach to a professional, scalable infrastructure using the official Cloud API.


1. Quick Desktop Automation: The pywhatkit Approach

If you need a rapid, local script that requires zero registration or API tokens, the open-source library pywhatkit is the fastest entry point. It acts as an orchestrator for your local web browser, simulating human clicks and keystrokes via WhatsApp Web.

Prerequisites & Installation

This approach requires Google Chrome installed on your machine and an active session already logged into WhatsApp Web. Install the library via pip:

pip install pywhatkit

The Code Implementation

Create a file named instant_whatsapp.py and implement the following logic:

import pywhatkit as kit
import time
import sys

def send_desktop_message(phone_number: str, message: str, delay_minutes: int = 2):
    """
    Sends a WhatsApp message by automating the local desktop browser.
    Note: The time must be at least 2-3 minutes ahead of your current system time.
    """
    try:
        print(f"[*] Initializing browser automation for {phone_number}...")
        
        # Calculate execution time
        current_time = time.localtime(time.time() + (delay_minutes * 60))
        target_hour = current_time.tm_hour
        target_minute = current_time.tm_min
        
        print(f"[*] Scheduling message for {target_hour:02d}:{target_minute:02d}")
        
        # Trigger the browser automation
        kit.sendwhatmsg(
            phone_no=phone_number,
            message=message,
            time_hour=target_hour,
            time_min=target_minute,
            wait_time=15,  # Seconds to let WhatsApp Web fully load
            tab_close=True, # Close tab automatically after sending
            close_time=3   # Seconds to wait before closing the tab
        )
        
        print("[+] Automation commands executed successfully.")
        
    except Exception as e:
        print(f"[-] An error occurred during automation: {e}", file=sys.stderr)

if __name__ == "__main__":
    # Format: Country Code + Phone Number without spaces or symbols
    TARGET_NUMBER = "+1234567890" 
    TEXT_CONTENT = "Hello! This is an automated message sent via Python using desktop orchestration."
    
    send_desktop_message(TARGET_NUMBER, TEXT_CONTENT)

Technical Limitations of Desktop Orchestration

While excellent for personal tasks, pywhatkit is fundamentally limited:

  • UI Dependency: It requires an active, unlocked display environment. It will fail on headless Linux VPS instances or cloud workers (e.g., AWS Lambda, DigitalOcean Droplets).
  • Concurrence Constraints: It hijacks your keyboard and mouse focus during execution. If you type while the script runs, the message payload can become corrupted.
  • Lack of Callbacks: Your script cannot programmatically confirm if the recipient actually received or read the message.

2. Headless Server Automation: Selenium WebDriver

To run desktop automation on a remote Linux server without a physical monitor, you must use a headless browser framework. Selenium WebDriver allows you to launch an invisible instance of Chrome, load your existing user profile (bypassing the QR code login step), and inject actions directly into the document object model (DOM).

Prerequisites & Architecture

You must save your WhatsApp Web authentication state locally. This is achieved by creating a dedicated Chrome profile directory on your machine, logging in manually once, and pointing your script to that directory.

pip install selenium webdriver-manager

The Code Implementation

Create a file named headless_whatsapp.py:

import os
import sys
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager

def send_headless_message(profile_path: str, phone_number: str, message: str):
    """
    Launches a headless or windowed Chrome instance using an authenticated
    user profile directory to bypass the WhatsApp Web QR code scan.
    """
    if not os.path.exists(profile_path):
        print(f"[-] Profile directory not found at: {profile_path}")
        print("[*] Please provide a valid path to your Chrome User Data folder.")
        return

    # Configure Chrome Options to use your authentic login session
    options = webdriver.ChromeOptions()
    options.add_argument(f"--user-data-dir={profile_path}")
    options.add_argument("--profile-directory=Default")
    
    # Cloud server optimization flags
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    # Comment out the next line if you want to visually debug the browser actions
    options.add_argument("--headless=new") 

    # Initialize WebDriver
    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service, options=options)
    
    try:
        # Navigate directly to the WhatsApp click-to-chat API URL
        encoded_message = urllib.parse.quote(message) if 'urllib' in sys.modules else message
        target_url = f"https://whatsapp.com{phone_number}&text={encoded_message}"
        
        print(f"[*] Opening target context: {target_url}")
        driver.get(target_url)
        
        # Explicit wait: Allow up to 45 seconds for the chat input field to resolve in the DOM
        print("[*] Waiting for WhatsApp Web interface to synchronize...")
        send_button_xpath = '//span[@data-icon="send"]' or '//button[@aria-label="Send"]'
        
        # Wait until the input container is interactive
        wait = WebDriverWait(driver, 45)
        input_box = wait.until(EC.presence_of_element_located((By.XPATH, '//div[@contenteditable="true"][@data-tab="10"]')))
        
        # Give the UI a brief moment to settle
        time.sleep(2)
        
        print("[*] Dispatching payload...")
        input_box.send_keys(Keys.ENTER)
        
        # Wait to ensure the websocket connection delivers the payload frames
        time.sleep(5)
        print("[+] Message dispatched successfully via Selenium DOM injection.")
        
    except Exception as e:
        print(f"[-] Execution failure: {e}", file=sys.stderr)
    finally:
        driver.quit()

if __name__ == "__main__":
    # Example Path: Windows -> r"C:\Users\YourUser\AppData\Local\Google\Chrome\User Data"
    # Example Path: macOS -> "/Users/YourUser/Library/Application Support/Google/Chrome"
    CHROME_PROFILE = os.path.expanduser("~/Desktop/WhatsAppAutomationProfile")
    TARGET = "1234567890"
    TEXT = "Automated message sent via headless Selenium core framework."
    
    send_headless_message(CHROME_PROFILE, TARGET, TEXT)

Risk Analysis: Shadow Banning

Web scraping engines that interact with WhatsApp Web violate Meta’s Terms of Service. WhatsApp employs advanced behavioral analysis algorithms that detect machine-like interactions—such as perfectly uniform typing speeds, instant page interactions, and continuous outbound traffic.

If flagged, your phone number faces an immediate, irreversible shadow ban. For production workflows, transactional alerts, or business integrations, you must use the official API.


3. Production Grade: The Official WhatsApp Cloud API

For reliable, high-volume delivery, the WhatsApp Cloud API hosted by Meta is the industry standard. It runs asynchronously over secure HTTPS protocols, provides explicit delivery confirmation statuses, and eliminates the risk of an account ban.

Architecture Plan

The data flow for a production enterprise engine bypasses local browsers completely, moving directly from your Python app to Meta’s servers:

[ Your Python Application ] 
          │
          â–¼ (Secure JSON Payload via HTTP POST)
[ Meta Cloud API Gateways ] 
          │
          â–¼ (Direct Carrier Routing)
[ Recipient Mobile Handset ]

Meta Developer Account Setup

  1. Navigate to the Meta for Developers Dashboard and register an account.
  2. Select Create App and choose the Other business classification type.
  3. Add the WhatsApp product configuration to your application ecosystem.
  4. Obtain your temporary Access Token, Phone Number ID, and configure a test recipient number in the developer panel.

Code Implementation

We will use the standard, high-performance requests library to interface with Meta’s REST endpoints.

pip install requests dotenv

Create a production configurations environment file named .env:

WHATSAPP_API_TOKEN=your_meta_bearer_access_token_here
WHATSAPP_PHONE_NUMBER_ID=your_assigned_phone_number_id

Create the implementation asset named cloud_api_whatsapp.py:

import os
import json
import requests
from dotenv import load_dotenv

# Initialize application configuration boundaries
load_dotenv()

class WhatsAppCloudEngine:
    def __init__(self):
        self.token = os.getenv("WHATSAPP_API_TOKEN")
        self.phone_id = os.getenv("WHATSAPP_PHONE_NUMBER_ID")
        self.base_url = f"https://facebook.com{self.phone_id}/messages"
        
        if not self.token or not self.phone_id:
            raise ValueError("[-] Missing critical environment variables inside configuration context.")

    def send_template_message(self, recipient_phone: str, template_name: str, language_code: str = "en_US"):
        """
        Initiates a conversation using an officially approved Meta message template.
        Essential for messaging users who haven't interacted with your business within 24 hours.
        """
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json"
        }
        
        payload = {
            "messaging_product": "whatsapp",
            "to": recipient_phone,
            "type": "template",
            "template": {
                "name": template_name,
                "language": {
                    "code": language_code
                }
            }
        }
        
        try:
            response = requests.post(self.base_url, headers=headers, json=payload, timeout=10)
            return self._parse_api_response(response)
        except requests.exceptions.RequestException as req_err:
            return {"status": "error", "message": f"Network transport breakdown: {req_err}"}

    def send_free_text_message(self, recipient_phone: str, message_text: str):
        """
        Sends an unformatted free-text message.
        Note: This only works if an active 24-hour support window is open with the recipient.
        """
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json"
        }
        
        payload = {
            "messaging_product": "whatsapp",
            "recipient_type": "individual",
            "to": recipient_phone,
            "type": "text",
            "text": {
                "preview_url": True,
                "body": message_text
            }
        }
        
        try:
            response = requests.post(self.base_url, headers=headers, json=payload, timeout=10)
            return self._parse_api_response(response)
        except requests.exceptions.RequestException as req_err:
            return {"status": "error", "message": f"Network transport breakdown: {req_err}"}

    def _parse_api_response(self, response: requests.Response) -> dict:
        """
        Evaluates server response blocks and translates internal headers to localized dictionary structures.
        """
        parsed_data = response.json()
        if response.status_code in [200, 201]:
            return {
                "status": "success",
                "message_id": parsed_data["messages"][0]["id"],
                "raw": parsed_data
            }
        else:
            return {
                "status": "fail",
                "error_code": parsed_data.get("error", {}).get("code"),
                "error_message": parsed_data.get("error", {}).get("message"),
                "raw": parsed_data
            }

if __name__ == "__main__":
    # Execution Test Run
    try:
        client = WhatsAppCloudEngine()
        
        # Test recipient structural formatting
        TEST_RECIPIENT = "1234567890" 
        
        print("[*] Dispatching official Meta template engine frame...")
        # 'hello_world' is the default template approved on registration by Meta
        result = client.send_template_message(TEST_RECIPIENT, template_name="hello_world")
        
        print(f"[+] Server Handshake Complete. Output:\n{json.dumps(result, indent=4)}")
        
    except Exception as initialization_failure:
        print(f"[-] Execution terminated prematurely: {initialization_failure}")

4. Comparing the Approaches

Core Metrics ComponentMethod 1: pywhatkitMethod 2: Selenium Web CoreMethod 3: Cloud API (Official)
Execution CostCompletely FreeCompletely FreeTiered Pricing (First 1K conversations free/mo)
Account Ban RiskVery HighExtremely HighZero Risk Profile
Headless CapabilityNoYesFull Native Integration
Delivery Success Rate~75% (Dependent on browser focus)~80% (Dependent on DOM rendering)>99.9% (Enterprise SLA tier)
Execution VelocitySlow (~20s setup per payload)Moderate (~10s initialization)Instantaneous (<200ms API roundtrip)

5. Building an Production-Grade Architecture

To run an enterprise alert system, your pipeline needs a reliable backend. If you try to send 10,000 notifications at once using a synchronous loop, you run the risk of running out of system memory, hitting API rate limits, or blocking thread execution loops.

A professional architecture should decouple message generation from delivery using an asynchronous producer-consumer message broker queue:

[ App Logic Layer ] ──(Pushes Task)──> [ Redis Queue Buffer ] ──(Worker Pool)──> [ Cloud API Client ]

Decoupled Worker Code with Celery and Redis

Below is an enterprise-ready configuration pattern that processes jobs asynchronously using Celery tasks and a Redis message queue.

pip install celery redis

Create a tasks module file named worker_tasks.py:

from celery import Celery
import os
import requests
import logging

# Initialize logger configuration
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Initialize Celery app with a local Redis instance acting as the broker broker
celery_worker = Celery(
    'whatsapp_tasks', 
    broker=os.getenv("REDIS_URL", "redis://localhost:6379/0"),
    backend=os.getenv("REDIS_URL", "redis://localhost:6379/0")
)

@celery_worker.task(bind=True, max_retries=5, default_retry_delay=60)
def async_dispatch_whatsapp(self, recipient_phone: str, payload_text: str):
    """
    Asynchronous task engine capable of handling high concurrent traffic loads.
    Includes built-in network fault toleration and exponential backoff retry cycles.
    """
    token = os.getenv("WHATSAPP_API_TOKEN")
    phone_id = os.getenv("WHATSAPP_PHONE_NUMBER_ID")
    url = f"https://facebook.com{phone_id}/messages"
    
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "messaging_product": "whatsapp",
        "to": recipient_phone,
        "type": "text",
        "text": {"body": payload_text}
    }
    
    try:
        logging.info(f"[*] Worker processing message delivery to tracking vector: {recipient_phone}")
        response = requests.post(url, headers=headers, json=payload, timeout=10)
        
        # If the API drops due to temporary server issues, trigger retry logic
        if response.status_code in [429, 500, 502, 503]:
            logging.warning(f"[-] Meta API experienced temporary issue ({response.status_code}). Retrying task...")
            raise self.retry(exc=Exception(f"API Error Code {response.status_code}"))
            
        data = response.json()
        logging.info(f"[+] Async delivery complete. Message reference ID: {data['messages'][0]['id']}")
        return {"status": "dispatched", "id": data['messages'][0]['id']}
        
    except requests.exceptions.Timeout as timeout_err:
        logging.error("[-] API gateway connection timed out. Retrying task window...")
        raise self.retry(exc=timeout_err)
    except Exception as structural_exception:
        logging.critical(f"[-] Absolute task exception resolved: {structural_exception}")
        return {"status": "failed", "reason": str(structural_exception)}

To run this background processing system, launch your worker pool terminal cluster with this command:

celery -A worker_tasks.celery_worker worker --loglevel=info --concurrency=4

6. Security & Production Compliance Protocols

When deploying Python automation engines into production environments, follow these security practices to protect your data and maintain compliance:

  • Secure Secret Storage: Never hardcode API tokens directly into your script files. Use environment variable stores or configuration tools like AWS Secrets Manager or HashiCorp Vault.
  • Respect Messaging Windows: Official policy states that you cannot target users with outbound free-form messages outside of an active 24-hour conversation window. Use approved Meta templates to initiate new conversations.
  • Sanitize Data Inputs: Before sending phone numbers to external endpoints, strip away structural artifacts like spaces, parentheses, and leading zero identifiers using regular expressions:import re clean_number = re.sub(r'(?<!^\+)\D|(?<=^\+)\D', '', raw_phone_input)

7. Advanced Integration: Bi-directional Webhooks

To build a conversational chatbot or a fully automated customer support engine, your system needs to do more than just send messages—it must listen and react when users reply.

When a user text matches your outbound line, Meta dispatches an asymmetric real-time event to an external endpoint via an HTTP POST Webhook. Below is a lightweight production implementation template built with the Flask framework to capture incoming messages and process them immediately.

pip install flask

Create an application entry infrastructure asset named webhook_receiver.py:

from flask import Flask, request, jsonify
import sys

app = Flask(__name__)

# This verification token must match the exact string token you configure in your Meta Developer Dashboard configuration panel
WEBHOOK_VERIFY_TOKEN = "MY_SECURE_INTEGRATION_TOKEN_SECRET"

@app.route("/webhook", methods=["GET"])
def meta_webhook_verification():
    """
    Handles Meta's initial handshake verification request.
    Validates your server's authenticity directly within the developer dashboard.
    """
    mode = request.args.get("hub.mode")
    token = request.args.get("hub.verify_token")
    challenge = request.args.get("hub.challenge")
    
    if mode and token:
        if mode == "subscribe" and token == WEBHOOK_VERIFY_TOKEN:
            print("[+] Webhook verification handshake successful.")
            return challenge, 200
        else:
            print("[-] Webhook handshake failed: Token authentication mismatch.", file=sys.stderr)
            return "Forbidden", 403
            
    return "Bad Request", 400

@app.route("/webhook", methods=["POST"])
def inbound_message_handler():
    """
    Receives incoming WhatsApp events, such as new messages, read receipts, and delivery statuses.
    """
    payload = request.json
    
    # Safely extract structured fields from the incoming JSON structure
    try:
        if "object" in payload:
            if payload.get("entry") and payload["entry"][0].get("changes") and payload["entry"][0]["changes"][0].get("value"):
                value_block = payload["entry"][0]["changes"][0]["value"]
                
                # Check if the payload contains an incoming message object array
                if "messages" in value_block:
                    message_data = value_block["messages"][0]
                    sender_id = message_data.get("from")
                    message_type = message_data.get("type")
                    
                    print(f"[*] Inbound communication detected from user object: {sender_id}")
                    
                    if message_type == "text":
                        text_body = message_data["text"].get("body")
                        print(f"[+] Processing payload message: '{text_body}'")
                        
                        # Integrate your AI chatbot logic, database lookups, or routing systems here
                        
                elif "statuses" in value_block:
                    status_block = value_block["statuses"][0]
                    msg_id = status_block.get("id")
                    delivery_status = status_block.get("status") # sent, delivered, or read
                    print(f"[*] Delivery Status Update for tracking packet [{msg_id}]: {delivery_status}")
                    
            return jsonify({"status": "received"}), 200
    except KeyError as parsing_failure:
        print(f"[-] Structural anomaly discovered in the incoming JSON schema format: {parsing_failure}", file=sys.stderr)
        
    # Always return a 200 OK response quickly to prevent Meta from retrying and flooding your server
    return jsonify({"status": "ignored"}), 200

if __name__ == "__main__":
    # In production, secure this endpoint with SSL certification layers
    app.run(host="0.0.0.0", port=5000, debug=False)

By deploying this webhook receiver alongside the asynchronous processing pipeline, you establish a resilient foundation for modern, scalable WhatsApp automation using Python.

Leave a Comment

Scroll to Top