Rate Limits & Reliability

To protect platform stability and ensure fair resource allocation across all organizations, Ziett enforces rate limits on API endpoints. This page explains the limits you will encounter, how to handle them correctly, and how to design your integration for long-term resilience.


Rate Limit Tiers

Different endpoints carry different rate limits depending on the nature and cost of the operation.

EndpointLimitWindow
GET /* (listing and retrieval)300 requestsper minute, per API Key
POST /messages (single message)120 requestsper minute, per API Key
POST /campaigns/batch (bulk dispatch)10 requestsper minute, per API Key
POST /contacts/import (CSV import)5 requestsper minute, per API Key

Limits are currently applied per API Key. We are migrating to organization-level limits — this page will be updated when that change rolls out.


Rate Limit Headers

Every API response includes headers that let you monitor your current consumption in real time.

HeaderDescription
X-RateLimit-LimitThe maximum number of requests allowed in the current window.
X-RateLimit-RemainingThe number of requests remaining before the limit resets.
X-RateLimit-ResetUnix timestamp of when the current window resets.
HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1752490260

These headers are currently under development and not yet active on all endpoints.


Handling 429 Errors

When you exceed a rate limit, the API returns 429 Too Many Requests:

{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "You have exceeded the request limit for this endpoint. Please slow down.",
  "status": 429,
  "trace_id": "e901a2bc77f344358a9e7011c789d012",
  "timestamp": "2025-07-14T11:00:00.000000+00:00",
  "service": "core"
}

The response also includes a Retry-After header indicating how many seconds to wait before retrying:

HTTP/1.1 429 Too Many Requests
Retry-After: 38

Exponential Backoff

The recommended strategy for handling 429 and transient 5xx errors is exponential backoff with jitter. This prevents multiple clients from retrying simultaneously after a limit window resets, which would cause another spike.

import time
import random
import httpx

def send_with_backoff(payload: dict, max_retries: int = 5):
    headers = {"X-API-KEY": "zk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
    delay = 1.0  # initial delay in seconds

    for attempt in range(max_retries):
        response = httpx.post(
            "https://api.ziett.co/c/v1/messages",
            json=payload,
            headers=headers,
        )

        if response.status_code not in (429, 500, 502, 503, 504):
            return response

        retry_after = response.headers.get("Retry-After")
        if retry_after:
            wait = float(retry_after)
        else:
            # Exponential backoff: 1s, 2s, 4s, 8s, 16s + random jitter
            wait = delay * (2 ** attempt) + random.uniform(0, 1)

        print(f"Attempt {attempt + 1} failed ({response.status_code}). Retrying in {wait:.1f}s...")
        time.sleep(wait)

    raise Exception("Max retries exceeded.")

Webhooks vs. Polling

Ziett is an asynchronous platform by design. Message delivery happens across carrier networks and Meta's infrastructure — it is not instantaneous. There are two approaches to track final delivery statuses:

Repeatedly calling GET /c/v1/campaigns/{id} until the status changes to COMPLETED works, but it is wasteful. It consumes your rate limit budget, adds latency between status changes and your awareness of them, and does not scale for high-volume operations.

Webhooks let Ziett push delivery events to your server in real time, the moment a status changes. You register an endpoint in the Dashboard and Ziett calls it automatically.

Webhook support is currently under development. This section will be expanded with full event schemas, signature verification, and configuration instructions once available.


Designing for Reliability

Beyond rate limits, these practices will make your Ziett integration significantly more robust in production.

Respond to webhooks quickly. Your endpoint should return a 2xx status code within 5 seconds. If your processing logic takes longer, acknowledge the webhook immediately and handle the event asynchronously in a background queue.

Handle duplicate events. Webhook events may occasionally be delivered more than once due to network retries. Use message_id or campaign_id as an idempotency key in your database to avoid processing the same event twice.

Store trace_id values. Log the trace_id from every API response and error. It is the most effective tool for debugging issues and the first thing our support team will ask for.

Monitor your credit balance. Configure low-balance alerts in the Dashboard to ensure your services are never interrupted by an exhausted balance at a critical moment.


With the fundamentals covered, you are ready to explore the full API surface. Continue to the API Reference to understand request conventions, response formats, and error contracts before calling your first endpoint.