Errors & rate limits
Errors use a consistent JSON shape and standard HTTP status codes.
Error shape
{
"statusCode": 403,
"error": "Forbidden",
"message": "Insufficient company permissions."
}Status codes
400: malformed request or invalid parameters.401: missing, invalid, expired or revoked key.402: no active plan yet.403: the key lacks the action on that wallet (or its creator lacks the role), the endpoint is dashboard-only, the chain/network isn't in your plan, or a hard plan cap is reached (free Developer tier only; on paid plans, wallets/addresses beyond your included limits are created normally and bill as monthly overage).404: resource not found (or not in your company).409/422: conflict or unprocessable request.429: rate limited.5xx: server error; retry with backoff.
Policy refusals
When one of the organization's spending policies refuses a withdrawal, the 403 carries two extra fields: code, and policyIds (comma-separated, for support and your logs). Nothing was reserved, signed or sent.
{
"statusCode": 403,
"error": "Forbidden",
"message": "Blocked by policy. "Daily limit": $52,400.00 in the last 24 hours by this person would be over the $50,000.00 limit ($51,150.00 already sent).",
"code": "policy_denied",
"policyIds": "4"
}policy_denied: not allowed at all. Don't retry it unchanged; the message names the rule.policy_approval_required: a direct withdrawal needs approval. Propose it withPOST /withdrawal-requestsand a person approves it.policy_approvals_pending: an approval was attempted before every sign-off a policy asks for was in.
The SDKs raise these as PolicyError, a subclass of ApiError, with policyCode and policyIds (policy_code and policy_ids in Python).
Rate limits
A relaxed global ceiling of 300 requests per minute per IP applies to every endpoint. On top of that, value-moving writes have a tighter, per-caller limit, counted per API key and scoped to each endpoint:
20/min per caller: withdrawal prepare/broadcast and wallet create/update/delete.
Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds). A 429 additionally sets Retry-After (seconds) and returns the standard error shape:
{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 30 seconds."
}On a 429, wait for Retry-After before retrying; don't retry in tight loops. Rate limits guard burst traffic only; there is no monthly cap on API requests.