Skip to content

Batch Messaging

Send SMS messages to multiple recipients efficiently.

Overview

The batch messaging feature allows you to send the same or different messages to multiple recipients in a single API call.

Basic Usage

typescript
import { MsGineClient } from '@msgine/sdk'

const client = new MsGineClient({
  apiToken: process.env.MSGINE_API_TOKEN!
})

const messages = [
  { to: '+256701521269', message: 'Hello Alice!' },
  { to: '+256701521270', message: 'Hello Bob!' },
  { to: '+256701521271', message: 'Hello Charlie!' }
]

const results = await client.sendSmsBatch(messages)

results.forEach((result, index) => {
  console.log(`Message ${index + 1}:`, result.id, result.status)
})

Response

The sendSmsBatch method returns an array of SendSmsResponse objects:

typescript
[
  {
    id: "msg_1234567890",
    status: "pending",
    to: ["+256701521269"],
    content: "Hello Alice!",
    cost: 0.05,
    currency: "USD",
    createdAt: "2024-01-15T10:30:00Z"
  },
  {
    id: "msg_1234567891",
    status: "pending",
    to: ["+256701521270"],
    content: "Hello Bob!",
    cost: 0.05,
    currency: "USD",
    createdAt: "2024-01-15T10:30:01Z"
  },
  {
    id: "msg_1234567892",
    status: "pending",
    to: ["+256701521271"],
    content: "Hello Charlie!",
    cost: 0.05,
    currency: "USD",
    createdAt: "2024-01-15T10:30:02Z"
  }
]

Examples

Send Same Message to Multiple Recipients

typescript
async function notifyUsers(phoneNumbers: string[], message: string) {
  const messages = phoneNumbers.map(to => ({
    to,
    message
  }))

  const results = await client.sendSmsBatch(messages)

  const successful = results.filter(r => r.status !== 'failed').length
  const failed = results.filter(r => r.status === 'failed').length

  console.log(`✅ Sent: ${successful}, ❌ Failed: ${failed}`)

  return results
}

await notifyUsers(
  ['+256701521269', '+256701521270', '+256701521271'],
  'System maintenance tonight at 10 PM'
)

Send Personalized Messages

typescript
interface User {
  phone: string
  name: string
  code: string
}

async function sendVerificationCodes(users: User[]) {
  const messages = users.map(user => ({
    to: user.phone,
    message: `Hi ${user.name}, your code is ${user.code}`
  }))

  const results = await client.sendSmsBatch(messages)

  return results.map((result, index) => ({
    user: users[index],
    messageId: result.id,
    status: result.status
  }))
}

const users = [
  { phone: '+256701521269', name: 'Alice', code: '123456' },
  { phone: '+256701521270', name: 'Bob', code: '789012' }
]

const sent = await sendVerificationCodes(users)

Batch with Error Handling

typescript
import { MsGineError, MsGineValidationError } from '@msgine/sdk'

async function sendBatchSafely(messages: Array<{ to: string; message: string }>) {
  try {
    const results = await client.sendSmsBatch(messages)

    const summary = {
      total: results.length,
      pending: results.filter(r => r.status === 'pending').length,
      sent: results.filter(r => r.status === 'sent').length,
      failed: results.filter(r => r.status === 'failed').length,
      totalCost: results.reduce((sum, r) => sum + r.cost, 0)
    }

    return { success: true, results, summary }
  } catch (error) {
    if (error instanceof MsGineValidationError) {
      console.error('Validation failed:', error.errors.issues)
      return { success: false, error: 'Invalid input' }
    }

    if (error instanceof MsGineError) {
      console.error('API error:', error.message)
      return { success: false, error: error.message }
    }

    return { success: false, error: 'Unknown error' }
  }
}

Limits

Batch Size

  • Maximum: 1000 messages per batch
  • Recommended: 100-500 messages per batch for optimal performance

For larger campaigns, split into multiple batches:

typescript
async function sendLargeCampaign(messages: Array<{ to: string; message: string }>) {
  const BATCH_SIZE = 500
  const results = []

  for (let i = 0; i < messages.length; i += BATCH_SIZE) {
    const batch = messages.slice(i, i + BATCH_SIZE)
    const batchResults = await client.sendSmsBatch(batch)
    results.push(...batchResults)

    console.log(`Sent batch ${i / BATCH_SIZE + 1}/${Math.ceil(messages.length / BATCH_SIZE)}`)

    // Optional: Add delay between batches to avoid rate limits
    if (i + BATCH_SIZE < messages.length) {
      await new Promise(resolve => setTimeout(resolve, 1000))
    }
  }

  return results
}

Best Practices

1. Validate All Recipients

Validate phone numbers before sending:

typescript
function validateBatch(messages: Array<{ to: string; message: string }>) {
  const e164Regex = /^\+[1-9]\d{1,14}$/

  return messages.filter(msg => {
    if (!e164Regex.test(msg.to)) {
      console.warn(`Invalid phone number: ${msg.to}`)
      return false
    }
    if (!msg.message || msg.message.length === 0) {
      console.warn(`Empty message for: ${msg.to}`)
      return false
    }
    return true
  })
}

const validMessages = validateBatch(messages)
const results = await client.sendSmsBatch(validMessages)

2. Monitor Results

Track successful and failed messages:

typescript
const results = await client.sendSmsBatch(messages)

const failed = results.filter(r => r.status === 'failed')
if (failed.length > 0) {
  console.error(`${failed.length} messages failed`)
  // Retry failed messages or log for review
}

3. Implement Retry Logic

Retry failed messages:

typescript
async function sendWithRetry(messages: Array<{ to: string; message: string }>, maxRetries = 3) {
  let results = await client.sendSmsBatch(messages)
  let retries = 0

  while (retries < maxRetries) {
    const failed = results.filter(r => r.status === 'failed')
    if (failed.length === 0) break

    console.log(`Retrying ${failed.length} failed messages (attempt ${retries + 1})`)

    // Extract original messages for failed results
    const retryMessages = failed.map(result => {
      const index = results.indexOf(result)
      return messages[index]
    })

    const retryResults = await client.sendSmsBatch(retryMessages)

    // Update results
    retryResults.forEach((retryResult, i) => {
      const originalIndex = results.findIndex(r => r.id === failed[i].id)
      results[originalIndex] = retryResult
    })

    retries++
  }

  return results
}

4. Calculate Costs

Estimate and track costs:

typescript
const results = await client.sendSmsBatch(messages)

const totalCost = results.reduce((sum, result) => sum + result.cost, 0)
const currency = results[0]?.currency || 'USD'

console.log(`Total cost: ${totalCost.toFixed(2)} ${currency}`)

5. Use Batching for Large Lists

Split large recipient lists:

typescript
async function* sendInBatches(
  messages: Array<{ to: string; message: string }>,
  batchSize = 100
) {
  for (let i = 0; i < messages.length; i += batchSize) {
    const batch = messages.slice(i, i + batchSize)
    const results = await client.sendSmsBatch(batch)
    yield results
  }
}

// Usage
for await (const batchResults of sendInBatches(largeMessageList)) {
  console.log(`Batch sent: ${batchResults.length} messages`)
}

Performance Considerations

Parallel Processing

For very large campaigns, consider parallel processing:

typescript
async function sendParallel(
  messages: Array<{ to: string; message: string }>,
  batchSize = 100,
  parallelBatches = 3
) {
  const batches = []
  for (let i = 0; i < messages.length; i += batchSize) {
    batches.push(messages.slice(i, i + batchSize))
  }

  const results = []
  for (let i = 0; i < batches.length; i += parallelBatches) {
    const parallelPromises = batches
      .slice(i, i + parallelBatches)
      .map(batch => client.sendSmsBatch(batch))

    const batchResults = await Promise.all(parallelPromises)
    results.push(...batchResults.flat())
  }

  return results
}

Rate Limits

Be mindful of rate limits when sending large batches. The SDK will handle retries automatically, but it's better to pace your requests.

Next Steps

Released under the MIT License.