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), the plan's queue full (queue_full), a temporary-mailbox signup (disposable_email).
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. account_suspended: the workspace is suspended. media_upload_limit: the month's media uploads (fair use) are used up.
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 over the plan's file size: file_too_large_for_plan), or one that must be uploaded in parts (use_multipart).
429Too many requests (rate_limited, per endpoint or per key a minute), too many publishes running at once (too_many_concurrent), too many new accounts from one network or browser (signup_limit), a plan's AI drafts a day used up (ai_daily_limit), or the month's AI drafts and any AI credits used up (ai_month_limit, with buy_ai_credits links and the plan above; back on the 1st).
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.
duplicate_recentA recurring post's occurrence was not sent to this network: the same text went out there in the last guard_days days.Add variants to the recurring post, or lower its guard_days.
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 (on Pro, Agency and Scale: the published fair-use ceiling, with fair_use: true). Comes with upgrade_url.Wait for the 1st, or upgrade.
daily_limitThe plan's posts for today (UTC) are used up — fair use, a post counts per network (limit, resets_at; upgrade_url when a bigger plan allows more).Post it after 00:00 UTC, or schedule it.
throttledThis workspace published far more this hour than its usual hour (over 10 times its median, never under a quarter of its day), so publishing is paused until the hour ends (retry_after, resets_at). Scheduled posts keep their place.Wait for the hour to end; if the volume is expected, write to support.
media_egress_limitThe month's media sent to networks is used up (fair use; each uploaded file counts once per network it goes to). Text posts and media hosted elsewhere still go out.Wait for the 1st, link media hosted elsewhere, or upgrade.
account_suspended403, whole request. The workspace is suspended by PostWire: nothing is published, from the API or the queue.Write to support.
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.
x_credits_requiredX: not enough X credits for this post (for a thread: for the whole thread). Nothing was sent to X. Comes with buy_credits (links) and credits_needed.Buy top-up credits, or wait for the 1st.
bad_x_thread400, whole request. A post of the X thread or the reply is empty, not text or over 280 characters as X counts them, or the thread has more than 25 posts. Nothing was published.Shorten or split the post the error names.
x_paid_plan_requiredX: the account is on Free or in its trial. X is on paid plans. Comes with upgrade_url when there is a plan to buy.Upgrade, or leave X out.
x_daily_limitX: the plan's X posts for today are used (Starter 5, Pro 20, Agency 60, Scale 150); every post of a thread counts.Post it after 00:00 UTC.
x_pausedX: posting to X is paused on PostWire. No credits were used.Try again later.
x_no_answerX never answered (or answered 500/504): PostWire can't tell whether it went out, so the credits stay used.Check the X account before posting again.
pending_approval202. The brand's approval rules (or a limit on this key) hold the post for a person; nothing was sent. Comes with id, approval_id, who was notified and review_url.Wait: GET /api/post/status with the id says when it is approved, rejected or published.
key_network_not_allowed403, whole request. This API key or AI app may not post to that network (a limit set in the dashboard). Nothing was published.Post to an allowed network, or ask the owner to change the key's limits.
key_brand_not_allowed403, whole request. This API key or AI app may not post for that brand.Use an allowed brand.
key_monthly_cap_reached403, whole request. This API key or AI app used its posts for the month (cap, used, resets_on).Wait for the 1st (UTC), or ask the owner to raise it.
approval_requires_human403. Approving, rejecting and changing approval rules or key limits is for a person in the dashboard (or the emailed link), never an API key or an AI connector.Open the review link in a browser.
governance_unavailable503. The approval rules or key limits could not be checked, so nothing was published.Try again in a minute.
role_forbidden403, whole request. Your role in this workspace (or the role of the member who created this key) can't do this — e.g. a Viewer or an Approver publishing. Says which role and what it can't do.Ask an admin, or use a key of a member with that role.
seat_read_only403. The workspace has more members than its plan's seats (after a downgrade): the latest members are read-only until a seat is free.The owner upgrades, or someone leaves.
brand_restricted403. You work with only some brands of this workspace and this covers all of them.Ask an admin.
workspace_forbidden403. Not a member of that workspace (any more), or an API key asked for another workspace (X-PostWire-Workspace).Use a key created inside that workspace.
key_owner_left401. The member who created this key or AI app left the workspace, so it no longer works.Create a new key.
seat_limit403. Every seat of the plan is taken (members plus open invitations). Comes with the next plan's checkout link.Remove a member, cancel an invitation, or upgrade.
team_requires_human403. Inviting, changing roles, removing and transferring are for a person in the dashboard, never an API key or an AI connector.Do it in Settings → Team.

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
Every authenticated callper API key (or connected AI app), a minute: Free 60, Starter 120, Pro 300, Agency 600, Scale 1,200
POST /api/post60 a minute per account; publishes running at once per workspace: Free 2, Starter 3, Pro 5, Agency 10, Scale 20 (429 too_many_concurrent)
POST /api/generate8 a minute per account, plus the plan's AI drafts a day and a month
POST /api/week3 an hour per account (each counts as one AI draft)
POST /api/bulk60 dry runs and 20 uploads an hour per account
POST /api/rss/preview30 an hour per account; POST /api/rss/{id}/check 6 an hour per feed
POST /api/media/upload-url60 an hour per account
POST /api/media/upload-link30 an hour per account
POST /api/media/video10 an hour per account; 10 a day on Free, 60 on paid plans
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; new accounts: 10 a day per network and 3 a day per browser (429 signup_limit); temporary-mailbox domains are refused (400 disposable_email)
POST /api/approvals/link…120 every 10 minutes per IP address; POST /api/approvals/{id}/decision 120 every 10 minutes per account
POST /api/team/invites30 invitations (and resends) a day per workspace; each one resent at most once an hour
POST /api/invites/…60 every 10 minutes per IP address; 4 codes per 15 minutes per invitation

Plan limits (posts a month and a day, AI drafts a day and a month, brands, networks per post, the queue, media a month) 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 and fair use.

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"
}