Skip to content

Send Bulk Messages

Send messages to multiple recipients at once with variable substitution support.

Endpoint

POST /api/v1/messages/bulk

Authentication

Requires API Key authentication via X-API-Key header.

Rate Limiting

Bulk send endpoint is rate limited to 10 requests per 5 minutes to prevent abuse.

Request Body

ParameterTypeRequiredDescription
channelstringYesMessage channel: sms, whatsapp, email, or voice
contentobjectYesMessage content with template variables
content.textstringYesMessage template with placeholders
recipientsarrayYesArray of recipient objects (max 1000 per request)
recipients[].tostringYesRecipient phone/email in E.164 format
recipients[].*anyNoAdditional fields for template variable substitution

Template Variables

Use syntax in your message text. Variables are replaced with values from each recipient object.

Example:

json
{
  "content": {
    "text": "Hi {{name}}, your order {{order_id}} is ready!"
  },
  "recipients": [
    { "to": "+1234567890", "name": "John", "order_id": "12345" },
    { "to": "+0987654321", "name": "Jane", "order_id": "67890" }
  ]
}

Try It Now

Send Bulk Messages

Your API key is stored locally and never sent to our servers. Get an API key →

Example Requests

Basic Bulk SMS

bash
curl -X POST https://api.yebolink.com/api/v1/messages/bulk \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ybk_live_your_api_key" \
  -d '{
    "channel": "sms",
    "content": {
      "text": "Hi {{name}}, welcome to YeboLink!"
    },
    "recipients": [
      { "to": "+1234567890", "name": "John" },
      { "to": "+0987654321", "name": "Jane" },
      { "to": "+1122334455", "name": "Bob" }
    ]
  }'
javascript
const sendBulkSMS = async (template, recipients) => {
  const response = await fetch('https://api.yebolink.com/api/v1/messages/bulk', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.YEBOLINK_API_KEY
    },
    body: JSON.stringify({
      channel: 'sms',
      content: {
        text: template
      },
      recipients
    })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message);
  }

  return await response.json();
};

// Usage
const result = await sendBulkSMS(
  'Hi {{name}}, welcome to YeboLink!',
  [
    { to: '+1234567890', name: 'John' },
    { to: '+0987654321', name: 'Jane' },
    { to: '+1122334455', name: 'Bob' }
  ]
);

console.log(`Queued: ${result.data.queued}, Failed: ${result.data.failed}`);
python
import os
import requests

def send_bulk_sms(template: str, recipients: list):
    """Send bulk SMS messages"""

    response = requests.post(
        'https://api.yebolink.com/api/v1/messages/bulk',
        headers={
            'X-API-Key': os.environ.get('YEBOLINK_API_KEY')
        },
        json={
            'channel': 'sms',
            'content': {
                'text': template
            },
            'recipients': recipients
        }
    )

    response.raise_for_status()
    return response.json()

# Usage
result = send_bulk_sms(
    'Hi {{name}}, welcome to YeboLink!',
    [
        {'to': '+1234567890', 'name': 'John'},
        {'to': '+0987654321', 'name': 'Jane'},
        {'to': '+1122334455', 'name': 'Bob'}
    ]
)

print(f"Queued: {result['data']['queued']}, Failed: {result['data']['failed']}")
php
<?php
function sendBulkSMS($template, $recipients) {
    $curl = curl_init();

    curl_setopt_array($curl, [
        CURLOPT_URL => 'https://api.yebolink.com/api/v1/messages/bulk',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'X-API-Key: ' . getenv('YEBOLINK_API_KEY')
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'channel' => 'sms',
            'content' => [
                'text' => $template
            ],
            'recipients' => $recipients
        ])
    ]);

    $response = curl_exec($curl);
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);

    if ($httpCode !== 201) {
        throw new Exception(json_decode($response)->message);
    }

    return json_decode($response, true);
}

// Usage
$result = sendBulkSMS(
    'Hi {{name}}, welcome to YeboLink!',
    [
        ['to' => '+1234567890', 'name' => 'John'],
        ['to' => '+0987654321', 'name' => 'Jane'],
        ['to' => '+1122334455', 'name' => 'Bob']
    ]
);

echo "Queued: {$result['data']['queued']}, Failed: {$result['data']['failed']}\n";
?>
ruby
require 'net/http'
require 'json'

def send_bulk_sms(template, recipients)
  uri = URI('https://api.yebolink.com/api/v1/messages/bulk')
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(uri.path)
  request['Content-Type'] = 'application/json'
  request['X-API-Key'] = ENV['YEBOLINK_API_KEY']
  request.body = {
    channel: 'sms',
    content: {
      text: template
    },
    recipients: recipients
  }.to_json

  response = http.request(request)

  if response.code.to_i != 201
    error = JSON.parse(response.body)
    raise "Failed: #{error['message']}"
  end

  JSON.parse(response.body)
end

# Usage
result = send_bulk_sms(
  'Hi {{name}}, welcome to YeboLink!',
  [
    { to: '+1234567890', name: 'John' },
    { to: '+0987654321', name: 'Jane' },
    { to: '+1122334455', name: 'Bob' }
  ]
)

puts "Queued: #{result['data']['queued']}, Failed: #{result['data']['failed']}"
go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

type BulkRequest struct {
    Channel    string                   `json:"channel"`
    Content    map[string]string        `json:"content"`
    Recipients []map[string]interface{} `json:"recipients"`
}

type BulkResponse struct {
    Success bool `json:"success"`
    Data    struct {
        Queued     int      `json:"queued"`
        Failed     int      `json:"failed"`
        MessageIDs []string `json:"message_ids"`
    } `json:"data"`
}

func sendBulkSMS(template string, recipients []map[string]interface{}) (*BulkResponse, error) {
    url := "https://api.yebolink.com/api/v1/messages/bulk"

    payload := BulkRequest{
        Channel: "sms",
        Content: map[string]string{
            "text": template,
        },
        Recipients: recipients,
    }

    jsonPayload, _ := json.Marshal(payload)

    req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", os.Getenv("YEBOLINK_API_KEY"))

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result BulkResponse
    json.NewDecoder(resp.Body).Decode(&result)

    return &result, nil
}

func main() {
    result, _ := sendBulkSMS(
        "Hi {{name}}, welcome to YeboLink!",
        []map[string]interface{}{
            {"to": "+1234567890", "name": "John"},
            {"to": "+0987654321", "name": "Jane"},
            {"to": "+1122334455", "name": "Bob"},
        },
    )

    fmt.Printf("Queued: %d, Failed: %d\n", result.Data.Queued, result.Data.Failed)
}

Personalized Campaign

javascript
const sendPersonalizedCampaign = async () => {
  const customers = [
    {
      to: '+1234567890',
      name: 'John Doe',
      discount: '20%',
      code: 'JOHN20',
      expiry: 'Nov 30'
    },
    {
      to: '+0987654321',
      name: 'Jane Smith',
      discount: '15%',
      code: 'JANE15',
      expiry: 'Nov 30'
    }
  ];

  const result = await sendBulkSMS(
    'Hi {{name}}! Get {{discount}} off with code {{code}}. Valid until {{expiry}}. Shop now!',
    customers
  );

  return result;
};
python
def send_personalized_campaign():
    customers = [
        {
            'to': '+1234567890',
            'name': 'John Doe',
            'discount': '20%',
            'code': 'JOHN20',
            'expiry': 'Nov 30'
        },
        {
            'to': '+0987654321',
            'name': 'Jane Smith',
            'discount': '15%',
            'code': 'JANE15',
            'expiry': 'Nov 30'
        }
    ]

    result = send_bulk_sms(
        'Hi {{name}}! Get {{discount}} off with code {{code}}. Valid until {{expiry}}. Shop now!',
        customers
    )

    return result

Response

Success Response (201 Created)

json
{
  "success": true,
  "data": {
    "queued": 3,
    "failed": 0,
    "message_ids": [
      "550e8400-e29b-41d4-a716-446655440000",
      "550e8400-e29b-41d4-a716-446655440001",
      "550e8400-e29b-41d4-a716-446655440002"
    ]
  }
}

Response Fields:

FieldTypeDescription
queuednumberNumber of messages successfully queued
failednumberNumber of messages that failed validation
message_idsarrayArray of message IDs for queued messages

Error Responses

Too Many Recipients (400)

json
{
  "success": false,
  "error": "validation_error",
  "message": "Maximum 1000 recipients per bulk request"
}

Invalid Recipients (400)

json
{
  "success": false,
  "error": "validation_error",
  "message": "All recipients must have a 'to' field"
}

Rate Limit Exceeded (429)

json
{
  "success": false,
  "error": "rate_limit_exceeded",
  "message": "Bulk send limited to 10 requests per 5 minutes"
}

Insufficient Credits (402)

json
{
  "success": false,
  "error": "insufficient_credits",
  "message": "Not enough credits. You need 100 credits but have 50."
}

Best Practices

1. Batch Large Sends

For more than 1000 recipients, split into multiple requests:

javascript
const sendToAllCustomers = async (customers, template) => {
  const BATCH_SIZE = 1000;
  const results = [];

  for (let i = 0; i < customers.length; i += BATCH_SIZE) {
    const batch = customers.slice(i, i + BATCH_SIZE);

    try {
      const result = await sendBulkSMS(template, batch);
      results.push(result);

      // Wait 5 minutes between batches to respect rate limits
      if (i + BATCH_SIZE < customers.length) {
        await new Promise(resolve => setTimeout(resolve, 5 * 60 * 1000));
      }
    } catch (error) {
      console.error(`Batch ${i / BATCH_SIZE + 1} failed:`, error);
    }
  }

  return results;
};

2. Validate Recipients Before Sending

javascript
const validateRecipient = (recipient) => {
  if (!recipient.to) {
    return false;
  }

  const e164Regex = /^\+[1-9]\d{1,14}$/;
  if (!e164Regex.test(recipient.to)) {
    return false;
  }

  return true;
};

const validRecipients = recipients.filter(validateRecipient);

if (validRecipients.length !== recipients.length) {
  console.warn(`Filtered out ${recipients.length - validRecipients.length} invalid recipients`);
}

3. Handle Partial Failures

javascript
const result = await sendBulkSMS(template, recipients);

if (result.data.failed > 0) {
  console.warn(`${result.data.failed} messages failed validation`);
  // Log failed recipients for manual review
}

console.log(`Successfully queued ${result.data.queued} messages`);

4. Estimate Credits Before Sending

javascript
const estimateCreditsCost = (messageTemplate, recipients) => {
  // Assume average message length after template substitution
  const avgLength = 160;
  const segmentsPerMessage = Math.ceil(avgLength / 160);
  const totalCredits = recipients.length * segmentsPerMessage;

  return totalCredits;
};

const estimatedCost = estimateCreditsCost(template, recipients);
console.log(`Estimated cost: ${estimatedCost} credits`);

5. Use Webhooks for Status Tracking

Set up webhooks to track individual message delivery:

javascript
// Your webhook handler
app.post('/webhooks/yebolink', (req, res) => {
  const { event, data } = req.body;

  if (event === 'message.delivered') {
    // Update delivery status in your database
    updateMessageStatus(data.message_id, 'delivered');
  } else if (event === 'message.failed') {
    // Log failure for investigation
    logMessageFailure(data.message_id, data.error_message);
  }

  res.status(200).send('OK');
});

Use Cases

Order Notifications

javascript
const notifyOrderUpdates = async (orders) => {
  const recipients = orders.map(order => ({
    to: order.customer_phone,
    name: order.customer_name,
    order_id: order.id,
    status: order.status,
    tracking_url: `https://example.com/track/${order.id}`
  }));

  return await sendBulkSMS(
    'Hi {{name}}, your order #{{order_id}} is {{status}}. Track it here: {{tracking_url}}',
    recipients
  );
};

Appointment Reminders

javascript
const sendAppointmentReminders = async (appointments) => {
  const recipients = appointments.map(apt => ({
    to: apt.patient_phone,
    name: apt.patient_name,
    date: apt.date,
    time: apt.time,
    doctor: apt.doctor_name
  }));

  return await sendBulkSMS(
    'Hi {{name}}, reminder: Your appointment with Dr. {{doctor}} is on {{date}} at {{time}}.',
    recipients
  );
};

Marketing Campaigns

javascript
const sendPromotionalCampaign = async (subscribers) => {
  const recipients = subscribers.map(sub => ({
    to: sub.phone,
    name: sub.first_name,
    discount_code: generateUniqueCode(sub.id),
    expiry_date: getExpiryDate()
  }));

  return await sendBulkSMS(
    'Hi {{name}}! Exclusive offer: Use code {{discount_code}} for 20% off. Expires {{expiry_date}}.',
    recipients
  );
};

Event Notifications

javascript
const notifyEventAttendees = async (event, attendees) => {
  const recipients = attendees.map(attendee => ({
    to: attendee.phone,
    name: attendee.name,
    event_name: event.name,
    venue: event.venue,
    date: event.date,
    ticket_link: `https://example.com/tickets/${attendee.id}`
  }));

  return await sendBulkSMS(
    'Hi {{name}}! {{event_name}} is happening on {{date}} at {{venue}}. Your ticket: {{ticket_link}}',
    recipients
  );
};

Performance Tips

1. Prepare Recipients in Advance

Fetch and validate all recipient data before calling the API:

javascript
const prepareRecipients = async () => {
  // Fetch from database
  const users = await db.users.findAll({ where: { subscribed: true } });

  // Transform and validate
  return users
    .filter(user => user.phone && isValidE164(user.phone))
    .map(user => ({
      to: user.phone,
      name: user.first_name,
      // ... other template variables
    }));
};

2. Monitor Queue Status

Track your bulk send operations:

javascript
const bulkSendWithTracking = async (template, recipients) => {
  const startTime = Date.now();

  try {
    const result = await sendBulkSMS(template, recipients);

    const duration = Date.now() - startTime;

    console.log({
      total: recipients.length,
      queued: result.data.queued,
      failed: result.data.failed,
      duration_ms: duration,
      credits_used: result.data.queued
    });

    return result;
  } catch (error) {
    console.error('Bulk send failed:', error);
    throw error;
  }
};

Limitations

  • Max Recipients: 1000 per request
  • Rate Limit: 10 requests per 5 minutes
  • Template Variables: Unlimited variables supported
  • Message Length: Same as single messages (160 chars for GSM-7, 70 for Unicode)

Credits & Pricing

  • Each message in the bulk send costs the same as a single message
  • Example: Bulk send to 100 recipients = 100 credits minimum
  • Concatenated messages cost multiple credits per recipient

Next Steps

Built with VitePress