readyDocs Open the appOpen the app

Bulk send and message lookup

#Send to many

Sends one text to up to 100 recipients, with a result for each. Each recipient is billed as one send.

Bulk is synchronous: 5 recipients at a time, returning when all are done. Set a client timeout longer than 120 seconds. After a timeout, retry with the same Idempotency-Key.

Parameters

Idempotency-Keystring, headerRecommended
One key for the whole batch.
tostring[]Required
Up to 100 recipients, E.164 recommended.
MoreEntries that aren’t non-empty strings are skipped and not counted in total. Duplicate numbers aren’t merged: the repeat is refused as duplicate_send. A group text is the exception: its duplicates are merged before it is counted.
bodystringRequired
The text for every recipient. message is an alias.
More parameters
fromE.164 string
One of your numbers.
media_urlstring
Image URL, as in Send a message.
confirm_empty_mergeboolean
Send true to send even when a merge field is blank for some recipients. Without it, those recipients get empty_merge_field and are not sent.
groupboolean
Send one group text: everyone in to gets it in one shared thread.
MoreNeeds 2 to 10 different numbers and a from number on an approved texting campaign. No merge fields, since everyone sees the same text. Each person is billed as one MMS. Someone who opted out or is in quiet hours is left out of the group and isn’t charged. Replies come into each person’s own conversation.
Returns

Returns 200 once the batch is processed, even if every recipient failed: check data.failed and each results[].sent. With a test key, nothing is sent and each result has a test-msg-… message_id and its segments.

data.totalinteger
Recipients processed.
data.sentinteger
How many were accepted.
data.failedinteger
Every recipient not sent and not held. That includes a recipient whose result has error_code send_outcome_unknown: that text may have gone out, so check List messages before you send it again.
data.heldinteger
How many are held for quiet hours and will send on their own.
data.groupboolean
true on a group send. Missing otherwise.
data.results[]object[]
One per recipient: to, status (the HTTP status a single send would have returned), sent, and either message_id, segments and sent_body, or error and error_code. A held recipient has queue_id, queued_for, send_at, recipient_tz and warning instead.
MoreA refused recipient’s error_code is what a single send returns (in error_code or code), else "send_failed", or "send_error" when the send failed on our side before anything went out (safe to retry). no_route and send_outcome_unknown mean it may have gone out: check List messages before you send it again. send_dedupe_unavailable means nothing was sent and it is safe to retry. Read error before retrying.
data.results[].heldboolean
true when the text is held for quiet hours. queue_id is the held text, recipient_tz the time zone used, and warning says why it was held: it isn’t sent yet, goes out at send_at, and must not be sent again. A held recipient has "sent": false and counts in held, not failed.
Errors
HTTPCodeWhen
400invalid_requestto isn’t a list with at least one number, or there’s no body.
400too_many_recipientsMore than 100 recipients.
400invalid_fromNot one of your numbers.
400invalid_fieldfrom is a list or an object, not one number.
400invalid_media_urlmedia_url isn’t one web address that starts with http:// or https://. Nothing is sent.
400invalid_group_sizegroup with fewer than 2 or more than 10 different numbers.
400group_from_requiredgroup without from.
400group_from_unregisteredfrom isn’t on an approved texting campaign.
400group_merge_fieldsA group text with merge fields.
402insufficient_creditsYour balance can’t cover the whole batch, so nothing is sent. Has required_cents, balance_cents and recipients. Top up in Billing by at least the difference, then try again.
402client_card_requiredA sub-account that pays with its own card and has not added one yet. Add one in Billing.
409idempotency_in_progressSame key while the batch still runs.
429throughput_exceededThe batch is over your carrier throughput, so nothing is sent. Has retry_after_ms and cap_per_sec.
429rate_limitedOver the per-account limits.

#Get a message

One message your account sent or received, including texts your team members sent. Returns a Message object in data.

Parameters

idstring, pathRequired
The message_id a send returned.
Errors
HTTPCodeWhen
404message_not_foundNo such message on your account (or it’s another account’s).
429rate_limitedOver the per-account limit.
503message_reference_ambiguousThe id matches more than one message. Contact support before you send it again.
Older paths

These paths predate /v1. They keep working and we aren’t turning them off; new code should use the /v1 endpoints. With an API key, the two GETs answer with a Deprecation: true header and a Link header (rel="successor-version") to the /v1 docs; the body doesn’t change.

  • POST /sms/send: the same send as POST /v1/messages. It stays for existing callers; new code should use POST /v1/messages. The text goes in message (body is an alias; message wins if you send both), and it also takes from_phone_number_id, which overrides from_strategy. A missing to answers 400 with error_code: "missing_field".
  • GET /sms/logs: the dashboard’s raw message log (50 rows a page by default; its search covers only your most recent 10,000 messages). Use GET /v1/messages. hasMore becomes has_more, offset becomes before_id (pass back next_before_id), a row on /v1 has the same value in both id and message_id, and status uses the public words (queued, not sent or pending).
  • GET /phone-numbers: use GET /v1/numbers.