This repository contains a Python script that uploads a PDF document to the Meta WhatsApp Cloud API, and then broadcasts it to all phone numbers listed in a CSV file (without headers). It also supports optional message templates, rate limiting, and .env configuration for easy setup.
-
Uploads a PDF file to WhatsApp Cloud API, retrieves a
media_id, and sends it as a native document attachment (not a public link) -
Reads recipient phone numbers from a CSV file with no headers
-
Comprehensive logging system with multiple levels and optional file output
-
Detailed error handling with specific error messages and recovery guidance
-
Failed numbers tracking - automatically saves failed phone numbers to CSV with error details
-
Extensive configuration validation to catch issues before sending
-
Progress tracking with success/failure statistics and timing information
-
Optional
.envconfiguration file for persistent settings -
Supports:
--dry-runfor testing without sending--ratefor throttling message sends--template=name:langto send an approved message template first--log-levelfor controlling logging verbosity (DEBUG, INFO, WARNING, ERROR)--log-filefor persistent logging to files- CLI overrides for all
.envvalues
- A WhatsApp Cloud API account configured in Meta for Developers
- A Phone Number ID and Permanent Access Token
- Python 3.8+
- The
requestslibrary (usually pre-installed; if not, install viapip install requests) - A publicly reachable PDF file or a local one to upload
Set up an isolated Python environment and install dependencies:
macOS/Linux (bash/zsh):
python3 -m venv .venv
source .venv/bin/activateWindows (PowerShell):
py -3 -m venv .venv
.venv\Scripts\Activate.ps1To exit later:
deactivate
Create a requirements.txt (or use the one in this repo) and install:
requirements.txt
requests>=2.31.0
Install:
pip install -r requirements.txtIf
pipis missing, trypython -m pip install -r requirements.txt(orpy -3 -m pip ...on Windows).
.
├── send_whatsapp_upload_and_broadcast.py
├── .env.example
├── README.md
├── requirements.txt
└── numbers.csv
The virtual environment folder (e.g.,
.venv/) is not shown above and should be excluded via.gitignore. Add lines like:
.venv/
.env
- Create a
.envfile in the same directory as the script:
cp .env.example .env- Edit
.envwith your own configuration:
# WhatsApp Cloud API
WA_PHONE_NUMBER_ID=123456789012345
WA_ACCESS_TOKEN=EAAB...your_permanent_token...
WA_GRAPH_VERSION=v20.0
# File paths
CSV_PATH=./numbers.csv
PDF_PATH=./Offer-Oct.pdf
# Message
CAPTION=Hi — here’s your PDF from Longbeard.
# Optional
FILENAME=Offer-Oct.pdf
RATE=0.75
# TEMPLATE=my_marketing_template:en_US
# DRY_RUN=true- Prepare your CSV file (
numbers.csv):
Each line should contain a single phone number in international format (no headers):
+27821234567
27829876543
002782345678
Run the script with:
python send_whatsapp_upload_and_broadcast.py --env ./.envCLI arguments override .env values. Examples:
python send_whatsapp_upload_and_broadcast.py --env ./.env --rate=0.3 --dry-runYou can also specify paths and message inline:
python send_whatsapp_upload_and_broadcast.py \
--env ./.env \
--csv=./numbers.csv \
--pdf=./docs/Invoice.pdf \
--caption="Here’s your October invoice." \
--filename=Invoice-Oct.pdf| Option | Description | Default |
|---|---|---|
--env |
Path to .env file |
./.env |
--csv |
Path to CSV file (no headers) | From .env |
--pdf |
Path to PDF file | From .env |
--caption |
Text caption for message | From .env |
--filename |
Filename shown in WhatsApp | PDF filename |
--rate |
Delay between sends (seconds) | 0.5 |
--template=name:lang |
Approved template to send first | None |
--dry-run |
Don't upload/send (log actions only) | False |
--log-level |
Logging level (DEBUG/INFO/WARNING/ERROR) | INFO |
--log-file |
Path to log file for persistent logging | None |
--failed-csv |
Path to CSV file for failed numbers | failed.csv |
- 24-hour rule: Free-form messages (with captions) can only be sent within 24 hours of a user’s last message. Outside that window, use an approved template first.
- Opt-in required: Only message users who have consented to WhatsApp communications from you.
- Security: Keep your
WA_ACCESS_TOKENsecret. Add.envto.gitignore. - CSV validation: Numbers must be international format; script auto-strips non-digits.
The script provides comprehensive logging with four levels:
- DEBUG: Detailed information for troubleshooting (API requests, phone number processing, etc.)
- INFO: General operational information (default level)
- WARNING: Warning messages and recoverable errors
- ERROR: Critical errors that prevent operation
By default, logs are displayed in the console with timestamps:
python send_whatsapp_upload_and_broadcast.py --env ./.env --log-level INFOEnable persistent logging to files for audit trails and debugging:
python send_whatsapp_upload_and_broadcast.py --env ./.env --log-file ./logs/run.logOr set in .env:
LOG_FILE=./logs/whatsapp_bulk_sender.logFor detailed troubleshooting, use DEBUG level:
python send_whatsapp_upload_and_broadcast.py --env ./.env --log-level DEBUGThis shows:
- Environment variable loading details
- Phone number validation process
- API request/response details (with token masking for security)
- File processing information
- Rate limiting delays
When messages fail to send, the script automatically creates a failed.csv file containing:
- phone_number: The phone number that failed
- error_message: Detailed error description
- timestamp: When the failure occurred (ISO format)
phone_number,error_message,timestamp
27821234567,"Send failed: 403 Forbidden",2025-10-20T10:30:15.123456
27829876543,"Network timeout after 30 seconds",2025-10-20T10:31:22.789012Customize the failed CSV path:
python send_whatsapp_upload_and_broadcast.py --env ./.env --failed-csv ./errors/failed_today.csvOr in .env:
FAILED_CSV_PATH=./failed_numbers.csvUses for failed numbers CSV:
- Retry failed numbers later
- Analyze failure patterns
- Update contact database
- Generate reports for stakeholders
python send_whatsapp_upload_and_broadcast.py --env ./.envOutput:
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - ============================================================
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - WhatsApp Bulk Document Sender - Starting
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Version: 2.0.0
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Start time: 2025-10-20 10:30:15.123456
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - ============================================================
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Loading environment from: .env
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Successfully loaded 8 environment variables from .env
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Configuration loaded:
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Configuration validation passed
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Loading recipient phone numbers...
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Successfully loaded 25 valid phone numbers from CSV
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Loaded 25 valid recipient numbers
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - ========================================
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - PHASE 1: Media Upload
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - ========================================
2025-10-20 10:30:15 - whatsapp_bulk_sender - INFO - Uploading media file: ./Offer-Oct.pdf (2458361 bytes, application/pdf)
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - Successfully uploaded media, ID: 908345987654321
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - ========================================
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - PHASE 2: Message Broadcasting
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - ========================================
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - Processing recipient 1/25: 27821234567
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - Document sent successfully to 27821234567, message ID: wamid.HBgM...
2025-10-20 10:30:17 - whatsapp_bulk_sender - INFO - ✓ Success for 27821234567
...
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - ============================================================
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - OPERATION COMPLETE
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - ============================================================
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Duration: 0:00:30.123456
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Total recipients: 25
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Successful sends: 24
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Failed sends: 1
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Success rate: 96.0%
2025-10-20 10:30:45 - whatsapp_bulk_sender - WARNING - 1 messages failed to send. Check the logs above for details.
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Writing 1 failed numbers to: failed.csv
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Successfully wrote failed numbers to: failed.csv
2025-10-20 10:30:45 - whatsapp_bulk_sender - INFO - Failed phone numbers written to: failed.csv
- 403 Permission Error: Check your access token and that your app is in Live mode.
- Media upload failure: Ensure the PDF file exists, is readable, and < 16MB.
- Message not delivered: Verify the number is a registered WhatsApp account and the user opted in.
- Configuration errors: Use
--log-level DEBUGto see detailed configuration loading. - CSV parsing issues: Enable DEBUG logging to see which numbers are being skipped and why.
- Rate limiting: Increase the
RATEvalue in your configuration to slow down sending.
For detailed troubleshooting information:
python send_whatsapp_upload_and_broadcast.py --env ./.env --log-level DEBUG --log-file debug.logThis will:
- Show all environment variables being loaded (with sensitive data masked)
- Display detailed phone number validation
- Log all API requests and responses
- Show file processing details
- Track rate limiting delays
When messages fail, check the failed.csv file for detailed error information:
# Review failed numbers
cat failed.csv
# Count failures by error type
cut -d',' -f2 failed.csv | sort | uniq -c
# Extract just the phone numbers for retry
tail -n +2 failed.csv | cut -d',' -f1 > retry_numbers.csvThe script uses specific exit codes:
- 0: Success (all messages sent)
- 1: Partial success (some messages failed) or configuration errors
- 130: Interrupted by user (Ctrl+C)
When using --log-file, you can analyze the logs:
# Show only errors
grep "ERROR" whatsapp_bulk_sender.log
# Show success/failure summary
grep -E "(✓ Success|✗ Failed)" whatsapp_bulk_sender.log
# Check configuration issues
grep "Configuration" whatsapp_bulk_sender.logThis script is provided under the MIT License.
Author: RM Lombard
Version: 2.0.0
Date: 2025-10-20
- Comprehensive Logging: Multi-level logging system with DEBUG, INFO, WARNING, ERROR levels
- File Logging: Optional persistent logging to files for audit trails
- Enhanced Error Handling: Detailed error messages with specific guidance
- Configuration Validation: Extensive validation with clear error reporting
- Progress Tracking: Real-time progress indicators and final statistics
- Improved Documentation: Complete function documentation with type hints
- Security Enhancements: Sensitive data masking in logs
- Better CLI Support: Additional command-line options for logging control