Skip to main content
Every error response from the Peeng API — whether it is a validation failure, an authentication problem, or an internal server error — uses the same JSON envelope shape. Consistent error structure means you can write a single error-handling path in your code and branch on statusCode or error for any special cases.
The three fields are always present: statusCode mirrors the HTTP status code, error is a machine-readable error class, and message is a human-readable explanation of what went wrong.
On POST /v1/logs validation failures, message can be an array of strings rather than a single string — one message per failed field, across all entries in the batch. Since a single request can carry up to 500 log entries, you may receive up to 500 validation messages in one response.

Status codes

Retryable vs. non-retryable

Both the JavaScript and Python SDKs apply the following rule automatically, and you should mirror it when calling the HTTP API directly. Retryable errors — the SDKs retry with exponential backoff plus jitter, up to maxRetries, and reuse the same Idempotency-Key so a successful retry can never double-insert logs:
  • 429 Too Many Requests
  • Any 5xx server error
  • Network-level failures: DNS resolution failures, connection refused, and request timeouts
Non-retryable errors — the SDKs fail fast and surface the failure immediately via the onError callback, without consuming any retry budget:
  • 400 Bad Request — retrying a malformed batch will never succeed; fix the payload instead
  • 401 Unauthorized — retrying with a bad or missing API key will never succeed; fix the key instead
  • Other 4xx responses not listed above
If you are calling the HTTP API directly, apply this same distinction in your own retry logic. Retrying a non-retryable error wastes time and burns through your rate-limit quota without any chance of success.