Scaling WhatsApp Message Broadcasting: How We Successfully Sent 50,000 Messages Using the WhatsApp API

Written on November 17, 2024

Views : Loading...

Scaling WhatsApp Message Broadcasting: How We Successfully Sent 50,000 Messages Using the WhatsApp API

Introduction

Broadcasting large volumes of messages on platforms like WhatsApp can be a daunting task, especially when dealing with scalability and reliability. Recently, I embarked on a project to broadcast 50,000 WhatsApp messages using the WhatsApp API. This blog details the step-by-step process, the challenges encountered, and the solutions implemented to achieve this feat efficiently using Next.js, Vercel, and AWS Lambda functions.

The Challenge

The primary objective was to allow users to select a message template, upload a CSV file containing variables and phone numbers, and broadcast personalized messages to all recipients. However, several challenges needed to be addressed:

  1. Handling Large CSV Files: Processing a CSV file with 50,000 entries in a single go could overwhelm the server, especially on platforms like Vercel, which have execution time limits.
  2. API Rate Limits: Sending messages serially at approximately 2 seconds per API call would take around 27 hours, which is impractical.
  3. Scalability: Ensuring the system could handle high concurrency and large-scale processing without failures.

Challenge Diagram

The Solution Architecture

To overcome these challenges, I designed a serverless architecture using AWS services that could efficiently handle the load. Here's an overview of the solution:

  1. Frontend (Next.js on Vercel):

    • Users select a message template.
    • Users upload a CSV file containing variables and phone numbers.
  2. AWS S3:

    • The CSV file is uploaded to S3 using a pre-signed URL.
  3. AWS Lambda Functions:

    • First Lambda: Processes the CSV file, converts each row into a JSON payload, and enqueues messages into AWS SQS.
    • Second Lambda: Triggers from SQS, batches messages, and sends them via the WhatsApp API.
  4. AWS SQS:

    • Acts as a queue to manage message payloads and ensure reliable processing.

Solution Architecture

Step-by-Step Implementation

1. Setting Up the Frontend with Next.js

The frontend allows users to select a message template and upload their CSV file. To handle large file uploads efficiently, a pre-signed URL is generated for direct upload to S3.

// components/UploadForm.jsx
import { useState } from 'react';

export default function UploadForm() {
  const [file, setFile] = useState(null);
  const [template, setTemplate] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    // Get pre-signed URL from your backend
    const res = await fetch('/api/get-presigned-url');
    const { url } = await res.json();

    // Upload file to S3
    await fetch(url, {
      method: 'PUT',
      body: file,
    });

    // Notify backend to process the file
    await fetch('/api/process-csv', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ s3Key: 'uploaded-file.csv', template }),
    });

    alert('Messages are being processed!');
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Select Template:
        <select value={template} onChange={(e) => setTemplate(e.target.value)}>
          <option value="template1">Template 1</option>
          <option value="template2">Template 2</option>
          {/* Add more templates as needed */}
        </select>
      </label>
      <br />
      <label>
        Upload CSV:
        <input type="file" accept=".csv" onChange={(e) => setFile(e.target.files[0])} required />
      </label>
      <br />
      <button type="submit">Submit</button>
    </form>
  );
}

2. Handling Large CSV Processing with AWS Lambda

Given that Vercel isn't suitable for processing 50,000 messages in one go, AWS Lambda functions were employed to handle the heavy lifting.

a. Uploading CSV to S3

Users upload the CSV file directly to S3 using a pre-signed URL, ensuring secure and efficient file transfers without overloading the server.

// pages/api/get-presigned-url.js
import AWS from 'aws-sdk';

const s3 = new AWS.S3({
  region: 'your-region',
  accessKeyId: process.env.AWS_ACCESS_KEY,
  secretAccessKey: process.env.AWS_SECRET_KEY,
});

export default async function handler(req, res) {
  const params = {
    Bucket: 'your-bucket-name',
    Key: `uploads/${Date.now()}-${req.body.filename}`,
    Expires: 60, // URL expiry time in seconds
    ContentType: 'text/csv',
  };

  const url = s3.getSignedUrl('putObject', params);
  res.status(200).json({ url });
}

b. Processing the CSV and Enqueuing Messages

Once the CSV is uploaded, the first Lambda function downloads it from S3, parses each row into a JSON payload, and enqueues messages into SQS in batches of 10.

// lambda/ProcessCSV.js
const AWS = require('aws-sdk');
const csv = require('csv-parser');
const s3 = new AWS.S3();
const sqs = new AWS.SQS();
const P_LIMIT = require('p-limit');
const limit = P_LIMIT(10); // Limit to 10 concurrent SQS sends

exports.handler = async (event) => {
  const { s3Key } = JSON.parse(event.body);
  const params = { Bucket: 'your-bucket-name', Key: s3Key };
  const stream = s3.getObject(params).createReadStream();

  const messages = [];
  
  return new Promise((resolve, reject) => {
    stream
      .pipe(csv())
      .on('data', (row) => {
        messages.push({
          Id: row.phoneNumber,
          MessageBody: JSON.stringify({
            phoneNumber: row.phoneNumber,
            variables: row.variables, // Adjust based on your CSV structure
          }),
        });
      })
      .on('end', async () => {
        try {
          const batches = [];
          while (messages.length) {
            batches.push(messages.splice(0, 10));
          }

          await Promise.all(
            batches.map((batch) =>
              limit(() =>
                sqs.sendMessageBatch({
                  QueueUrl: 'your-sqs-queue-url',
                  Entries: batch,
                }).promise()
              )
            )
          );

          resolve({ statusCode: 200, body: 'Messages enqueued successfully!' });
        } catch (error) {
          reject(error);
        }
      })
      .on('error', reject);
  });
};

3. Sending Messages via WhatsApp API with the Second Lambda

The second Lambda function is triggered by SQS messages. It batches up to 1,000 messages and sends them through the WhatsApp API using Meta's endpoints.

// lambda/SendWhatsAppMessages.js
const AWS = require('aws-sdk');
const axios = require('axios');
const sqs = new AWS.SQS();

exports.handler = async (event) => {
  const messages = event.Records.map((record) => JSON.parse(record.body));

  // Batch messages into groups of 1000
  const batches = [];
  while (messages.length) {
    batches.push(messages.splice(0, 1000));
  }

  await Promise.all(
    batches.map(async (batch) => {
      const payload = {
        messaging_product: "whatsapp",
        to: batch.map(msg => msg.phoneNumber),
        type: "template",
        template: {
          name: "your_template_name",
          language: { code: "en_US" },
          components: [
            {
              type: "body",
              parameters: batch.map(msg => ({ type: "text", text: msg.variables.someVariable })),
            },
          ],
        },
      };

      await axios.post('https://graph.facebook.com/v13.0/your_phone_number_id/messages', payload, {
        headers: {
          'Authorization': `Bearer your_access_token`,
          'Content-Type': 'application/json',
        },
      });
    })
  );

  return { statusCode: 200, body: 'Messages sent successfully!' };
};

4. Leveraging AWS SQS for Reliable Messaging

AWS SQS ensures that messages are reliably queued and processed. By decoupling the message processing from the frontend, the system can handle high volumes without overloading any single component.

5. Optimizing Performance and Scalability

  • Batch Processing: By sending messages in batches of 10 to SQS and processing 1,000 messages per Lambda invocation, we significantly reduce the number of API calls and enhance throughput.
  • Concurrency Control: Using p-limit ensures that we don't exceed the SQS rate limits and maintain efficient processing.
  • Serverless Architecture: AWS Lambda scales automatically based on the load, ensuring that the system can handle spikes without manual intervention.

Conclusion

Broadcasting 50,000 WhatsApp messages efficiently required a robust, scalable, and reliable architecture. By leveraging Next.js for the frontend, AWS S3 for file storage, Lambda functions for processing, and SQS for message queuing, we successfully scaled our messaging system to handle large volumes seamlessly.

This approach not only ensures scalability but also maintains high performance and reliability, making it a suitable solution for large-scale messaging needs.

Further Reading


If you found this guide helpful, feel free to share it with others

Share this blog

Related Posts

Implementing Serverless AI Deployments with AWS Lambda: Performance Improvements

18-04-2025

Cloud Computing
serverless AI
AWS Lambda
performance optimization

Explore effective strategies for enhancing the performance of serverless AI deployments on AWS Lambd...

Implementing Serverless AI: Metric Improvements

27-04-2025

Machine Learning
serverless AI
cloud functions
machine learning deployment

Learn how to implement serverless AI to improve cost efficiency, latency, and scalability in machine...

Serverless vs Containerized Microservices: Benchmarking Performance for AI Deployments

26-04-2025

Technology
serverless
containers
microservices
AI deployment

Benchmarking the performance of serverless vs containerized microservices for AI deployments.

Implementing Real-Time Data Processing with Apache Kafka and TensorFlow: Metric Improvements

25-04-2025

Data Engineering
Apache Kafka
TensorFlow
Real-Time Data Processing
AI Deployment

Learn how to implement real-time data processing using Apache Kafka and TensorFlow, and achieve sign...

Implementing Quantum-Enhanced Machine Learning Models: Metric Improvements

24-04-2025

Machine Learning
Quantum Computing
Machine Learning
Performance Metrics

Explore how quantum-enhanced machine learning models can improve performance metrics like accuracy a...

Implementing Real-Time Object Detection with Edge AI: Performance Gains

23-04-2025

Computer Science
Edge AI
Real-Time Object Detection
Performance Gains

Discover how to implement real-time object detection with edge AI and achieve significant performanc...

Implementing Algebraic Semantics for Machine Knitting: Metric Improvements

22-04-2025

Mathematics and Computer Science
algebraic semantics
machine knitting
AI deployment

Enhancing machine knitting efficiency and scalability through algebraic semantics.

Comparative Analysis: TensorFlow vs PyTorch for Edge AI Deployment

21-04-2025

Machine Learning
TensorFlow
PyTorch
Edge AI
Deployment

This blog provides a detailed comparative analysis of TensorFlow and PyTorch for deploying AI models...

Implementing Real-Time Anomaly Detection with Edge AI: Performance Metrics

20-04-2025

Computer Science
Edge AI
Real-Time Anomaly Detection
Performance Metrics

Discover how to effectively implement real-time anomaly detection using edge AI and evaluate perform...

Implementing Real-Time Inference with Edge AI: Metric Improvements

19-04-2025

Computer Science
edge AI
real-time inference
performance metrics

Explore how edge AI enhances real-time inference by improving latency, throughput, and energy consum...