Errors and rate limits
Every failed request returns the same JSON shape with a stable code. Here's what each code means and what to do about it.
Failed requests use a standard HTTP status and a JSON body with a machine-readable code:
{
"ok": false,
"error": {
"code": "amount_mismatch",
"message": "amount (5000) doesn't match the items total (4900).",
"param": "amount"
}
}codeis stable. Branch on it in your code.messageis for people. It's safe to log, but its wording can change.paramnames the request field the error is about, likeitems[0].amount, when there is one.
Every response also carries a Request-Id header (req_…). Log it next to failures: searching for it in Dashboard → Developers → Logs shows that exact request and our response. Include it if you contact support.
Codes
| Status | Code | What happened |
|---|---|---|
| 400 | invalid_json | The body isn't a JSON object. Check Content-Type: application/json. |
| 400 | invalid_request | A field is missing, the wrong type or out of range. See param. |
| 400 | invalid_amount | The items add up to less than 99 or more than 100,000,000 minor units. |
| 400 | amount_mismatch | amount doesn't equal the items total. |
| 400 | invalid_currency | Lollipay doesn't support that currency. |
| 400 | not_supported | The field exists in the API design but isn't available yet, like coupon_code or items[].product_id. |
| 401 | unauthorized | No key, a revoked key, or an HMAC signature that doesn't match the request. |
| 401 | invalid_signature | HMAC headers are missing or malformed. |
| 401 | stale_signature | The HMAC timestamp is more than 5 minutes off. |
| 403 | forbidden | The key is valid but lacks the permission the endpoint needs. |
| 404 | store_not_found | The store ID in the path isn't the key's store. |
| 404 | checkout_not_found | No session with that ID for this store and mode. |
| 413 | payload_too_large | The body is over 64 KB. |
| 429 | rate_limited | Too many requests for this key. Wait for Retry-After. |
| 500 | internal_error | Something failed on our side. Retry with backoff. |
| 503 | service_unavailable | The API is temporarily unavailable. Retry with backoff. |
Retrying
- Retry
429,500and503with exponential backoff (for example 1s, 2s, 4s, with jitter). - Don't retry other
4xxresponses unchanged; fix the request first. - A retried create can open a second session. That's harmless (the unused one expires), but store the
idof the session you redirect to, not the first one you created.
Rate limits
Each key can create 100 checkout sessions per minute. Past that, requests get 429 with a Retry-After header
in seconds:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{ "ok": false, "error": { "code": "rate_limited", "message": "Too many checkout sessions for this key. Try again in a minute." } }If you need more for a launch or a sale, contact support@lollipay.cc.