> ## Documentation Index
> Fetch the complete documentation index at: https://developers.nativehub.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Bulk SMS

> Send SMS messages to multiple recipients efficiently using the bulk endpoint

## Overview

The bulk SMS endpoint allows you to send the same message to multiple recipients in a single API call. Each message is tracked individually with its own message ID.

## Send Bulk Messages

Use the `/messages/bulk` endpoint to send SMS to multiple recipients.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api-message.nativehub.live/api/v1/messages/bulk \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "recipients": [
        "+8801712345678",
        "+8801798765432",
        "+8801611122233"
      ],
      "from": "BRAND",
      "body": "Hello! This is a bulk message from NativeMessage."
    }'
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');

  const response = await axios.post(
    'https://api-message.nativehub.live/api/v1/messages/bulk',
    {
      recipients: [
        '+8801712345678',
        '+8801798765432',
        '+8801611122233'
      ],
      from: 'BRAND',
      body: 'Hello! This is a bulk message from NativeMessage.'
    },
    {
      headers: {
        'Authorization': `Bearer ${access_token}`,
        'Content-Type': 'application/json'
      }
    }
  );

  const { batch_id, queued, failed, messages } = response.data;
  console.log(`Batch ID: ${batch_id}`);
  console.log(`Queued: ${queued}, Failed: ${failed}`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api-message.nativehub.live/api/v1/messages/bulk',
      headers={
          'Authorization': f'Bearer {access_token}',
          'Content-Type': 'application/json'
      },
      json={
          'recipients': [
              '+8801712345678',
              '+8801798765432',
              '+8801611122233'
          ],
          'from': 'BRAND',
          'body': 'Hello! This is a bulk message from NativeMessage.'
      }
  )

  data = response.json()
  print(f"Batch ID: {data['batch_id']}")
  print(f"Queued: {data['queued']}, Failed: {data['failed']}")
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://api-message.nativehub.live/api/v1/messages/bulk');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
      'recipients' => [
          '+8801712345678',
          '+8801798765432',
          '+8801611122233'
      ],
      'from' => 'BRAND',
      'body' => 'Hello! This is a bulk message from NativeMessage.'
  ]));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Content-Type: application/json',
      'Authorization: Bearer ' . $access_token
  ]);

  $response = curl_exec($ch);
  $data = json_decode($response, true);
  echo "Batch ID: " . $data['batch_id'] . "\n";
  echo "Queued: " . $data['queued'] . ", Failed: " . $data['failed'];
  ```

  ```go Go theme={null}
  package main

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

  type BulkMessage struct {
      Recipients []string `json:"recipients"`
      From       string   `json:"from"`
      Body       string   `json:"body"`
  }

  type BulkResponse struct {
      BatchID  string           `json:"batch_id"`
      Queued   int              `json:"queued"`
      Failed   int              `json:"failed"`
      Rejected int              `json:"rejected"`
      Messages []MessageResponse `json:"messages"`
  }

  func sendBulkSMS(accessToken string) {
      msg, _ := json.Marshal(BulkMessage{
          Recipients: []string{
              "+8801712345678",
              "+8801798765432",
              "+8801611122233",
          },
          From: "BRAND",
          Body: "Hello! This is a bulk message from NativeMessage.",
      })

      req, _ := http.NewRequest(
          "POST",
          "https://api-message.nativehub.live/api/v1/messages/bulk",
          bytes.NewBuffer(msg),
      )
      req.Header.Set("Authorization", "Bearer "+accessToken)
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, _ := client.Do(req)

      var bulkResp BulkResponse
      json.NewDecoder(resp.Body).Decode(&bulkResp)
  }
  ```

  ```java Java theme={null}
  import java.net.http.*;
  import java.net.URI;
  import java.util.List;
  import com.google.gson.Gson;

  public class SendBulkSMS {
      public static void main(String[] args) throws Exception {
          HttpClient client = HttpClient.newHttpClient();

          String requestBody = new Gson().toJson(Map.of(
              "recipients", List.of(
                  "+8801712345678",
                  "+8801798765432",
                  "+8801611122233"
              ),
              "from", "BRAND",
              "body", "Hello! This is a bulk message from NativeMessage."
          ));

          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create("https://api-message.nativehub.live/api/v1/messages/bulk"))
              .header("Authorization", "Bearer " + accessToken)
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(requestBody))
              .build();

          HttpResponse<String> response = client.send(request,
              HttpResponse.BodyHandlers.ofString());

          System.out.println(response.body());
      }
  }
  ```

  ```csharp C# theme={null}
  using System.Net.Http;
  using System.Text;
  using System.Text.Json;

  var client = new HttpClient();
  client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");

  var content = new StringContent(
      JsonSerializer.Serialize(new {
          recipients = new[] {
              "+8801712345678",
              "+8801798765432",
              "+8801611122233"
          },
          from = "BRAND",
          body = "Hello! This is a bulk message from NativeMessage."
      }),
      Encoding.UTF8,
      "application/json"
  );

  var response = await client.PostAsync(
      "https://api-message.nativehub.live/api/v1/messages/bulk",
      content
  );

  var data = await response.Content.ReadFromJsonAsync<dynamic>();
  Console.WriteLine($"Batch ID: {data.batch_id}");
  Console.WriteLine($"Queued: {data.queued}, Failed: {data.failed}");
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'

  uri = URI('https://api-message.nativehub.live/api/v1/messages/bulk')
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(uri.path)
  request['Authorization'] = "Bearer #{access_token}"
  request['Content-Type'] = 'application/json'
  request.body = {
    recipients: [
      '+8801712345678',
      '+8801798765432',
      '+8801611122233'
    ],
    from: 'BRAND',
    body: 'Hello! This is a bulk message from NativeMessage.'
  }.to_json

  response = http.request(request)
  data = JSON.parse(response.body)
  puts "Batch ID: #{data['batch_id']}"
  puts "Queued: #{data['queued']}, Failed: #{data['failed']}"
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "batch_id": "batch_9x8y7z6w5v4u",
  "queued": 3,
  "failed": 0,
  "rejected": 0,
  "messages": [
    {
      "id": "msg_1a2b3c4d5e6f",
      "to": "+8801712345678",
      "status": "queued",
      "part_count": 1
    },
    {
      "id": "msg_2b3c4d5e6f7g",
      "to": "+8801798765432",
      "status": "queued",
      "part_count": 1
    },
    {
      "id": "msg_3c4d5e6f7g8h",
      "to": "+8801611122233",
      "status": "queued",
      "part_count": 1
    }
  ]
}
```

## Understanding Batch ID

The `batch_id` is a unique identifier for your bulk send operation. Use it to:

* Track all messages from a single bulk request
* Filter delivery reports by batch
* Monitor campaign performance

## Track Bulk Delivery

You can track individual messages using their message IDs or query all messages from a batch.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api-message.nativehub.live/api/v1/messages?batch_id=batch_9x8y7z6w5v4u" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');

  const response = await axios.get(
    'https://api-message.nativehub.live/api/v1/messages',
    {
      params: {
        batch_id: 'batch_9x8y7z6w5v4u'
      },
      headers: {
        'Authorization': `Bearer ${access_token}`
      }
    }
  );

  const messages = response.data.messages;
  const delivered = messages.filter(m => m.status === 'delivered').length;
  console.log(`Delivered: ${delivered}/${messages.length}`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api-message.nativehub.live/api/v1/messages',
      params={
          'batch_id': 'batch_9x8y7z6w5v4u'
      },
      headers={
          'Authorization': f'Bearer {access_token}'
      }
  )

  messages = response.json()['messages']
  delivered = sum(1 for m in messages if m['status'] == 'delivered')
  print(f"Delivered: {delivered}/{len(messages)}")
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://api-message.nativehub.live/api/v1/messages?batch_id=batch_9x8y7z6w5v4u');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer ' . $access_token
  ]);

  $response = curl_exec($ch);
  $data = json_decode($response, true);
  $messages = $data['messages'];
  $delivered = count(array_filter($messages, fn($m) => $m['status'] === 'delivered'));
  echo "Delivered: $delivered/" . count($messages);
  ```

  ```go Go theme={null}
  package main

  import (
      "encoding/json"
      "net/http"
      "net/url"
  )

  type MessagesResponse struct {
      Messages []MessageResponse `json:"messages"`
  }

  func trackBatch(accessToken, batchID string) {
      params := url.Values{}
      params.Add("batch_id", batchID)

      req, _ := http.NewRequest(
          "GET",
          "https://api-message.nativehub.live/api/v1/messages?"+params.Encode(),
          nil,
      )
      req.Header.Set("Authorization", "Bearer "+accessToken)

      client := &http.Client{}
      resp, _ := client.Do(req)

      var msgResp MessagesResponse
      json.NewDecoder(resp.Body).Decode(&msgResp)
  }
  ```

  ```java Java theme={null}
  import java.net.http.*;
  import java.net.URI;

  public class TrackBatch {
      public static void main(String[] args) throws Exception {
          HttpClient client = HttpClient.newHttpClient();

          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create("https://api-message.nativehub.live/api/v1/messages?batch_id=batch_9x8y7z6w5v4u"))
              .header("Authorization", "Bearer " + accessToken)
              .GET()
              .build();

          HttpResponse<String> response = client.send(request,
              HttpResponse.BodyHandlers.ofString());

          System.out.println(response.body());
      }
  }
  ```

  ```csharp C# theme={null}
  using System.Net.Http;

  var client = new HttpClient();
  client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");

  var response = await client.GetAsync(
      "https://api-message.nativehub.live/api/v1/messages?batch_id=batch_9x8y7z6w5v4u"
  );

  var data = await response.Content.ReadFromJsonAsync<dynamic>();
  var messages = data.messages;
  Console.WriteLine($"Total messages: {messages.Count}");
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'

  uri = URI('https://api-message.nativehub.live/api/v1/messages')
  uri.query = URI.encode_www_form({ batch_id: 'batch_9x8y7z6w5v4u' })

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = "Bearer #{access_token}"

  response = http.request(request)
  data = JSON.parse(response.body)
  messages = data['messages']
  delivered = messages.count { |m| m['status'] == 'delivered' }
  puts "Delivered: #{delivered}/#{messages.length}"
  ```
</CodeGroup>

## Handling Failures

Some messages may fail due to invalid phone numbers or other issues. The response includes details for each failed message.

```json theme={null}
{
  "batch_id": "batch_9x8y7z6w5v4u",
  "queued": 2,
  "failed": 1,
  "rejected": 0,
  "messages": [
    {
      "id": "msg_1a2b3c4d5e6f",
      "to": "+8801712345678",
      "status": "queued",
      "part_count": 1
    },
    {
      "id": "msg_2b3c4d5e6f7g",
      "to": "+8801798765432",
      "status": "queued",
      "part_count": 1
    },
    {
      "to": "+880INVALID",
      "status": "failed",
      "error": "Invalid phone number format"
    }
  ]
}
```

<Warning>
  Always validate phone numbers before sending to reduce failure rates and optimize your messaging costs.
</Warning>

## Best Practices

<AccordionGroup>
  <Accordion title="Optimal Batch Sizes">
    * Keep batches under 10,000 recipients per request
    * For larger campaigns, split into multiple batches
    * Process batches sequentially to avoid rate limits
  </Accordion>

  <Accordion title="Error Handling">
    * Check the `failed` and `rejected` counts in the response
    * Log message IDs for successful sends
    * Retry failed messages with corrected data
    * Don't retry messages with invalid phone numbers
  </Accordion>

  <Accordion title="Performance Tips">
    * Send during off-peak hours for better delivery rates
    * Use webhooks instead of polling for status updates
    * Store batch\_id for reporting and analytics
    * Monitor delivery rates per batch to identify issues
  </Accordion>

  <Accordion title="Rate Limits">
    * Default: 100 requests per minute
    * Bulk endpoint: 50 requests per minute
    * Contact support for higher limits on enterprise plans
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Receive Delivery Reports" icon="webhook" href="/quickstart/receive-delivery-reports">
    Set up webhooks to track message delivery in real-time
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/messages/send-bulk">
    Explore all bulk messaging parameters and options
  </Card>
</CardGroup>
