Twilio billing alerts

How to set up Twilio usage alerts before a bill spikes

You get Twilio billing alerts by creating a UsageTrigger resource via the REST API, which points an HTTP webhook back at your server whenever your spend or unit counts cross a threshold you choose. Twilio evaluates these triggers roughly once a minute, limits each account to 1,000 total triggers, and fires the callback within about a minute of your usage passing the limit.

There is no native switch in the console that halts your account before a bill climbs. Twilio has no hard spend cap, and usage data can take some time to reflect in trigger evaluations, so you need to configure your webhooks to catch unusual traffic early and respond on your own side.

How Twilio UsageTriggers work

A UsageTrigger is a webhook that notifies your application when usage thresholds are reached. It is tied to your account or subaccounts and watches a specific category of usage as it accumulates over time.

When you create a trigger, you tell Twilio what metric to track, the limit that should fire it, and where to send the webhook. Triggers can evaluate three fields from your usage records: price (the dollar amount accrued in your currency), count (the number of specific events), or usage (the primary billing units, such as call minutes or messages sent). If you want an alert when SMS volume climbs, you can trigger on message counts. If you want an alert before a monthly invoice surprises you, you track price.

Twilio evaluates triggers frequently - about once a minute - against your live account usage. Once the threshold is exceeded, Twilio sends an asynchronous HTTP POST request to your callback URL, usually within a minute. A trigger only fires once during its recurring interval, so it won't repeatedly call your endpoint every minute after your threshold is broken. If your endpoint responds with a 5xx error, Twilio will quickly retry the callback URL three times.

curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Usage/Triggers.json" \
  -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "CallbackUrl=https://example.com/webhooks/twilio-alert" \
  --data-urlencode "TriggerValue=50.00" \
  --data-urlencode "UsageCategory=totalprice" \
  --data-urlencode "Recurring=monthly" \
  --data-urlencode "TriggerBy=price" \
  --data-urlencode "FriendlyName=Monthly Spend Alert"

Creating a trigger through the REST API

Creating a trigger requires a POST request to https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Usage/Triggers.json. Twilio requires three form-urlencoded parameters: callbackUrl, triggerValue, and usageCategory. Everything else is optional or has defaults.

The triggerValue parameter must be a positive numeric value. You can supply an absolute number like 100 or an offset value like +30 (which you URL-encode as %2B30). The offset tells Twilio to place the threshold 30 units above the current usage level at the moment of creation, which helps when you want an alert after a sudden surge rather than waiting for an absolute monthly mark.

By default, the HTTP method used for the callback is POST, though you can explicitly set callbackMethod to GET or POST. You can also assign a friendlyName up to 64 characters long to keep track of what the alert is for.

Each Twilio account has an upper boundary of 1,000 UsageTriggers. If you run multiple projects or test alerts frequently, you have to watch this limit. Inactive UsageTriggers are not deleted automatically, which means old one-off triggers stay in your account until you delete them yourself.

PropertyAllowed valuesDefaultDescription
recurringdaily, monthly, yearly, alltimeempty (non-recurring)Reset cadence in GMT
triggerBycount, usage, pricedocs do not specifyUsageRecord field being measured
callbackMethodGET, POSTPOSTHTTP method sent to your endpoint
friendlyNameString up to 64 charsemptyHuman-readable label
triggerValuePositive numeric value or offset (e.g. +30)None (required)Threshold level that fires the alert
usageCategorytotalprice, sms, calls, pv, etc.None (required)Twilio product category tracked

Tracking categories: total price, calls, and SMS

When you pick a usageCategory, the metric behaves differently depending on the Twilio product:

  • totalprice: Tracks the overall price across your account. In this record, Usage equals Price, while Count remains empty. Because some costs fall outside standard usage categories, the sum of individual category prices might not strictly equal totalprice.
  • sms: Covers all inbound and outbound SMS messages. Both Count and Usage reflect the number of messages sent.
  • calls: Covers inbound and outbound voice calls. Count represents the number of calls, and Usage represents the number of minutes. This category does not include SIP or client calls.
  • pv: Programmable Video, including TURN. Here, Price captures expenses across all video products, while Count and Usage should be ignored.

If your primary goal is avoiding an expensive invoice, setting totalprice with recurring=monthly is the standard approach. Remember that recurring reset schedules run on GMT time.

Receiving and processing the webhook

When your usage crosses the triggerValue, Twilio fires an asynchronous HTTP request to your callbackUrl. The payload contains details about your trigger state:

  • AccountSid: The 34-character account ID starting with AC.
  • UsageTriggerSid: The 34-character unique identifier for the trigger starting with UT.
  • TriggerValue and CurrentValue: The configured threshold and the level recorded when fired.
  • UsageCategory and TriggerBy: The specific category and field monitored.
  • Recurring: The reset interval (daily, monthly, yearly, or alltime).
  • DateFired: Timestamp indicating when the threshold broke.
  • UsageRecordUri: Relative URI to pull the underlying usage record.
  • IdempotencyToken: A unique token assigned to that specific firing.

Your webhook handler can receive duplicate HTTP requests if network conditions cause delays or retries. You should store the IdempotencyToken on arrival and drop any subsequent incoming request that shares it. Once verified, your endpoint can notify your team via internal channels or push alerts to Slack or Discord.

{
  "AccountSid": "AC00000000000000000000000000000000",
  "UsageTriggerSid": "UT00000000000000000000000000000000",
  "UsageCategory": "totalprice",
  "TriggerBy": "price",
  "TriggerValue": "100.0",
  "CurrentValue": "100.04",
  "Recurring": "monthly",
  "DateFired": "Fri, 06 Nov 2026 12:00:00 +0000",
  "UsageRecordUri": "/2010-04-01/Accounts/AC00000000000000000000000000000000/Usage/Records.json",
  "IdempotencyToken": "d41d8cd98f00b204e9800998ecf8427e"
}

Managing, updating, and deleting triggers

Managing triggers over time takes careful handling because you cannot update either usageCategory or triggerValue on an existing trigger resource.

If you make a POST request to https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Usage/Triggers/{Sid}.json, Twilio only permits updates to callbackMethod, callbackUrl, and friendlyName. If you decide to raise your monthly limit from 50 to 100, you have to create a new trigger via POST and send a DELETE request to remove the old trigger.

# Delete an existing trigger
curl -X DELETE "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Usage/Triggers/$TRIGGER_SID.json" \
  -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"

To view current triggers, send a GET request to https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Usage/Triggers.json. You can read a single trigger by targeting its SID at /Triggers/{Sid}.json. The default page size when listing triggers is 50, and you can specify a pageSize up to 1,000.

Querying UsageRecords directly

If you want to poll usage data instead of waiting for webhooks, or check your totals following an alert, use the UsageRecords API. Send a GET request to https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Usage/Records.json.

By default, this endpoint returns one UsageRecord per category showing all usage accrued all-time. Every record carries an asOf property formatted in GMT as YYYY-MM-DDTHH:MM:SS+00:00, indicating how fresh the evaluation data is.

You can modify what comes back using query parameters, but notice that query parameters on this endpoint are case-sensitive:

  • Category: Restricts output to a specific usage type, like sms or totalprice.
  • startDate and endDate: Filters records using YYYY-MM-DD in GMT, or relative values such as -30days or +30days.
  • includeSubaccounts: Defaults to true, combining records across your master account and all subaccounts. Set it to false if you only want the primary account's direct usage.
  • Subresources: Instead of passing query filters, Twilio provides convenience paths like /Usage/Records/Today, /Usage/Records/Yesterday, /Usage/Records/ThisMonth, /Usage/Records/Daily, and /Usage/Records/Monthly.

Using subaccounts to isolate end-user spikes

If you build multitenant software where end-users send SMS or initiate calls, sharing a single account makes spike detection harder. Twilio supports creating individual subaccounts for each user.

When you pair UsageTriggers with subaccounts, a trigger can monitor the specific subaccount and notify your application as soon as an individual user exceeds their monthly threshold. Because the webhook includes the subaccount's AccountSid, your system knows which user crossed the allocation without parsing the whole company's aggregated log.

Subaccounts also let you contain runaway costs. Twilio does not offer an automated hard spend cap, but if you isolate high-volume tenants into their own subaccounts, you can disable traffic for that tenant without breaking communication for everyone else.

Watching Twilio costs with Costbrake

Managing self-hosted webhooks and polling usage records by hand works, but it leaves gaps if you also pay bills across AI APIs and other cloud providers. Costbrake connects directly to Twilio to track spending spikes and usage patterns alongside 22 other providers, including AWS, OpenAI, and Cloudflare.

Costbrake reads your Twilio usage records using a Restricted API key with only /twilio/billing/usage/read permissions. Checks run hourly on the Free plan and every 5 minutes on Pro and Team (every 2 minutes during active spikes). A spike alert fires when an hour costs more than 3x the 7-day baseline and at least $5 above normal. You can route alerts to email, Slack, Discord, or webhooks.

Beyond alerts, Costbrake includes opt-in brakes on Pro and Team plans. If an anomaly occurs, an admin can manually confirm a brake to block high-risk voice destinations (disabling voice traffic to high-risk special and toll-fraud numbers while keeping normal calling alive) or suspend a specific subaccount without touching the main account. If you want to automate deployment, you can set it up via agent setup.

Questions and answers

Can Twilio automatically suspend my account when a limit is hit?

No. Twilio has no hard spend cap and will not halt your service automatically when an alert fires. You have to handle the webhook on your own server or use manual killswitch tools to suspend subaccounts or block traffic.

Why did my UsageTrigger fail to fire immediately during a spike?

It can take some amount of time for real-world usage to appear in UsageTriggers evaluations. Twilio checks usage roughly once a minute, but upstream ingestion delays mean triggers might fire a few minutes after traffic surges occur.

Can I change the threshold value of an existing trigger?

No. Twilio does not allow editing triggerValue or usageCategory on an existing trigger. You have to send a POST request to make a new trigger and a DELETE request to clear the old one.

How many UsageTriggers can I have active at once?

Each Twilio account can maintain up to 1,000 UsageTriggers. Old or unused triggers are not deleted automatically, so you must remove stale resources using the DELETE endpoint to prevent hitting the limit.

What happens if my webhook endpoint goes down when a trigger fires?

Twilio guarantees a trigger will fire at most once during its recurring interval, but if your server returns a 5xx response, Twilio will quickly retry calling your URL three times before stopping.

Sources

Updated