Error codes
API returns standard HTTP codes with JSON body. The code field is a stable machine-readable identifier in lowercase snake_case (e.g. subscriber_not_found); retryable indicates whether the request can be retried; docs_url links to the code description.
Error format:
400 — Invalid request or validation error (bad_request, validation_failed, invalid_url, invalid_external_id, mutually_exclusive_identifiers, etc.). Malformed or incomplete JSON in the request body also returns 400 with code validation_failed — always in the standard envelope, never raw text.
401 — Invalid or missing API key (unauthorized, invalid_api_key)
402 — Access blocked: credits exhausted and the grace period has expired (billing_blocked). Paid operations (sending messages, OTP, broadcasts, helpdesk replies, form submissions) are rejected until the plan is renewed or a credit pack is purchased.
403 — No access to resource (forbidden, project_inactive)
404 — Subscriber or resource not found (subscriber_not_found, not_found)
409 — Conflict (conflict, idempotency_conflict, already_subscribed)
429 — Rate limit exceeded (rate_limit_project, retryable: true, Retry-After header)
500 — Internal error or platform unavailable (internal_error, platform_busy, retryable: true)
Limits
Rate limits and field size restrictions.
Rate limit: 300 requests/min per project. Exceeding it returns 429 Too Many Requests.
Message text: up to 4,000 characters.
Buttons: up to 3 rows, up to 3 buttons per row.
Media: up to 20 MB (photo), 50 MB (video/document).
Broadcast: up to 100,000 subscribers per broadcast.
OTP: code TTL 5 min by default (1–30), 3 attempts by default (1–10), 1 active code per (subscriber, purpose) pair.
Tags: up to 20 tags per project, tag length up to 64 characters.
Permissions: up to 20 per project.
Templates: up to 100 per project.
Projects and team members: limits depend on the plan (Free — 1 project / 1 member; unlimited on higher plans). CSV export is available from the Basic plan.
Forms: up to 10 per project, up to 40 routes per form.
Webhook endpoints: up to 5 per project.
Drip campaigns: up to 50 steps per chain, up to 100 scheduled entries per project.
X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers are returned on every /v1/send (Stripe/GitHub/AWS style). On exceeded limit — HTTP 429 + Retry-After. SDKs and integrations should throttle proactively via X-RateLimit-Remaining without waiting for 429.
Error handling & retry
Recommendations for handling API errors and retry strategies.
retryable: true — the error is temporary (429, 500, platform_busy). Use exponential backoff; when the Retry-After header is present, wait the number of seconds it specifies.
retryable: false — retrying won't help (400, 401, 403, 404, 409). Fix the request before resending.
Retry-After — on 429 (and 425/503) this header tells how many seconds to wait. Zapnoty rate limit for /v1/send: 300 requests per minute per project.
Idempotency-Key: for safe retry of POST /v1/send pass a unique Idempotency-Key header (8–128 characters from [A-Za-z0-9_-]). The same key within 24h returns the cached successful response or recorded error — no side effects. Use it for retry-heavy workloads.
Webhook delivery: outgoing Zapnoty webhooks have their own 24-hour retry cycle on a Stripe-style schedule (T+30s, +2min, +10min, +1h, +6h, +24h). Each attempt refreshes the timestamp and signature. Delivery log is visible in the cabinet → project → settings → Webhook.