Documentation

Authentication

Every request carries the API key in the Authorization header as a bearer token. Keys start with zp_ and are 256 bits of entropy; we store only their SHA-256 hash, so a leaked database does not leak keys.

Authorization: Bearer zp_...
  • A missing, unknown or revoked key answers 401 with the same body: the API does not confirm which keys exist.
  • An account with no active subscription answers 402 with code subscription_required. There is no free trial: an account subscribes at least one number to open the console. Numbers stay connected; subscribing restores access on the next call.
  • Revoking a key in the console takes effect on the next request. Keys have no scopes: one key opens the whole account, so keep one per environment and rotate by creating a new one first.

Errors

Every non-2xx response is an object with an error field carrying a stable code, an English message and, when useful, details. The code is what your code should branch on.

{
  "error": {
    "code": "validation_failed",
    "message": "The request body does not match the schema.",
    "details": { "path": "message.text", "expected": "string", "got": "undefined" },
    "docs": "https://zaiped.com/developers/docs"
  }
}
  • 400: invalid JSON, a body that does not match the route schema (validation_failed, with the path), or a rule the API enforces before Meta (for example recipient_invalid, meta_window_closed).
  • 401 and 402: authentication and entitlement, as above.
  • 404: the route does not exist, or the id belongs to another account (we never distinguish the two).
  • 429: a Zaiped budget or a Meta rate limit. Read Retry-After and wait.
  • 502 and 500: Meta refused in a way we could not translate, or we failed. Retry with backoff.

Rate limits

Per account: 600 requests per minute, plus a sending budget of 1,000 messages per hour and 5,000 per day. Meta applies its own limits per number on top; those come back as 429 with a meta_ code.