Skip to main content

Twilio vs Vonage vs Stream: Communication APIs and Chat SDKs (2026)

·PkgPulse Team

TL;DR

Twilio is the full communications platform — SMS, voice, video, email (SendGrid), WhatsApp, programmable messaging, the industry standard for communication APIs. Vonage (formerly Nexmo) is the multi-channel communications API — SMS, voice, video, verify (2FA), conversations, competitive pricing. Stream is the chat and activity feed SDK — pre-built chat UI components, real-time messaging, activity feeds, moderation, purpose-built for in-app chat. In 2026: Twilio for full communications (SMS + voice + video), Vonage for multi-channel messaging at competitive pricing, Stream for building in-app chat and activity feeds.

Key Takeaways

  • Twilio: twilio SDK ~2M weekly downloads — SMS, voice, video, email, WhatsApp
  • Vonage: @vonage/server-sdk ~200K weekly downloads — SMS, voice, verify, video
  • Stream: stream-chat ~150K weekly downloads — in-app chat, UI components, moderation
  • Twilio is the most comprehensive but also the most expensive
  • Vonage offers similar features with competitive pricing
  • Stream is purpose-built for chat — not a general communications platform

Twilio

Twilio — full communications platform:

Send SMS

import twilio from "twilio"

const client = twilio(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
)

// Send SMS:
const message = await client.messages.create({
  body: "Your package react@19.0.0 has been published!",
  from: "+15551234567",
  to: "+15559876543",
})

console.log(`Message SID: ${message.sid}`)
console.log(`Status: ${message.status}`)

Voice call

import twilio from "twilio"

const client = twilio(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
)

// Make a call:
const call = await client.calls.create({
  url: "https://api.pkgpulse.com/twiml/alert",
  to: "+15559876543",
  from: "+15551234567",
})

// TwiML response (Express):
app.post("/twiml/alert", (req, res) => {
  const VoiceResponse = twilio.twiml.VoiceResponse
  const response = new VoiceResponse()

  response.say({ voice: "alice" },
    "Alert: a critical security vulnerability was found in your dependencies."
  )
  response.pause({ length: 1 })
  response.say("Press 1 to acknowledge, or 2 to escalate.")
  response.gather({
    numDigits: 1,
    action: "/twiml/handle-input",
  })

  res.type("text/xml")
  res.send(response.toString())
})

Verify (2FA)

import twilio from "twilio"

const client = twilio(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
)

// Send verification code:
const verification = await client.verify.v2
  .services(process.env.TWILIO_VERIFY_SID!)
  .verifications.create({
    to: "+15559876543",
    channel: "sms",  // "sms", "call", "email", "whatsapp"
  })

console.log(`Status: ${verification.status}`)  // "pending"

// Check verification code:
const check = await client.verify.v2
  .services(process.env.TWILIO_VERIFY_SID!)
  .verificationChecks.create({
    to: "+15559876543",
    code: "123456",
  })

console.log(`Verified: ${check.status}`)  // "approved" or "pending"

WhatsApp

import twilio from "twilio"

const client = twilio(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
)

// Send WhatsApp message:
await client.messages.create({
  body: "Your PkgPulse weekly report is ready!",
  from: "whatsapp:+14155238886",
  to: "whatsapp:+15559876543",
})

// Send WhatsApp template:
await client.messages.create({
  from: "whatsapp:+14155238886",
  to: "whatsapp:+15559876543",
  contentSid: "HXXXXXXXXXXXXXXXXXXX",  // Template SID
  contentVariables: JSON.stringify({
    "1": "react",
    "2": "19.0.0",
  }),
})

Vonage

Vonage — multi-channel communications:

Send SMS

import { Vonage } from "@vonage/server-sdk"

const vonage = new Vonage({
  apiKey: process.env.VONAGE_API_KEY!,
  apiSecret: process.env.VONAGE_API_SECRET!,
})

// Send SMS:
const response = await vonage.sms.send({
  to: "15559876543",
  from: "PkgPulse",
  text: "Your package react@19.0.0 has been published!",
})

console.log(`Message ID: ${response.messages[0]["message-id"]}`)
console.log(`Status: ${response.messages[0].status}`)

Voice call

import { Vonage } from "@vonage/server-sdk"

const vonage = new Vonage({
  applicationId: process.env.VONAGE_APP_ID!,
  privateKey: process.env.VONAGE_PRIVATE_KEY!,
})

// Make a call:
const response = await vonage.voice.createOutboundCall({
  to: [{ type: "phone", number: "15559876543" }],
  from: { type: "phone", number: "15551234567" },
  ncco: [
    {
      action: "talk",
      text: "Alert: a critical vulnerability was found in your dependencies.",
      voiceName: "Amy",
    },
    {
      action: "input",
      type: ["dtmf"],
      dtmf: { maxDigits: 1 },
      eventUrl: ["https://api.pkgpulse.com/vonage/input"],
    },
  ],
})

console.log(`Call UUID: ${response.uuid}`)

Verify (2FA)

import { Vonage } from "@vonage/server-sdk"
import { Auth } from "@vonage/auth"

const vonage = new Vonage(new Auth({
  apiKey: process.env.VONAGE_API_KEY!,
  apiSecret: process.env.VONAGE_API_SECRET!,
}))

// Start verification:
const response = await vonage.verify.start({
  number: "15559876543",
  brand: "PkgPulse",
  code_length: 6,
})

const requestId = response.request_id

// Check code:
const check = await vonage.verify.check(requestId, "123456")

if (check.status === "0") {
  console.log("Verification successful!")
} else {
  console.log("Verification failed:", check.error_text)
}

Messages API (multi-channel)

import { Vonage } from "@vonage/server-sdk"

const vonage = new Vonage({
  applicationId: process.env.VONAGE_APP_ID!,
  privateKey: process.env.VONAGE_PRIVATE_KEY!,
})

// SMS via Messages API:
await vonage.messages.send({
  message_type: "text",
  text: "Package update available!",
  to: "15559876543",
  from: "15551234567",
  channel: "sms",
})

// WhatsApp:
await vonage.messages.send({
  message_type: "text",
  text: "Your weekly report is ready!",
  to: "15559876543",
  from: "14155238886",
  channel: "whatsapp",
})

// Viber:
await vonage.messages.send({
  message_type: "text",
  text: "New version alert!",
  to: "15559876543",
  from: "PkgPulse",
  channel: "viber_service",
})

Stream

Stream — in-app chat and feeds:

Chat client setup

import { StreamChat } from "stream-chat"

// Server-side (generate tokens):
const serverClient = StreamChat.getInstance(
  process.env.STREAM_API_KEY!,
  process.env.STREAM_API_SECRET!
)

// Create user token:
const token = serverClient.createToken("user-123")

// Client-side:
const client = StreamChat.getInstance(process.env.STREAM_API_KEY!)
await client.connectUser(
  { id: "user-123", name: "Royce", image: "/avatar.png" },
  token
)

Channels and messaging

import { StreamChat } from "stream-chat"

const client = StreamChat.getInstance(apiKey)

// Create channel:
const channel = client.channel("messaging", "project-pkgpulse", {
  name: "PkgPulse Team",
  members: ["user-123", "user-456", "user-789"],
  image: "/team-avatar.png",
})

await channel.create()

// Send message:
await channel.sendMessage({
  text: "New version of react just dropped! 🎉",
  user_id: "user-123",
})

// Send with attachment:
await channel.sendMessage({
  text: "Check out this comparison chart",
  attachments: [{
    type: "image",
    image_url: "https://pkgpulse.com/charts/react-vs-vue.png",
    title: "React vs Vue Downloads",
  }],
})

// React to message:
await channel.sendReaction(messageId, {
  type: "like",
  user_id: "user-456",
})

React components

import {
  Chat, Channel, ChannelList, ChannelHeader,
  MessageList, MessageInput, Thread, Window,
} from "stream-chat-react"
import { StreamChat } from "stream-chat"
import "stream-chat-react/dist/css/v2/index.css"

function ChatApp() {
  const client = StreamChat.getInstance(apiKey)

  return (
    <Chat client={client} theme="str-chat__theme-dark">
      <ChannelList
        filters={{ type: "messaging", members: { $in: ["user-123"] } }}
        sort={{ last_message_at: -1 }}
      />
      <Channel>
        <Window>
          <ChannelHeader />
          <MessageList />
          <MessageInput />
        </Window>
        <Thread />
      </Channel>
    </Chat>
  )
}

Moderation and events

import { StreamChat } from "stream-chat"

const client = StreamChat.getInstance(apiKey, apiSecret)

// Auto-moderation:
await client.updateAppSettings({
  auto_moderation_enabled: true,
  auto_moderation_config: {
    platform: "AI",
    rules: [
      {
        action: "flag",
        word_list: ["spam", "abuse"],
      },
    ],
  },
})

// Ban user:
await client.banUser("spam-user", {
  banned_by_id: "admin-123",
  reason: "Spam messages",
  timeout: 60 * 24,  // 24 hours
})

// Listen for events:
channel.on("message.new", (event) => {
  console.log("New message:", event.message?.text)
})

channel.on("typing.start", (event) => {
  console.log(`${event.user?.name} is typing...`)
})

channel.on("message.read", (event) => {
  console.log(`${event.user?.name} read messages`)
})

Feature Comparison

FeatureTwilioVonageStream
SMS
Voice calls
Video
WhatsApp
In-app chat✅ (Conversations)✅ (Conversations)✅ (core feature)
Chat UI components✅ (React, React Native)
Activity feeds
Moderation✅ (AI-powered)
2FA/Verify
Email✅ (SendGrid)
Webhooks
TypeScript
Pricing modelPer-message/minutePer-message/minutePer MAU
Free tierTrial creditsTrial credits25 MAU

When to Use Each

Use Twilio if:

  • Need a full communications platform (SMS + voice + video + email)
  • Building notification systems with multiple channels
  • Need WhatsApp Business API
  • Want the largest developer ecosystem and documentation

Use Vonage if:

  • Need SMS, voice, and video at competitive pricing
  • Building multi-channel messaging (SMS, WhatsApp, Viber)
  • Need phone verification (Verify API)
  • Want a Twilio alternative with lower costs

Use Stream if:

  • Building in-app chat or messaging features
  • Want pre-built React chat UI components
  • Need activity feeds (social, notification feeds)
  • Want AI-powered moderation and real-time typing indicators

Methodology

Feature comparison based on twilio v5.x, @vonage/server-sdk v3.x, and stream-chat v8.x as of March 2026.

Compare communication and developer tooling on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.