Analytics

See what the carrier saw.

Set up in:
Cursor

Every send produces a carrier delivery receipt. Bird turns those receipts into delivery, failure, and latency metrics, broken down by country, carrier, and sender, in the dashboard and through a stats API you can query from your own code.

send-notification.ts
202 · 0.4s
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

const { data, error } = await bird.sms.send({
  from:     "Bird",
  to:       "+15005550006",
  text:     "Your order #4821 has shipped. Track it: bird.ly/t/4821x",
  category: "transactional",
}).safe();

if (error) throw error;
console.log(data.id);
// → "sms_4kT01Lq2m..."
Hi Ada, reminder of your appointment tomorrow at 14:30 with Dr. Kowalski.
Your order #4821 has shipped. Track it: bird.ly/t/4821x
Your subscription renews on 3 Sep for €12/mo. Manage it here: bird.ly/account

The reporting side of the same API.

Nothing new to instrument.

Analytics is the reporting side of the Bird SMS API. You already send through it and already receive a delivery webhook on every state change; analytics is Bird keeping the count for you, so you can ask how a campaign delivered without standing up a warehouse to hold the events first.

What a delivery receipt tells you.

Measured from the carrier, not inferred.

  1. 01

    Delivery rate.

    The share of sends the carrier confirmed delivered, against what was submitted. Track it per country and per sender, not just as one site-wide number that hides the route that's quietly dropping.

  2. 02

    Failure reasons by carrier.

    Failed sends carry the carrier's reason code, grouped by destination carrier (MCC/MNC). A spike is usually one operator rejecting one sender ID, which is a registration fix, not a platform outage.

  3. 03

    Segments and cost.

    Each message reports its encoding and segment count, so volume rolls up into the segments you were actually billed for. A run that went Unicode and doubled its segments shows up here, not on the invoice.

  4. 04

    Latency to delivery.

    The time from submission to the delivery receipt, as a distribution rather than an average. Globally about 95% of messages confirm in under 2.5 seconds; the tail is where a degraded route announces itself.

Query the numbers from your own code.

The stats API takes a time range and a groupBy, and returns the rolled-up counts. Group by country and carrier to find the route that's underperforming, by sender to see which of your IDs a carrier trusts. The same aggregation backs the dashboard charts, so a number you screenshot matches a number you can pull on a schedule.

delivery-by-route.ts
200 · stats
const { data, error } = await bird.sms.stats
  .query({
    from:    "2026-06-01",
    to:      "2026-06-26",
    groupBy: ["country", "carrier"],
    metrics: ["sent", "delivered", "failed", "p95_latency_ms"],
  })
  .safe();

if (error) throw error;

console.log(data.rows[0]);
// → {
//     country:        "BR",
//     carrier:        "Vivo",          // MCC/MNC 724/06
//     sent:           48213,
//     delivered:      47190,
//     failed:         1023,
//     p95_latency_ms: 2310,
//   }

Pull the timeline for one message.

Aggregates answer how a campaign did; a support ticket asks about one text. Pass a single message ID to the events endpoint and you get its whole life in order: queued, sent, the carrier delivery receipt or the failure, each stamped with a time and, when it failed, the carrier's own reason code.

message-timeline.ts
200 · events
const { data } = await bird.sms
  .events("sms_4kT01Lq2m...")
  .safe();

console.log(data.events);
// → [
//     { type: "sms.accepted",  at: "2026-06-26T10:00:00.110Z" },
//     { type: "sms.sent",      at: "2026-06-26T10:00:00.640Z" },
//     { type: "sms.delivered", at: "2026-06-26T10:00:02.300Z" },
//   ]

Slice the same sends however the question is shaped.

Every breakdown reads from the same delivery receipts; the groupBy just changes the lens.

DimensionWhat it tells you
CountryWhere delivery holds and where a destination is dragging the global rate down.
Carrier (MCC/MNC)Which operator inside a country is rejecting traffic, down to the network code.
SenderHow each of your sender IDs or numbers is trusted, since reputation is per-sender.
Time bucketWhen a rate moved, so a drop lines up against a deploy, a registration change, or an outage.

Go deeper in the docs.

Build your own store from the delivery webhooks, read the deliverability guide for what the failure codes mean, and reconcile counts against billing and usage.

The receipts come from the routing layer.

A delivery receipt is only as good as the path that produced it: routing chooses the carrier link each message takes and hands back the DLR these metrics are built from. If you run two-way numbers, inbound messages are counted here too, so a reply volume sits next to the delivery rate that earned it.

The metrics ship with the API that produces them.

Analytics isn't a separate product to buy. Send through the Bird SMS API and the delivery, failure, and latency reporting is already there, on infrastructure that carries about 40% of the world's commercial SMS.

Start with one channel.
Add the others when you're ready.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Using Claude Code, Cursor, or Codex? Copy a setup prompt and your agent installs the Bird CLI and skills for you. Pick yours:

Cursor