API key security
- Store API keys in environment variables or secret management systems.
- Never commit API keys to version control or expose them in client-side code.
- Rotate API keys regularly and immediately revoke compromised keys.
- Use separate API keys for different applications and environments.
Caching recommendations
- Cache hierarchy data for 5–10 minutes (changes infrequently).
- Cache historical project/message stats for completed date ranges indefinitely.
- Use shorter TTLs (1–2 minutes) for current-day statistics.
- Implement cache invalidation when you detect data changes.
Rate limiting strategies
- Monitor
X-RateLimit-Remainingon every response. - Implement exponential backoff for
429responses. - Use caching to reduce the number of API calls needed.
- Batch operations when possible (e.g., query larger date ranges).
Carrier throughput limits
Carriers limit how fast a brand can send. Two matter for API projects:- T-Mobile daily cap. T-Mobile caps the messages a brand can send per day. This applies to brands that have a T-Mobile daily limit on file (political brands verified through Campaign Verify do not have one today), and the cap resets at midnight Pacific. At the cap, recipients on known other carriers keep sending; T-Mobile recipients and recipients with an unknown carrier wait. When only those remain, and everything already queued has been sent, the project auto-pauses with
pause_reasonbrand_daily_cap. Run list analysis first (POST /contact-lists/{id}/analyze) and only T-Mobile recipients wait, because no carrier is unknown. Do not resume at midnight: that is inside quiet hours for much of the country. CallPOST /projects/{id}/schedulefor the next day during sending hours (the project’s sending window: 8 AM to 10 PM in the time zone of most recipients, or 8 AM Eastern to 10 PM Pacific when no zone holds a majority) with a morningscheduled_at, or resume now withdaily_cap_bypass: true(T-Mobile messages over the cap may fail, and failed messages are still billed).POST /projects/{id}/scheduleaccepts paused projects, so it is the resume call. - AT&T per-minute rate. AT&T limits message parts per minute per campaign, with separate SMS and MMS rates. AT&T counts each part of a text: a text that fits in one part counts once, and a longer text that splits into 2 or 3 parts counts 2 or 3 times. A picture message (MMS) counts once against the MMS rate. Every 10DLC campaign with an AT&T rate on file has its AT&T traffic delivered at that rate, whatever the brand type. The platform sends AT&T recipients right away and the carrier queue delivers them at that rate, usually within about an hour; only in the last 90 minutes of the recipients’ sending window does the platform hold back AT&T recipients that could not be delivered before the window closes. A campaign with no rate on file has no AT&T rate applied. Recipients on other carriers are never slowed by it, and it never pauses a project.
analysis.status complete or the contact_list.analyzed webhook, then create or schedule the project (until then the API returns 409 LIST_ANALYSIS_IN_PROGRESS). A list that was never analyzed sends to every number on it; an analyzed list sends to wireless numbers only.
The two lanes are independent: a campaign can have one without the other. A political (Campaign Verify) brand has an AT&T rate but no T-Mobile daily limit today, and a brand can also lack a T-Mobile limit because it has not synced yet. For how this looks in the dashboard and how to finish a large send sooner, see Sending speed and carrier limits.
Check before you schedule. GET /campaigns/{id}/throughput returns the brand’s daily cap, how much is used today, and the AT&T rates. t_mobile is null until a T-Mobile daily cap has synced for the campaign, and att is null only when the campaign has no AT&T rate on file (inside att, sms_tpm and mms_tpm can each be null, one per message type). t_mobile is also null when the brand has no T-Mobile daily limit on file. carrier_metered is true if either lane applies; a campaign with neither returns false with both null. A political brand with an AT&T rate returns carrier_metered: true, t_mobile: null and a populated att. GET /projects/{id}/throughput estimates how a specific project will fare: t_mobile.will_pause, t_mobile.estimated_send_days, and att.estimated_minutes. A null means the number is not known right now (for example, today’s usage is temporarily unavailable, in which case will_pause is false); it is never zero. A project with daily_cap_bypass on reports will_pause: false and estimated_send_days: 1. When carrier_coverage is low, the estimates use the platform-wide carrier split rather than your recipients’ actual carriers. Project estimates are cached for up to 60 seconds, so numbers can lag by that much. If the estimate query times out, the endpoint returns 503 with code CARRIER_ESTIMATE_TIMEOUT; retry later.
Poll for paused, not just completed. GET /projects/{id} returns status, pause_reason, and auto_paused. A project with status: paused and pause_reason: brand_daily_cap is waiting for the next Pacific day, not stuck; call POST /projects/{id}/schedule with a morning scheduled_at inside the next day’s sending hours to resume it, or send daily_cap_bypass: true to resume now. A poller that waits only for completed will time out on a large send.
Known pause_reason values:
Other values may appear. Every automatic pause needs a manual restart via
POST /projects/{id}/schedule.
Running through the cap. Send daily_cap_bypass: true on POST /projects/{id}/schedule to run the whole project in one pass. Messages to T-Mobile recipients over the cap may fail, and failed attempts are still billed. The setting holds until the next schedule or resume call rewrites it, so send it on every schedule or resume call you want it to apply to. GET /projects/{id} returns the current value as daily_cap_bypass.
Failed messages over the cap report error code 016 (see error codes).
Date range optimization
- Use the maximum 31-day range when fetching historical data.
- Request only the specific date ranges you need.
- Use hierarchy filters (
organization_id,brand_id,campaign_id) to reduce data volume. - Cache aggregated results for reporting dashboards.
Webhook reliability
- Return
200within 10 seconds (process asynchronously if needed). - Always validate the HMAC signature before processing.
- Use
event_idto deduplicate (rare but possible). - Log failures and monitor your endpoint health.
- Use HTTPS endpoints (required in production).
- Rotate secrets periodically (every 90 days recommended).
