Errors and limits
The error body
Every error, on every route, is JSON with one field, error, and an HTTP
status that says what kind of error it is:
HTTP/1.1 400 Bad Request
Content-Type: application/json
X-Request-Id: Rz8vKm3pWq6tXc1nLb9sHd4fJg7yAe2M
{"error": "minimum payment in USDT_TRC20 is $6.44 right now (network fees)"}
Branch on the status, and log the message. Messages are written for developers, in English, and may be reworded over time: do not match on their exact text, and do not show them to your customers.
Every response, error or not, carries an X-Request-Id header. Quote it when
you write to payitgo's support: it finds the request in the logs.
Status codes
| Status | Meaning | Typical causes | Retry? |
|---|---|---|---|
200 OK |
Done | A read, an update, or POST /v1/payments returning the order's open payment |
|
201 Created |
Created | A new payment, key, user, store or payout | |
202 Accepted |
Queued | A webhook test or a redelivery: it is sent within seconds | |
400 Bad Request |
The request is invalid | A missing or malformed field, an unsupported token, an amount under the minimum, a webhook URL that is not https:// |
No: fix the request |
401 Unauthorized |
Not authenticated | missing api key, invalid api key (unknown or revoked), missing token, invalid or expired token, invalid credentials |
No: fix the key, or refresh the access token |
403 Forbidden |
Authenticated, but not allowed | Live key while live mode is off, a suspended account, a role without the permission (insufficient permissions), a storefront key changing the account's webhook, a read-only key creating or changing something, a payment for a customer you blocked, a wrong password |
No |
404 Not Found |
No such thing | An unknown id, a payment of the other mode, an order with no open payment, an unknown route (Not Found) |
No |
409 Conflict |
It clashes with the current state | An email already registered, removing the last owner, choosing the coin of a payment twice, simulating a test payment while its simulation runs, cancelling a payout that is no longer requested |
No |
413 Request Entity Too Large |
The body is over 1 MiB | No | |
429 Too Many Requests |
Over the rate limit | See below | Yes, after a short wait |
500 Internal Server Error |
Something failed on payitgo's side | The message is generic (payment creation failed, could not set the webhook…); the details are in payitgo's logs |
Yes, with backoff |
The message for each error of each endpoint is listed on its page: Payments, Checkout, Webhooks, Authentication, Account and team.
Rate limits
| Routes | Limit | Counted per |
|---|---|---|
/v1/* |
20 requests a second, bursts up to 40 | API key |
/merchant/login, /merchant/signup |
5 requests a second, bursts up to 10 | IP address |
POST /checkout/{checkout_token}/token |
5 requests a second, bursts up to 10 | IP address |
POST /checkout/{checkout_token}/simulate |
5 requests a second, bursts up to 10 | IP address |
Over the limit you get:
HTTP/1.1 429 Too Many Requests
{"error": "rate limit exceeded"}
Wait a second and retry. Webhooks make polling unnecessary for almost every integration: if you hit the limit, you are probably polling payments you could be told about.
Retrying safely
Networks fail. These requests can be sent again without doing anything twice:
| Request | Why it is safe |
|---|---|
POST /v1/payments with the same order_id |
While the order has an open payment you get that payment back (200), not a second one |
PUT /v1/webhook |
Setting the URL it already has changes nothing |
DELETE /v1/webhook |
Removing a URL that is already removed changes nothing |
GET anything |
Reads change nothing |
POST /v1/webhook/test is harmless to repeat, but each call sends one more
ping.
On a 5xx or a network error, retry with exponential backoff (for example
1 s, 2 s, 4 s, up to a minute). On a 4xx other than 429, retrying the same
request gets the same answer.
Conventions
- Requests are JSON with
Content-Type: application/json. Without that header the body is not read. - Amounts are decimal strings in responses (
"49.9","0.00117"). Requests accept a JSON number or a decimal string. Parse responses with a decimal type, not a float. - Times are RFC 3339 in UTC (
2026-10-10T09:00:00.112Z). The number of fractional digits varies: use a real RFC 3339 parser. - Optional fields are left out of a response when they have no value. Code that reads them must handle their absence.
- New fields may appear in responses at any time, and unknown fields in requests are ignored: parse leniently.
- Lists take
limit(1 to 200, default 50) andoffset, and returntotal. - Ids are opaque strings: store and compare them whole, and never read
meaning into their shape. An id is 24 lowercase hex characters
(
6710c3a1f2b4e5d6a7b8c9d0), whatever it is: a payment, a settlement, an event, an API key. API keys and webhook signing secrets are 64 hex characters; they do not say their mode either (a key'slivemodedoes).