Send to Many People
Surge has two mechanisms for sending to multiple recipients: Blasts for one-time sends and Audiences for reusable contact lists. The choice between them depends on whether the same group of people will receive messages more than once.
Blasts vs Audiences
Use a Blast when you have a list of recipients you're assembling specifically for this send. You pass the recipient list at send time and don't need to maintain it afterward — weekly flash sales, event announcements, one-time notifications.
Use an Audience when the same group of people receives messages regularly. You maintain the audience over time (adding and removing contacts), then blast the whole audience — weekly newsletters, customer cohorts, or geographic segments.
Both send the same underlying message; the difference is how you manage the recipient list.
Blasts
Send to a list of phone numbers
The simplest case: pass E.164 phone numbers directly in the to array.
from surge import Surge
surge = Surge()
blast = surge.blasts.create(
account_id="{account_id}",
from_="pn_01jrzhe8d9enptypyx360pcmxm",
body="Flash sale: 30% off everything this weekend. Shop at acme.com/sale",
to=["+18015551234", "+18025551234", "+18035551234"],
)import Surge from "@surgeapi/node";
const surge = new Surge();
const blast = await surge.blasts.create("{account_id}", {
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Flash sale: 30% off everything this weekend. Shop at acme.com/sale",
to: ["+18015551234", "+18025551234", "+18035551234"],
});require "surge_api"
surge = SurgeAPI::Client.new
blast = surge.blasts.create(
"{account_id}",
from_: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Flash sale: 30% off everything this weekend. Shop at acme.com/sale",
to: ["+18015551234", "+18025551234", "+18035551234"]
)client = Surge.Client.new(System.get_env("SURGE_API_KEY"))
{:ok, blast} =
Surge.Blasts.create(client, "{account_id}", %{
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Flash sale: 30% off everything this weekend. Shop at acme.com/sale",
to: ["+18015551234", "+18025551234", "+18035551234"]
})curl -X POST https://api.surge.app/accounts/{account_id}/blasts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "pn_01jrzhe8d9enptypyx360pcmxm",
"body": "Flash sale: 30% off everything this weekend. Shop at acme.com/sale",
"to": ["+18015551234", "+18025551234", "+18035551234"]
}'Send to contacts
Pass contact IDs (with ctc_ prefix) instead of phone numbers. This links the blast to your contact records for tracking and opt-out management.
blast = surge.blasts.create(
account_id="{account_id}",
from_="pn_01jrzhe8d9enptypyx360pcmxm",
body="Your subscription renews tomorrow. Manage it at acme.com/account",
to=[
"ctc_01jrzhe8d9enptypyx360pcmxa",
"ctc_01jrzhe8d9enptypyx360pcmxb",
"ctc_01jrzhe8d9enptypyx360pcmxc",
],
)const blast = await surge.blasts.create("{account_id}", {
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Your subscription renews tomorrow. Manage it at acme.com/account",
to: [
"ctc_01jrzhe8d9enptypyx360pcmxa",
"ctc_01jrzhe8d9enptypyx360pcmxb",
"ctc_01jrzhe8d9enptypyx360pcmxc",
],
});blast = surge.blasts.create(
"{account_id}",
from_: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Your subscription renews tomorrow. Manage it at acme.com/account",
to: [
"ctc_01jrzhe8d9enptypyx360pcmxa",
"ctc_01jrzhe8d9enptypyx360pcmxb",
"ctc_01jrzhe8d9enptypyx360pcmxc"
]
){:ok, blast} =
Surge.Blasts.create(client, "{account_id}", %{
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Your subscription renews tomorrow. Manage it at acme.com/account",
to: [
"ctc_01jrzhe8d9enptypyx360pcmxa",
"ctc_01jrzhe8d9enptypyx360pcmxb",
"ctc_01jrzhe8d9enptypyx360pcmxc"
]
})curl -X POST https://api.surge.app/accounts/{account_id}/blasts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "pn_01jrzhe8d9enptypyx360pcmxm",
"body": "Your subscription renews tomorrow. Manage it at acme.com/account",
"to": [
"ctc_01jrzhe8d9enptypyx360pcmxa",
"ctc_01jrzhe8d9enptypyx360pcmxb",
"ctc_01jrzhe8d9enptypyx360pcmxc"
]
}'Schedule a blast
Add send_at to queue the blast for a future time:
blast = surge.blasts.create(
account_id="{account_id}",
name="April sale launch",
from_="pn_01jrzhe8d9enptypyx360pcmxm",
body="Acme Corp: Our biggest sale of the year starts now. Shop: acme.com/spring",
to=["+18015551234", "+18025551234", "+18035551234"],
send_at="2026-06-01T09:00:00Z",
)const blast = await surge.blasts.create("{account_id}", {
name: "April sale launch",
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Acme Corp: Our biggest sale of the year starts now. Shop: acme.com/spring",
to: ["+18015551234", "+18025551234", "+18035551234"],
send_at: "2026-06-01T09:00:00Z",
});blast = surge.blasts.create(
"{account_id}",
name: "April sale launch",
from_: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Acme Corp: Our biggest sale of the year starts now. Shop: acme.com/spring",
to: ["+18015551234", "+18025551234", "+18035551234"],
send_at: "2026-06-01T09:00:00Z"
){:ok, blast} =
Surge.Blasts.create(client, "{account_id}", %{
name: "April sale launch",
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Acme Corp: Our biggest sale of the year starts now. Shop: acme.com/spring",
to: ["+18015551234", "+18025551234", "+18035551234"],
send_at: "2026-06-01T09:00:00Z"
})curl -X POST https://api.surge.app/accounts/{account_id}/blasts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "April sale launch",
"from": "pn_01jrzhe8d9enptypyx360pcmxm",
"body": "Acme Corp: Our biggest sale of the year starts now. Shop: acme.com/spring",
"to": ["+18015551234", "+18025551234", "+18035551234"],
"send_at": "2026-06-01T09:00:00Z"
}'Audiences
Create an audience
An audience is a named contact list. Create it first, then populate it.
audience = surge.audiences.create(
account_id="{account_id}",
name="Premium subscribers",
)const audience = await surge.audiences.create("{account_id}", {
name: "Premium subscribers",
});audience = surge.audiences.create(
"{account_id}",
name: "Premium subscribers"
)# Surge.Audiences isn't typed in the Elixir SDK yet — use the raw client.
{:ok, audience} =
Surge.Client.request(client, :post, "/accounts/{account_id}/audiences",
json: %{name: "Premium subscribers"}
)curl -X POST https://api.surge.app/accounts/{account_id}/audiences \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium subscribers"
}'{
"id": "aud_01jrzhe8d9enptypyx360pcmxz",
"name": "Premium subscribers"
}Add contacts to the audience
surge.audiences.add_contact(
audience_id="aud_01jrzhe8d9enptypyx360pcmxz",
id="ctc_01jrzhe8d9enptypyx360pcmxa",
)await surge.audiences.addContact("aud_01jrzhe8d9enptypyx360pcmxz", {
id: "ctc_01jrzhe8d9enptypyx360pcmxa",
});surge.audiences.add_contact(
"aud_01jrzhe8d9enptypyx360pcmxz",
id: "ctc_01jrzhe8d9enptypyx360pcmxa"
){:ok, _} =
Surge.Client.request(client, :post,
"/audiences/aud_01jrzhe8d9enptypyx360pcmxz/contacts",
json: %{id: "ctc_01jrzhe8d9enptypyx360pcmxa"}
)curl -X POST https://api.surge.app/audiences/aud_01jrzhe8d9enptypyx360pcmxz/contacts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "ctc_01jrzhe8d9enptypyx360pcmxa"
}'Repeat for each contact, or build this into the workflow that creates or updates contacts.
Blast the audience
Pass the audience ID in the to array.
Surge expands the audience at send time and applies opt-out status automatically — opted-out contacts are skipped, so you never need to filter the list yourself.
blast = surge.blasts.create(
account_id="{account_id}",
from_="pn_01jrzhe8d9enptypyx360pcmxm",
body="Your monthly update from Acme Corp: acme.com/newsletter/april",
to=["aud_01jrzhe8d9enptypyx360pcmxz"],
)const blast = await surge.blasts.create("{account_id}", {
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Your monthly update from Acme Corp: acme.com/newsletter/april",
to: ["aud_01jrzhe8d9enptypyx360pcmxz"],
});blast = surge.blasts.create(
"{account_id}",
from_: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Your monthly update from Acme Corp: acme.com/newsletter/april",
to: ["aud_01jrzhe8d9enptypyx360pcmxz"]
){:ok, blast} =
Surge.Blasts.create(client, "{account_id}", %{
from: "pn_01jrzhe8d9enptypyx360pcmxm",
body: "Your monthly update from Acme Corp: acme.com/newsletter/april",
to: ["aud_01jrzhe8d9enptypyx360pcmxz"]
})curl -X POST https://api.surge.app/accounts/{account_id}/blasts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "pn_01jrzhe8d9enptypyx360pcmxm",
"body": "Your monthly update from Acme Corp: acme.com/newsletter/april",
"to": ["aud_01jrzhe8d9enptypyx360pcmxz"]
}'You can mix audience IDs, contact IDs, and raw phone numbers in the same to array.
Volume caps
Campaign volume limits apply to all sends, including blasts:
| Campaign volume | T-Mobile limit |
|---|---|
low | 2,000 SMS segments per day |
high | Up to 200,000 segments per day (subject to TCR trust score) |
Surge queues messages within your daily cap and delivers them in order. If you regularly approach the low cap, request a volume increase through support after your campaign is approved.
Tracking a blast
Blast-level delivery metrics (deliverability rate, opt-out rate) are available in the dashboard under the Blasts section. Per-message delivery status comes through the message.sent, message.delivered, and message.failed webhook events — see Track Message Delivery for the full mechanism.