Text reports
The same figures as the text reports in the app. Like the overview, most of them count the account and the sub-accounts under it (Top campaigns and Best hours to send count only the account the key belongs to), and they need reports access. A key user who only sees assigned contacts gets figures over those contacts, except for the two reports that cover the whole account, which answer 403 report_scope_forbidden. Delivery receipts and reply tags can still change a recent period, so read it again if you need it final.
#Texts by hour
Texts sent and delivered in each clock hour of one short window, up to 48 hours. Use it for today or yesterday, where a daily row would be a single point.
Parameters
fromstring · query- Start of the window, an ISO timestamp. Required.
tostring · query- End of the window, exclusive. Default: now. At most 48 hours after
from.
Returns
data[]object[]- One row per hour that had texts, oldest first.
data[].hourstring- The hour, as a UTC timestamp such as
2026-10-31T14:00:00Z. data[].countinteger- Texts sent in that hour.
data[].deliveredinteger- Of those, delivered so far.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_request | from is missing, is not a valid time, or the window is longer than 48 hours. |
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |
#Daily text volume
Texts sent and delivered per day, oldest first, in the key user’s time zone. This counts every text created that day, MMS and texts that failed included, across the account and its sub-accounts, the same as the Reports page. The daily report counts sms_sent differently: only texts handed to the carrier, without MMS, for one account in the account time zone, so the two can differ. Use the daily report for costs, replies and opt-outs.
Parameters
daysinteger · queryDefault15- The last N days.
from, tostring · query- A custom window instead of
days: dates asYYYY-MM-DD, in the key user’s time zone, or full timestamps.
Returns
data[]object[]- One row per day.
data[].datestring- The day,
YYYY-MM-DD. data[].countinteger- Texts sent that day.
data[].deliveredinteger- Of those, delivered so far.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |
#Top campaigns
The campaigns and drip campaigns that sent the most in the window, largest first. A campaign with no sends in the window is left out. Counts only the account the key belongs to, not its sub-accounts.
Parameters
daysinteger · queryDefault30- The last N days.
from, tostring · query- A custom window instead of
days: dates asYYYY-MM-DD, in the key user’s time zone, or full timestamps.
Returns
data[]object[]- Up to the 5 busiest.
data[].idinteger- The campaign or drip campaign id.
data[].namestring- Its name.
data[].kindstringblastfor a one time campaign,sequencefor a drip campaign.data[].created_atstring- When it was created.
data[].total_sentinteger- Texts sent in the window.
data[].deliveredinteger- Of those, delivered.
data[].failedinteger- Of those, failed.
data[].repliesinteger- Contacts who replied.
data[].delivery_ratenumber or null- Delivered over delivered plus failed, as a percent.
nullwhen nothing has resolved yet.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |
#Delivery breakdown
Did the texts arrive: totals, a trend per day, the reasons the failures gave, and the result for each sending number.
Parameters
daysinteger · queryDefault30- The last N days.
from, tostring · query- A custom window instead of
days: dates asYYYY-MM-DD, in the key user’s time zone, or full timestamps. partstring · queryDefaultall- Read less to answer faster:
summary(totals and daily),numbers,reasons,detail(numbers and reasons) orall. A part you did not ask for comes backnull, exceptdaily, which comes back as an empty list.
Returns
data.daysinteger- The window length used.
data.totalsobjectsent,delivered,failed,pending,delivery_rateandfailure_rate(percents).data.daily[]object[]- One row per day with the same counts.
data.by_reason[]object[]- The most common failure reasons, each with
reasonandcount. data.reason_totalinteger- Failures that have a reason.
data.by_number[]object[]- Per sending number:
phone_number,sent,delivered,failed,delivery_rate. data.blockedobject- Texts Ready held back before sending, as
complianceandundeliverable, each withattemptsandrecipients. Only there when you ask forsummaryorall;nullwhen it can’t be read or the key user sees only assigned contacts.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 503 | FAILURE_REASONS_UNAVAILABLE | The failure reasons could not be read. The answer carries retryable: true. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |
#Reply heatmap
How many replies arrived in each hour of each weekday. It counts replies, not a reply rate, because a reply can come long after the text that earned it.
Parameters
daysinteger · queryDefault30- The last N days.
from, tostring · query- A custom window instead of
days: dates asYYYY-MM-DD, in the key user’s time zone, or full timestamps. tzstring · queryDefault: the key user’s time zone- An IANA name such as
America/New_York. An unknown name is ignored.
Returns
data[]object[]- One row for each weekday and hour that had replies.
data[].dowinteger- Day of the week,
0for Sunday to6for Saturday. data[].hourinteger- Hour of the day,
0to23. data[].ninteger- Replies in that slot.
timezonestring- The time zone the slots are counted in.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |
#Best hours to send
Texts sent and replies for each hour of the day, to show when people answer. The window is the last N times 24 hours, not whole days. Counts only the account the key belongs to, not its sub-accounts.
Parameters
daysinteger · queryDefault30Max365- The last N days.
tzstring · queryDefault: the key user’s time zone- An IANA name such as
America/New_York. An unknown name is ignored.
Returns
data[]object[]- Always 24 rows, hour
0to23. data[].hourinteger- Hour of the day.
data[].sentinteger- Texts sent in that hour.
data[].repliesinteger- Replies received in that hour.
data[].cohort_sentinteger- Texts sent to a known contact, the base for the next field.
data[].sent_with_replyinteger- Of those, texts whose contact replied within 7 days.
bestobject or null- The best sending window the report found.
nullwhen there is not enough data. timezonestring- The time zone the hours are counted in.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 403 | report_scope_forbidden | The key’s user only sees contacts assigned to them. This report covers the whole account. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |
#Deliverability
Why texts fail. The last 24 hours, 7 days and 30 days, failures by group, the most common errors with advice, the result per sending number and per carrier, and the latest failures. The figures are refreshed about every 5 minutes.
Parameters
numbers_onlystring · queryDefaultfalse- Send
trueto get onlyper_phone, faster.
Returns
summaryobject- Keyed
24h,7dand30d, each withtotal,delivered,failed,pending,delivery_rateandfailure_rate. failures_by_group[]object[]status_groupandcount.status_groupis the carrier’s group for the failure:UNDELIVERABLE(the phone could not take it),EXPIRED(the carrier gave up before it was delivered),REJECTED(the carrier refused it) orUNKNOWN(no receipt said why).top_errors[]object[]error_name,error_code,status_group,count,guidanceandexplanation.per_phone[]object[]from_number,total,delivered,failed,delivery_rate.by_carrier[]object[]carrier,total,delivered,failed,pending,delivery_rate,failure_rate. Carriers with fewer than 10 texts are left out.recent_failures[]object[]- The latest failed texts, each with the reason in plain words.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A parameter sent twice, or too long; field names it. |
| 403 | permission_denied | The key’s user has no reports access. |
| 403 | report_scope_forbidden | The key’s user only sees contacts assigned to them. This report covers the whole account. |
| 403 | report_scope_forbidden | The key’s user is a team member limited to some of the account’s numbers. This report covers every number on the account. |
| 503 | report_unavailable | The report couldn’t be read. Nothing changed; retry. Do not record zeros. |