Errors, rate limits and idempotency

Every error has the same shape and a machine-readable code, so a flow can branch on it without parsing English.

View as MarkdownOpenAPIBase URL https://postwire.io

Error shape

JSON
{
  "error": "not connected: tiktok. Connect it in Accounts, or post only to bluesky.",
  "code": "not_connected",
  "platforms": [
    "tiktok"
  ]
}
StatusWhen
400The request is wrong: a missing field, a past run_at, a network not connected (code not_connected), media required (media_required) or unreachable (media_unreachable).
401No API key, or a key that does not exist or was revoked.
402A feature the plan does not include (plan_feature), with the plan that does.
403Email not verified, or a key that cannot publish.
404Not found (not_found, brand_not_found). Ids that are not valid answer 404 too.
409ambiguous_brand (say which brand), duplicate_post (see idempotency), or a scheduled post that is already publishing.
413A file too large (too_large), or one that must be uploaded in parts (use_multipart).
429Too many requests (rate_limited), or a plan's daily AI drafts used up.
503Something on PostWire's side is not available (auth_unavailable, ai_unavailable, webhooks_unavailable). Retry.

Result codes per network

In results[] of POST /api/post, in a scheduled post's results and in post.failed webhooks. The list is open: new codes can appear, so treat an unknown code as a failure with the text in error.

codeMeansWhat to do
not_connectedThe network is not connected (in that brand).Connect it, or leave it out of the request.
ambiguous_brandThe network is connected in several brands and no brand_id was sent (whole request, 409).Send brand_id.
media_requiredTikTok, YouTube and Instagram need a video or photos; a Reel needs a video.Add video_url, photo_url or media.
media_unreachableA media link could not be downloaded (private, expired, deleted, or a web page). Checked once before anything is sent.Use a public direct link, or upload the file.
bad_mediaThe media list does not fit the network (too many items, a type it does not take).Follow the error text.
caption_is_jsonThe text was a JSON object, not the words of the post.Send the post's words as text.
empty_textNothing to publish.Send text, a per_platform draft, or media.
limit_reachedThe month's posts on the plan are used up. Comes with upgrade_url.Wait for the 1st, or upgrade.
networks_per_postheld: true. On Free a post goes out to 2 networks; this one was written and not sent, and spent nothing.Upgrade, or send it in another post.
rate_limitedThe network asked to wait (retry_after seconds) and the post could not be queued for after it.Post it again after retry_after.
rate_limited_deferredok: true, status scheduled. The network asked to wait longer than the request had; this network was queued for right after the wait.Nothing. Follow schedule_id.
timed_outstatus: unknown. The network did not answer before PostWire had to reply.Check the network before posting again.

A failed network also carries what and fix: the same plain-words explanation the failure emails use.

Request rate limits

Over a limit the API answers 429 with a Retry-After header (seconds) and the same number in the body:

JSON
{
  "error": "too many requests — please slow down and retry in 60 seconds",
  "code": "rate_limited",
  "retry_after": 60
}
EndpointLimit
POST /api/post60 a minute per account
POST /api/generate8 a minute per account, plus the plan's AI drafts a day
POST /api/week3 an hour per account (each counts as one AI draft)
POST /api/media/upload-url60 an hour per account
POST /api/media/upload-link30 an hour per account
POST /api/keys20 a day per account
POST /api/webhooks20 a day per account; POST …/test 30 an hour
GET /api/connect/{platform}/health240 an hour per account
POST /api/signup5 an hour per IP address, 3 a day per email

Plan limits (posts a month, AI drafts a day, brands, networks per post, scheduled posts at a time) are not rate limits: they answer with their own code and an upgrade link, and GET /api/usage shows each one. See plans and limits.

Network rate limits

Networks have their own limits. Telegram allows about one message a second per chat and 20 a minute in a group. PostWire spaces posts to the same chat, waits out a Telegram 429 when the request has time for it, and otherwise queues that network for right after the wait: its result is ok: true, status: "scheduled", deferred: true, code rate_limited_deferred, with the schedule_id and run_at. Nothing is lost, and the queued post is counted and sent to your webhooks when it goes out.

Idempotency: retries without duplicates

A publish cannot be undone, and n8n, Make, Zapier and agents all retry. So POST /api/post refuses the same post twice:

  • Send idempotency_key (any string up to 200 characters). The same key on the same account within 24 hours answers 409 duplicate_post and publishes nothing.
  • Without a key, an identical request (same networks, text, drafts, media and brand) within 2 minutes answers 409 duplicate_post.
  • When nothing went out (every network failed), the lock is released at once, so the retry that fixes the problem is not refused.
  • The key travels in the post.published and post.failed webhooks, so you can match events to your request.

Request

JSON
{
  "platforms": [
    "linkedin"
  ],
  "text": "Doors open at 7.",
  "idempotency_key": "order-1842-announce"
}

The same request again

JSON
{
  "error": "That idempotency_key was already used for a post on this account in the last 24 hours. Nothing was published again.",
  "code": "duplicate_post"
}