Rate Limits
Understanding and handling API rate limits.
The Linq API enforces rate limits to protect Apple’s ecosystem and prevent automated feedback loops between bots.
Recommended daily volume
Section titled “Recommended daily volume”We recommend limiting message volume to a combined total of up to 7,000 inbound and outbound messages per day per line. Volume north of the recommended range has the potential to degrade performance.
There are no hard caps on daily volume (excluding sandbox accounts). These recommendations are meant to optimize the performance of your lines. We recommend you control volume via onboarding flows you build on your end.
Per-phone-pair rate limit
Section titled “Per-phone-pair rate limit”The API enforces a rate limit of 30 messages per 60-second window for each unique sender–recipient pair. This is designed to prevent automated feedback loops where two numbers (i.e. bots) rapidly exchange messages.
When exceeded, the API returns HTTP 429 with a Retry-After header (seconds until reset) and error code 1007. The same value is also available as retry_after in the error body. Hitting this limit almost always means a bug — check for retry loops, duplicate sends, or webhook handlers that inadvertently reply to themselves.
Sandbox limits
Section titled “Sandbox limits”Sandbox accounts are the exception to the “no hard caps” rule:
- 100 messages per day — Resets at midnight UTC
Capability check rate limit
Section titled “Capability check rate limit”Capability check endpoints (/v3/capability/check_imessage and /v3/capability/check_rcs) are also rate-limited to prevent abuse (e.g. using them to enumerate which numbers are on iMessage). Excessive calls return the same HTTP 429 with Retry-After and error code 1007.
Cache results briefly (minutes, not days) rather than re-checking the same address on every send — capability is stable over short windows and the cache reduces your risk of hitting this limit.
Rate limit response
Section titled “Rate limit response”When you hit a rate limit, the API returns HTTP 429 Too Many Requests with a Retry-After header and this body:
{ "success": false, "error": { "status": 429, "code": 1007, "message": "Rate limited. Try again in 45 seconds.", "retry_after": 45 }, "trace_id": "trace_abc123def456"}The retry_after field mirrors the Retry-After header and is only present on 429 responses. See Error Codes for the full envelope shape used by all error responses.
Handling rate limits
Section titled “Handling rate limits”Prefer the Retry-After header when present; fall back to exponential backoff otherwise:
async function sendWithRetry( client: LinqAPIV3, chatId: string, message: MessageParams, maxRetries = 3,) { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await client.chats.messages.send(chatId, message); } catch (error) { if (error.status === 429 && attempt < maxRetries) { const retryAfter = Number(error.headers?.['retry-after']); const delayMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : Math.pow(2, attempt) * 1000; // 1s, 2s, 4s fallback await new Promise(resolve => setTimeout(resolve, delayMs)); continue; } throw error; } }}import time
def send_with_retry(client, chat_id, message, max_retries=3): for attempt in range(max_retries + 1): try: return client.chats.messages.send(chat_id, **message) except Exception as e: if getattr(e, "status_code", None) == 429 and attempt < max_retries: retry_after = getattr(e, "response", None) and e.response.headers.get("retry-after") delay = int(retry_after) if retry_after else (2 ** attempt) # 1s, 2s, 4s fallback time.sleep(delay) continue raiseTip: The official SDKs handle retries automatically with exponential backoff. You only need custom retry logic if you’re using the API directly.
Best practices
Section titled “Best practices”- Queue messages — Use a message queue to control send rate rather than sending as fast as possible
- Monitor usage — Track your daily message count to avoid hitting limits unexpectedly
- Batch wisely — Space out bulk sends rather than sending all at once
- Use idempotency keys — Include an
idempotency_keyfield in the message body on retries to prevent duplicates (see Sending Messages)
Other error codes
Section titled “Other error codes”Rate limits aren’t the only reason requests can fail. See Error Codes for the complete reference, including infrastructure errors (3xxx) that may also require retry logic. See the Send Message and Create Chat API references for endpoint details.