Errors
Every error is JSON in the RFC 9457 problem details format, served as application/problem+json, with a stable code you can branch on.
The shape
Response · 400 Bad Request
{
"type": "https://postpeg.com/errors/invalid_request",
"title": "Invalid request",
"status": 400,
"detail": "X: text is 312 characters, the limit is 280",
"code": "invalid_request",
"request_id": "8f14e45f-ceea-467a-9575-1f2a5c3e6b0d",
"issues": [
{
"path": "content",
"message": "X: text is 312 characters, the limit is 280"
}
]
}codeis stable and only ever gains new values. Branch on it, not ondetail.detailis written for a person: show it, log it, don’t parse it.issuesappears oninvalid_requestand lists every problem found, each with thepathof the field.retry_afterappears onrate_limited, in seconds, matching theRetry-Afterheader.request_idmatches theX-Request-Idresponse header. Send your ownX-Request-Idto correlate with your logs, and quote it when you contact us.
Codes
| Code | Status | What to do |
|---|---|---|
invalid_request | 400 | The body, query or a platform rule is wrong. Read issues: it lists every problem with its path. Fix the request; retrying it unchanged fails the same way. |
unauthorized | 401 | The key is missing, malformed or revoked. Send Authorization: Bearer pp_live_… with a key from the dashboard. |
account_limit | 402 | Connecting one more account would go over your plan. Disconnect an account or upgrade. |
subscription_required | 402 | The trial or subscription has ended. Only GET /v1/me and /v1/billing still work: start a checkout to continue. |
forbidden | 403 | The network refused because the account’s login has expired. The account is now reconnect_required: run the connect flow again. |
not_found | 404 | No such route, or no object with that id on your account (disconnected accounts count as gone). |
conflict | 409 | The object is in the wrong state for this request, for example cancelling a post that already went out. detail says which. |
rate_limited | 429 | Too many requests, from your key or on the network’s side. Wait retry_after seconds (also in the Retry-After header), then retry. |
internal_error | 500 | Something broke on our side. Retry with backoff, and quote request_id if it persists. |
upstream_error | 502 | The network failed or refused the request; detail carries its reason. Retry later if it didn’t respond; fix the request if it refused. |
platform_unavailable | 503 | That feature or network is not available on postpeg yet. Retrying won’t help until it is. |
What to retry
Retry rate_limited after retry_after, and internal_error and upstream_error with backoff when the network didn’t respond. Don’t retry invalid_request, unauthorized, account_limit, subscription_required or platform_unavailable unchanged: they’ll fail the same way. Send an Idempotency-Key on writes so a retry is always safe.
Publishing itself retries on its own: a failed target is retried in the background, and you see the outcome on the post. See Retries.