Quota and usage
Turbo Notify measures two independent quotas per linked number (main or an
extra alias): messages sent and distinct contacts reached. Both
quotas are per number: each number has its own allowance, and an
organization with N numbers has N times the plan’s allowance.
Only what goes out through Turbo Notify (origin: "tn") uses quota;
messages sent directly from the phone (origin: "native") do not count.
Billing cycle: when the quota resets
Both quotas are measured over the organization’s current billing cycle
(from cycle_start to cycle_end, the subscription’s current period).
resets_at always matches cycle_end: the quota resets when the
cycle turns over.
usedandquotaare always measured against the cycle that is open.- When the cycle closes (subscription renewal), the counters go back to zero and a new
period
[cycle_start, cycle_end)starts. cycle_startandcycle_endare the same for every number in the organization (the cycle belongs to the subscription, not to the number); onlyusedandquotavary per number.
Plan table (per number, per cycle)
Mapping to the commercial plan names: Free = FREE · Lone Wolf = SOLO · Business = BUSINESS · Custom = CUSTOM.
| Plan | Messages/cycle | Distinct contacts/cycle | Extra numbers (max.) |
|---|---|---|---|
FREE | 300 | 2 | 0 |
SOLO | 1,800 | 100 | 0 |
BUSINESS | 50,000 | 5,000 | 1,000 |
CUSTOM | 500,000 | 50,000 | 1,000 |
Prices for each plan are on the Billing page of the dashboard and at turbonotify.com. Your organization can ask support for a custom limit, which replaces the plan’s per-number ceiling.
The tier field in the responses below is always one of the 4 values above, in
uppercase ("FREE", "SOLO", "BUSINESS", "CUSTOM").
Groups and received messages
- A send to a group uses 1 unit of message quota and zero contact quota: a group is not a contact.
- Received messages do not use either quota.
- Only sends with
origin: "tn"use message quota; only contacts reached through Turbo Notify use contact quota.
What counts toward the quota
A send through Turbo Notify only uses the message unit and the contact unit if it reaches WhatsApp (or while it is still on its way there).
- The API refused the send (for example
429for spacing between sends, a repeated message or the recipients-per-day limit): nothing is used. - The send was accepted (
202) but failed before reaching WhatsApp (for examplemessage.send.failedwithnumber_not_connectedor a failure downloading the media): both units are given back shortly after the failure, within a few minutes.usedgoes back to reflecting only what got through. - The send was not confirmed within the confirmation time limit
(
message.send.failedwithmessage_timeout): the unit is not given back, because the message may still have left your number. If it reaches WhatsApp later, the send moves tosentand keeps counting; if you send it again, the new send counts on its own. - The message reached WhatsApp and delivery failed afterwards: it counts, like any message that left your number.
Test messages sent from the Turbo Notify dashboard do not use message quota or contact quota.
The sending pace limits (spacing, messages per minute, recipients per day) count attempts, not deliveries: a refused attempt still counts toward those limits.
Sending to groups requires the Business plan or higher
Sending a message or a typing indicator to a group_id requires the
groups:messages feature, available from the Business plan. On the Free and Lone Wolf plans you
can still receive group messages, list groups and get group details.
Only sending to a group is blocked:
{
"error": "feature_not_available"
} See Groups: Overview and Group limitations.
Endpoint: account-wide usage
GET /v1/usageRequired scope: none: any valid API key.
Caching: the response is returned with Cache-Control: no-store, no-cache, must-revalidate and Pragma: no-cache, meaning it cannot be cached. Do not
cache the body; call again whenever you need up-to-date
data.
GET /v1/usage is the account total: messages and contacts at the root level
add up the usage of all numbers in the organization, and quota is the sum of
each number’s allowance (an organization with 2 numbers on a plan of
100 per cycle reports quota: 200). resets_at is the cycle_end of the organization’s
current cycle (the same for every number). The response also includes numbers[]:
one entry per active number (main and extras), with the
used/quota/remaining breakdown of each quota, without resets_at or tier per entry
(both already apply to all at the root level).
{
"messages": {
"used": 1234,
"quota": 1800,
"remaining": 566,
"resets_at": "2026-07-01T00:00:00Z",
"tier": "SOLO"
},
"contacts": {
"used": 87,
"quota": 100,
"remaining": 13,
"resets_at": "2026-07-01T00:00:00Z",
"tier": "SOLO"
},
"numbers": [
{
"alias": "main",
"messages": {
"used": 1234,
"quota": 1800,
"remaining": 566
},
"contacts": {
"used": 87,
"quota": 100,
"remaining": 13
}
}
]
} GET /v1/usage/messages
The same account total, for the message quota only, also with its
own numbers[] (entries with messages only), for when you only need that
quota and not the contact total:
{
"messages": {
"used": 1234,
"quota": 1800,
"remaining": 566,
"resets_at": "2026-07-01T00:00:00Z",
"tier": "SOLO"
},
"numbers": [
{
"alias": "main",
"messages": {
"used": 1234,
"quota": 1800,
"remaining": 566
}
}
]
} Per-number endpoints
GET /v1/numbers/{ref}/messages/quota: the number’s message quota (flat route for the main number:GET /v1/messages/quota, equivalent toGET /v1/numbers/main/messages/quota).GET /v1/numbers/{ref}/contacts/quota: the number’s contact quota (flat route for the main number:GET /v1/contacts/quota).
Both return the same format: the state of that number in the current cycle.
{
"used": 1234,
"quota": 1800,
"remaining": 566,
"cycle_start": "2026-06-01T00:00:00Z",
"cycle_end": "2026-07-01T00:00:00Z",
"resets_at": "2026-07-01T00:00:00Z",
"tier": "SOLO"
} resets_at always repeats cycle_end, for readers who only look at one
field. Unlike GET /v1/usage, these two endpoints return cycle_start and
cycle_end explicitly, because they describe one number.
Through the MCP server, an agent gets the same data with
get_usage, get_message_quota and get_contact_quota. These three tools
need no scope: any valid key can use them.
Content retention period
Besides the quota, each plan sets how many hours a message’s content
(text, caption, media reference, transcription, preview) is kept.
The content period is 1 hour by default, up to 7 hours on Lone Wolf and up to 72 hours on Business and Custom.
Once that period has passed, Turbo Notify permanently deletes the content.
Only content expires: the message stays available through the API for 12
months.
GET /v1/numbers/{ref}/messageskeeps listing the message, withcontent_expired: trueand an emptypreview.- The lookup
.../messages/{id}, the sub-resources.../status,.../receiptsand.../reactions, and the actions.../reply,.../edit,.../deleteand.../reactkeep working, on both the nested and the flat routes. - The message record (metadata: status, dates, participants) is kept for 12 months, the same for every plan, regardless of the content period chosen.
The periods per plan, how to choose a longer one and the full lifecycle of content and metadata are in Message retention.
Errors
402: inactive subscription
Returned when you try to send while the organization’s subscription does not allow
using features (ACTIVE, and TRIALING within the trial period, are
the only states that pass without restriction):
{
"error": "subscription_inactive",
"message": "Subscription is not active",
"details": {
"status": "trial_expired"
}
} details.status is one of these values: trial_expired (trial period ended without
a paid plan), past_due (payment overdue: sending stays blocked until the
payment goes through) or missing (organization without an active subscription).
Active subscriptions and subscriptions in their trial period do not get this error. A scheduled move to a smaller plan or a scheduled cancellation does not block sending: the subscription keeps working until the end of the period already paid for.
402: feature not available on the plan
{
"error": "feature_not_available"
} Today the features restricted by plan are:
- Reactions (
reactions): Lone Wolf and up. - Typing indicator (
typing_indicator): Lone Wolf and up. It is the base restriction and applies to every call. - Sending a message or typing indicator to a group (
groups:messages): Business and up, see above. - Detailed receipt history and manually marking a chat as read (
read_receipts): Business and up, see Receipts and Mark as read. - Creating extra numbers (
extra_numbers): not available on Free or Lone Wolf, see Extra numbers. - Webhook redelivery (
webhooks:retries): Business and up. On Lone Wolf each event gets a single delivery attempt and, if it fails, there is no other; see Events via webhook. On Free there are no webhooks: useGET /v1/events, available on every plan.
402: extra number limit reached
Returned by POST /v1/numbers when the organization already has
max_extra_numbers active extra numbers for its plan (Business 1,000, Custom
1,000; Free and Lone Wolf have no access to extra numbers):
{
"error": "extra_numbers_limit_reached",
"message": "Extra numbers limit reached for the current plan (1000/1000).",
"details": {
"limit": 1000,
"current": 1000
}
} details.limit is the plan’s max_extra_numbers ceiling; details.current is the
organization’s current number of active extra numbers.
402: contact quota exceeded
{
"error": "contact_quota_exceeded",
"message": "Distinct-contacts quota exceeded for tier BUSINESS (5000/5000 this cycle)",
"details": {
"quota": 5000,
"used": 5000,
"resets_at": "2026-07-01T00:00:00Z",
"tier": "BUSINESS"
}
} 429: message quota exceeded
- Retry-After: 1296000
{
"error": "messages_quota_exceeded"
} Unlike the contacts 402, this 429 does not carry details with
used/quota/resets_at in the body. Call
GET /v1/numbers/{ref}/messages/quota (or GET /v1/usage/messages) for the
full state and the actual resets_at. The Retry-After header gives how many
seconds are left until the number’s quota renews, that is, until resets_at (in the
example, 15 days). Retrying the send before then gets the same 429; to send
right away, use another number or change plans.
402 vs 429: when they appear
| 402 (contacts) | 429 (messages) | |
|---|---|---|
| Signal | Move to a bigger plan | Cycle quota used up |
Retry-After header | Absent | Present: seconds until resets_at |
Body with used/quota/resets_at | Yes (details) | No: call the quota endpoint |
| Resets at | resets_at (end of the current cycle) or when moving to a bigger plan | resets_at (end of the current cycle) |
Quota webhooks
contact.quota.warningat 80/90/95%.contact.quota.exceededwhen exceeded.message.quota.warningat 80/90/95%.message.quota.exceededwhen exceeded.
Both families carry cycle_start/cycle_end in data. See
Message quota events,
Contact events and Rate limits.