API: Errors & Rate Limits
Pingovo uses standard HTTP status codes, and every error body carries a machine-readable error code plus a human-readable message. Branch on the status and the code; show the message to people.
HTTP status codes
| Status | Meaning | Retry? |
|---|---|---|
| 200 | Success. | — |
| 400 | The request is malformed or a field is invalid. Fix it and resend. | No |
| 401 | API key missing, invalid, deactivated or expired. | No |
| 402 | Not enough credits for this call. | After topping up |
| 403 | API access is not enabled on your plan, the request came from an IP that is not on the key’s allowlist, your email is unverified, campaign access is not approved (for /send), or the key was revoked by Pingovo. | No |
| 404 | The send or template does not exist on your account. | No |
| 422 | The recipient was refused: invalid, suppressed or skipped by your rules. | No |
| 429 | Too many requests, your daily send limit is used up, or too many sends are queued. Wait for Retry-After. | Yes, after Retry-After |
| 500 | Something went wrong on our side. | Yes, with backoff |
| 502 | Verification or delivery could not be completed. | Yes, with backoff |
Rate limits
Your plan sets how many requests you can make per minute and per day. The allowance is shared by all of your API keys, so a second key does not buy more. A key can additionally be given its own per-minute cap to stop one integration starving another.
Every authenticated response tells you where you stand:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests per minute allowed for your account. |
X-RateLimit-Remaining | Requests left in the current minute. |
X-RateLimit-Reset | Seconds until the per-minute window resets. |
Retry-After | On a 429 only: seconds to wait before trying again. |
A rate-limited call never reaches the endpoint, so it is never charged:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
{
"success": false,
"error": "RATE_LIMITED",
"message": "Rate limit exceeded (60 requests per minute). Retry after 23s.",
"retry_after_seconds": 23,
"limit": 60,
"window": "minute"
}| error | Cause |
|---|---|
| RATE_LIMITED | Your plan’s requests-per-minute or per-day allowance (or the key’s own cap) was exceeded. window says which. |
| AUTH_THROTTLED | The IP address you are calling from presented too many unknown or missing API keys and is blocked for a while. Every call from it is refused, even with a valid key, until the block lifts. |
| DAILY_LIMIT_REACHED | Your own daily_send_limit on /send is used up. See resets_at. |
| SEND_QUEUE_BUSY | Too many /send calls are waiting behind delay_between_emails. |
Handling 429 properly
Wait the number of seconds in Retry-After, then retry. If the header is missing, back off exponentially. Do not retry in a tight loop — against AUTH_THROTTLED that only extends the block.
# -i prints the response headers: X-RateLimit-Limit, X-RateLimit-Remaining,
# X-RateLimit-Reset on every call, and Retry-After on a 429.
curl -i https://pingovo.com/api/v1/credits \
-H "X-API-Key: YOUR_API_KEY"Authentication errors
| Status | message | Fix |
|---|---|---|
| 401 | API key is required. Please provide X-API-Key header. | Add the header. |
| 401 | Invalid API key | Check for a typo or a truncated copy. |
| 401 | API key has been deactivated | Re-enable it in Dashboard → API, or create a new key. |
| 401 | API key has expired | Create a new key. |
| 403 | IP address not whitelisted for this API key | Call from an allowed IP, or edit the key’s allowlist. |
| 403 | Email verification required | Verify your account email address. |
| 403 | API_KEY_REVOKED | Pingovo revoked this key (the response names a neutral reason category). It cannot be re-enabled from the dashboard — contact support. |
Insufficient credits
A 402 from /send tells you exactly how short you are:
{
"success": false,
"error": "INSUFFICIENT_CREDITS",
"message": "Not enough credits to send this email",
"credits_required": 11,
"credits_available": 4,
"tracking_credits": 10
}tracking_credits appears only when you asked for tracking and is the part of credits_required that tracking accounts for.
Verify endpoint errors
| Status | error | Meaning |
|---|---|---|
| 400 | EMAIL_REQUIRED | /verify was called without an email. |
| 400 | INVALID_EMAIL | The value is not a well-formed email address. |
| 400 | EMAILS_REQUIRED | /verify/batch was called without an emails array. |
| 400 | EMPTY_ARRAY | The emails array is empty. |
| 400 | BATCH_LIMIT_EXCEEDED | More than 50 emails in one batch. |
| 402 | INSUFFICIENT_CREDITS | Not enough credits for the batch. |
Reliability tips
- Store the ids. Keep
send_idandrequest_idnext to your own record. They are how you and support find a specific email. - Retry only what is safe. A
429or5xxmeans the email was not sent, but a timeout leaves it unknown: checkGET /sends?q=recipientbefore resending, or you may send a customer the same email twice. - Do not retry 4xx. The same request will fail the same way.
- Set client timeouts above 50 seconds if you use
delay_between_emails.