> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracklysms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Limits & Best Practices

> Payload constraints, batch limits, and best practices for high-volume sending with the Trackly SMS v2 API

# API Limits & Best Practices

Trackly SMS is built for high-volume sending. Throughput scales with bulk requests, since one request can carry up to 1,000 messages — batch aggressively to reach high send rates.

## Rate Limits

The core sending endpoints (`/v2/send`, `/v2/send/bulk`, and related SMS/email endpoints) do not enforce a fixed per-IP request-rate cap. In practice, throughput is bound by request latency and payload size, so **bulk endpoints are how you scale**: a single `POST /v2/send/bulk` carries up to 1,000 messages.

Some specialized endpoints (for example OTP and phone validation) and account-level controls (such as daily send caps) enforce their own limits. When one is exceeded, the response is `429 Too Many Requests` with a `rate_limited` code:

```json 429 - Too Many Requests theme={null}
{
  "error": "Rate limit exceeded",
  "code": "rate_limited"
}
```

When you receive a `429`, pause and retry with exponential backoff after the window resets. Prefer bulk requests over many single requests for efficiency and reliability.

## Payload Limits

| Limit                      | Value           | Response                |
| -------------------------- | --------------- | ----------------------- |
| Maximum request payload    | **5 MB**        | `413 Payload Too Large` |
| Individual contact payload | **4,000 bytes** | `413 payload_too_large` |

## Batch Limits

Bulk endpoints accept up to **1,000 records** per request. Each record is processed individually — partial success is possible.

| Operation       | Single Endpoint     | Bulk Endpoint             | Max per Request |
| --------------- | ------------------- | ------------------------- | --------------- |
| Send messages   | `POST /v2/send`     | `POST /v2/send/bulk`      | 1,000           |
| Create contacts | `POST /v2/contacts` | `POST /v2/contacts/bulk`  | 1,000           |
| Record revenue  | `POST /v2/revenue`  | `POST /v2/revenue/bulk`   | 1,000           |
| Import history  | —                   | `POST /v2/history/import` | 1,000           |

To process more than 1,000 items, split your data into batches and send multiple requests.

## Best Practices

### Use Bulk Endpoints

Buffer messages locally and send in bulk batches. Sending 1,000 messages in a single bulk request is more efficient than 1,000 individual requests — fewer round trips, lower latency, and less overhead on both sides.

<CodeGroup>
  ```python Python theme={null}
  import requests

  # Send up to 1,000 messages in one request
  messages = [
      {"to": f"+1415555{i:04d}", "body": "Flash sale! 50% off today."}
      for i in range(1000)
  ]

  response = requests.post(
      "https://api.tracklysms.com/api/v2/send/bulk",
      headers={"X-Api-Key": "trk_your_api_key_here"},
      json={"list_number": "+18005551234", "messages": messages}
  )

  data = response.json()
  print(f"Queued: {data['queued_count']}, Errors: {data['error_count']}")
  ```

  ```javascript Node.js theme={null}
  const messages = Array.from({ length: 1000 }, (_, i) => ({
    to: `+1415555${String(i).padStart(4, "0")}`,
    body: "Flash sale! 50% off today.",
  }));

  const response = await fetch("https://api.tracklysms.com/api/v2/send/bulk", {
    method: "POST",
    headers: {
      "X-Api-Key": "trk_your_api_key_here",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ list_number: "+18005551234", messages }),
  });

  const data = await response.json();
  console.log(`Queued: ${data.queued_count}, Errors: ${data.error_count}`);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.tracklysms.com/api/v2/send/bulk \
    -H "X-Api-Key: trk_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "list_number": "+18005551234",
      "messages": [
        {"to": "+14155551234", "body": "Flash sale! 50% off today."},
        {"to": "+14155555678", "body": "Flash sale! 50% off today."}
      ]
    }'
  ```
</CodeGroup>

### Handle Transient Errors

Use exponential backoff with jitter when you receive `5xx` errors. These are transient and resolve on retry.

```python theme={null}
import time
import random
import requests

def send_with_retry(url, headers, payload, max_retries=5):
    for attempt in range(max_retries):
        response = requests.post(url, headers=headers, json=payload)

        if response.status_code < 500:
            return response

        wait = (2 ** attempt) + random.uniform(0, 1)
        time.sleep(wait)

    raise Exception("Max retries exceeded")
```

### Handle Partial Failures

Bulk responses include per-record errors. Parse the `errors` array to identify and retry failed items without re-sending the entire batch.

```json theme={null}
{
  "queued_count": 998,
  "error_count": 2,
  "errors": [
    {"index": 42, "to": "+1invalid", "code": "invalid_phone", "error": "Invalid recipient phone number format"},
    {"index": 501, "to": "+14155559999", "code": "doi_expired", "error": "Contact never confirmed their double opt-in (window expired)"}
  ]
}
```

### Parallelize Across Batches

You can send multiple bulk requests concurrently. Split large sends into 1,000-message batches and dispatch them in parallel; very high concurrency is bound by request latency, so batch aggressively with bulk endpoints.

## Next Steps

<CardGroup cols={2}>
  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/v2/error-codes">
    Handle errors in your integration
  </Card>

  <Card title="Send Bulk" icon="paper-plane" href="/api-reference/v2/messages/send-bulk">
    Send up to 1,000 messages per request
  </Card>
</CardGroup>
