Skip to main content

Broadcast Messages

The Broadcast Messages feature enables you to send messages to multiple recipients efficiently while automatically handling WhatsApp API rate limits and providing real-time progress tracking.

Overview

Send the same message or personalized template messages to thousands of recipients with:
  • Intelligent Rate Limiting: Automatic compliance with WhatsApp’s 80 msg/sec and 1000 msg/min limits
  • Progress Tracking: Real-time updates on send status, success rate, and estimated completion time
  • Error Handling: Individual message failures don’t stop the entire broadcast
  • Batch Processing: Efficient chunked processing for optimal throughput
  • Abort Control: Stop broadcasts mid-process when needed

Use Cases

  • Order confirmations
  • Shipping updates
  • Delivery notifications
  • Cart abandonment reminders
  • Medical appointment confirmations
  • Service reminders
  • Event notifications
  • Promotional messages (using approved templates)
  • Product launches
  • Special offers
  • System status updates
  • Emergency alerts
  • Important announcements
  • Survey requests
  • Feedback collection
  • Service updates

Quick Start

Simple Text Broadcast

import { WhatsAppClient } from 'whatsapp-client-sdk';

const client = new WhatsAppClient({
  accessToken: 'your-token',
  phoneNumberId: 'your-phone-id'
});

const phoneNumbers = [
  '+1234567890',
  '+0987654321',
  '+1122334455'
];

const result = await client.sendBroadcastText(
  phoneNumbers,
  'Hello! This is a broadcast message.'
);

console.log(`Sent: ${result.successful}/${result.total}`);
console.log(`Failed: ${result.failed}`);
console.log(`Duration: ${result.duration}ms`);

Personalized Template Broadcast

const recipients = [
  {
    phoneNumber: '+1234567890',
    variables: {
      name: 'John',
      order_id: '#12345',
      amount: '$99.99'
    }
  },
  {
    phoneNumber: '+0987654321',
    variables: {
      name: 'Jane',
      order_id: '#12346',
      amount: '$149.99'
    }
  }
];

const result = await client.sendBulkTemplates(
  recipients,
  'order_confirmation',  // Your approved template name
  'en_US'
);

Advanced Usage

Progress Tracking

Monitor your broadcast in real-time:
const result = await client.sendBroadcastText(
  phoneNumbers,
  'Your message here',
  {
    onProgress: (progress) => {
      console.log(`Progress: ${progress.percentage.toFixed(1)}%`);
      console.log(`Sent: ${progress.sent}/${progress.total}`);
      console.log(`Failed: ${progress.failed}`);
      console.log(`Pending: ${progress.pending}`);
      console.log(`ETA: ${Math.round(progress.estimatedTimeRemaining / 1000)}s`);
    },
    onMessageSent: (result) => {
      if (result.success) {
        console.log(`✓ Sent to ${result.phoneNumber}`);
      } else {
        console.log(`✗ Failed to ${result.phoneNumber}: ${result.error}`);
      }
    }
  }
);

Custom Batch Configuration

const result = await client.sendBroadcastText(
  phoneNumbers,
  'Your message',
  {
    batchSize: 100,              // Messages per batch (default: 50)
    delayBetweenBatches: 5000,   // Delay in ms (default: auto-calculated)
    stopOnError: false           // Continue on errors (default: false)
  }
);

Abort Broadcast

// Start broadcast
const broadcastPromise = client.sendBroadcastText(
  largePhoneNumberList,
  'Message'
);

// Abort after 5 seconds
setTimeout(() => {
  if (client.isBroadcastRunning()) {
    client.abortBroadcast();
    console.log('Broadcast aborted!');
  }
}, 5000);

const result = await broadcastPromise;
console.log(`Sent before abort: ${result.successful}`);

Rate Limiting

The SDK automatically handles WhatsApp Business API rate limits:
LimitValueSDK Behavior
Messages per second80Throttles to ~77 msg/sec (safety margin)
Messages per minute1000Batches with calculated delays
Concurrent requestsNo official limitLimits to 10 concurrent within batch

How It Works

  1. Chunking: Large batches are split into chunks of 10 messages
  2. Throttling: Each message takes minimum 13ms (~77 msg/sec)
  3. Batch Delays: Automatic delays between batches to respect per-minute limits
  4. Sequential Processing: Chunks processed sequentially to prevent spikes

API Reference

sendBroadcast(phoneNumbers, message, options?)

Send the same message to multiple recipients. Parameters:
  • phoneNumbers (string[]): Array of recipient phone numbers
  • message (OutgoingMessage): Message object with type and content
  • options (BroadcastOptions): Optional configuration
Returns: Promise<BroadcastResult>

sendBroadcastText(phoneNumbers, text, options?)

Shorthand for sending text broadcasts. Parameters:
  • phoneNumbers (string[]): Array of recipient phone numbers
  • text (string): Message text
  • options (BroadcastOptions): Optional configuration
Returns: Promise<BroadcastResult>

sendBulkTemplates(recipients, templateName, languageCode, options?)

Send personalized template messages to multiple recipients. Parameters:
  • recipients (BroadcastRecipient[]): Recipients with variables
  • templateName (string): Approved template name
  • languageCode (string): Template language (e.g., ‘en_US’)
  • options (BroadcastOptions): Optional configuration
Returns: Promise<BroadcastResult>

abortBroadcast()

Stop the currently running broadcast. Returns: void

isBroadcastRunning()

Check if a broadcast is currently in progress. Returns: boolean

Types

BroadcastOptions

interface BroadcastOptions {
  batchSize?: number;                              // Messages per batch (default: 50)
  delayBetweenBatches?: number;                    // Delay in ms (default: auto)
  onProgress?: (progress: BroadcastProgress) => void;
  onMessageSent?: (result: MessageSendResult) => void;
  stopOnError?: boolean;                           // Stop on first error (default: false)
}

BroadcastResult

interface BroadcastResult {
  broadcastId: string;           // Unique broadcast identifier
  total: number;                 // Total messages attempted
  successful: number;            // Successfully sent
  failed: number;                // Failed to send
  results: MessageSendResult[];  // Individual results
  duration: number;              // Total duration in ms
  startTime: number;             // Start timestamp
  endTime: number;               // End timestamp
}

BroadcastProgress

interface BroadcastProgress {
  total: number;                 // Total messages
  sent: number;                  // Messages sent (successful + failed)
  failed: number;                // Failed messages
  pending: number;               // Remaining messages
  percentage: number;            // Completion percentage (0-100)
  startTime: number;             // Start timestamp
  currentTime: number;           // Current timestamp
  estimatedTimeRemaining?: number; // Estimated ms remaining
}

MessageSendResult

interface MessageSendResult {
  phoneNumber: string;
  success: boolean;
  messageId?: string;            // WhatsApp message ID if successful
  error?: string;                // Error message if failed
  timestamp: number;             // Send attempt timestamp
}

BroadcastRecipient

interface BroadcastRecipient {
  phoneNumber: string;
  variables?: Record<string, string>; // Template variable substitutions
}

Best Practices

1. Use Templates for Marketing

Text messages only work within the 24-hour conversation window. For marketing or notifications outside this window, use approved message templates.
  • ✅ Good
  • ❌ Avoid
// Using templates
await client.sendBulkTemplates(recipients, 'promo_template', 'en_US');

2. Handle Individual Failures

const result = await client.sendBroadcastText(numbers, text);

// Check failed messages
const failedNumbers = result.results
  .filter(r => !r.success)
  .map(r => ({ phone: r.phoneNumber, error: r.error }));

if (failedNumbers.length > 0) {
  console.log('Failed messages:', failedNumbers);
  // Implement retry logic or alert system
}

3. Respect User Preferences

// Filter opt-out users before sending
const activeUsers = await getActiveSubscribers(); // Your DB logic
const phoneNumbers = activeUsers.map(u => u.phoneNumber);

await client.sendBroadcastText(phoneNumbers, message);

4. Optimize Batch Size

// For smaller lists (< 500), use larger batches
await client.sendBroadcastText(numbers, text, {
  batchSize: 100
});

// For very large lists (10k+), use default or smaller batches
await client.sendBroadcastText(numbers, text, {
  batchSize: 50  // More frequent progress updates
});

5. Monitor and Log

const result = await client.sendBroadcastText(numbers, text, {
  onProgress: (progress) => {
    // Log to your monitoring system
    logger.info('Broadcast progress', {
      broadcastId: result.broadcastId,
      progress: progress.percentage,
      failed: progress.failed
    });
  }
});

// Store results for analysis
await db.saveBroadcastResults(result);

Examples

Example 1: Simple Broadcast with Progress Tracking

async function sendAnnouncementBroadcast() {
  const phoneNumbers = [
    '+1234567890',
    '+0987654321',
    '+1122334455',
    // Add more recipients...
  ];

  const message = 'Hello! This is an important announcement for all our customers. 🎉';

  try {
    const result = await client.sendBroadcastText(phoneNumbers, message, {
      batchSize: 50,
      onProgress: (progress) => {
        console.log(`📊 Progress: ${progress.sent}/${progress.total} (${progress.percentage.toFixed(1)}%)`);
        console.log(`   ✅ Successful: ${progress.sent - progress.failed}`);
        console.log(`   ❌ Failed: ${progress.failed}`);
        if (progress.estimatedTimeRemaining) {
          console.log(`   ⏱️  ETA: ${Math.round(progress.estimatedTimeRemaining / 1000)}s\n`);
        }
      },
    });

    console.log('\n✅ Broadcast completed!');
    console.log(`   Total: ${result.total}`);
    console.log(`   Successful: ${result.successful}`);
    console.log(`   Failed: ${result.failed}`);
    console.log(`   Duration: ${(result.duration / 1000).toFixed(2)}s`);

    // Handle failed messages
    if (result.failed > 0) {
      const failedNumbers = result.results
        .filter(r => !r.success)
        .map(r => ({ phone: r.phoneNumber, error: r.error }));

      console.log('\n❌ Failed recipients:', failedNumbers);
      // Implement retry logic here
    }
  } catch (error) {
    console.error('❌ Broadcast failed:', error.message);
  }
}

Example 2: Detailed Message Tracking

async function sendPromotionWithTracking() {
  const phoneNumbers = ['+1234567890', '+0987654321', '+1122334455'];
  const message = '🎁 Special offer! Use code WELCOME10 for 10% off.';

  const successfulRecipients = [];
  const failedRecipients = [];

  const result = await client.sendBroadcastText(phoneNumbers, message, {
    onMessageSent: (msgResult) => {
      if (msgResult.success) {
        successfulRecipients.push(msgResult.phoneNumber);
        console.log(`✅ Sent to ${msgResult.phoneNumber} (ID: ${msgResult.messageId})`);
      } else {
        failedRecipients.push(msgResult);
        console.log(`❌ Failed: ${msgResult.phoneNumber} - ${msgResult.error}`);
      }
    },
    onProgress: (progress) => {
      // Visual progress bar
      const bar = '█'.repeat(Math.floor(progress.percentage / 5)) +
                  '░'.repeat(20 - Math.floor(progress.percentage / 5));
      console.log(`[${bar}] ${progress.percentage.toFixed(1)}%`);
    },
  });

  console.log('\n📊 Final Report:');
  console.log(`   Broadcast ID: ${result.broadcastId}`);
  console.log(`   Success Rate: ${((result.successful / result.total) * 100).toFixed(1)}%`);
  console.log(`   Duration: ${(result.duration / 1000).toFixed(2)}s`);
}

Example 3: Personalized Template Broadcast

async function sendOrderConfirmations() {
  // Each recipient with personalized data
  const recipients = [
    {
      phoneNumber: '+1234567890',
      variables: {
        name: 'John Doe',
        orderNumber: 'ORD-12345',
        deliveryDate: 'December 25, 2024',
      },
    },
    {
      phoneNumber: '+0987654321',
      variables: {
        name: 'Jane Smith',
        orderNumber: 'ORD-12346',
        deliveryDate: 'December 26, 2024',
      },
    },
  ];

  try {
    const result = await client.sendBulkTemplates(
      recipients,
      'order_confirmation', // Your pre-approved template name
      'en_US',
      {
        batchSize: 30,
        onProgress: (progress) => {
          console.log(`📤 Sending: ${progress.sent}/${progress.total}`);
        },
        onMessageSent: (msgResult) => {
          if (msgResult.success) {
            console.log(`✅ Template sent to ${msgResult.phoneNumber}`);
          } else {
            console.log(`❌ Failed: ${msgResult.phoneNumber} - ${msgResult.error}`);
          }
        },
      }
    );

    console.log('\n✅ Template broadcast completed!');
    console.log(`   Success: ${result.successful}/${result.total}`);
    console.log(`   Avg time per message: ${(result.duration / result.total).toFixed(0)}ms`);
  } catch (error) {
    console.error('❌ Template broadcast failed:', error.message);
  }
}
Template Setup Required: Before using template broadcasts, ensure your template is approved in WhatsApp Business Manager. Template approval can take 1-48 hours.

Limitations

Important Limitations
  1. 24-Hour Window: Text broadcasts require an active conversation window (user messaged you in last 24h). Use templates otherwise.
  2. Template Approval: Template messages must be pre-approved by Meta. This can take 1-48 hours.
  3. Rate Limits: While the SDK handles rate limiting, sending to 100k+ recipients will take considerable time (~20+ minutes).
  4. No Retry Logic: Currently, failed messages are not automatically retried. Implement your own retry logic if needed.
  5. Single Broadcast: Only one broadcast can run at a time per client instance.

Troubleshooting

Solution: The SDK should handle this automatically. If you’re still seeing errors:
  • Reduce batchSize option
  • Increase delayBetweenBatches
  • Ensure you’re not running multiple broadcasts simultaneously
Possible causes:
  • Invalid access token or phone number ID
  • Messages sent to users outside 24-hour window (use templates)
  • Phone numbers incorrectly formatted
This is normal: For 10,000 recipients at 80 msg/sec, expect ~2 minutes minimum. The SDK prioritizes delivery success over speed.

Migration Guide

If you’re upgrading from a previous version without broadcast support:
// OLD: Sending one by one
for (const phone of phoneNumbers) {
  await client.sendText(phone, message);
}

// NEW: Broadcast
await client.sendBroadcastText(phoneNumbers, message);

Next Steps

Version

Broadcast feature added in version 1.6.0
I