Guides

Errors & retries

Every error, at the request level or per target, uses one normalised shape, so you (or an agent) can handle failures the same way everywhere.

The error envelope

Errors return { "error": { … } } with a stable type, a human message, an optional platform, and a retryable boolean.

{
  "error": {
    "type": "entitlement_exceeded",
    "message": "Not enough credits: this post costs 5, you have 2.",
    "platform": null,
    "retryable": false
  }
}

Two levels of error

A per-target failure never changes the status code. You get 201 whether one target failed or all of them, and whether the post was immediate or scheduled. So read state and targets[], not just the status:

post stateWhat happened
publishedEvery target went out.
partialSome targets published, some failed. Check each one.
scheduledAt least one target is queued to fire.
failedNothing published, and nothing is queued. On a scheduled post this means every target was refused, so it will never fire.

When a network refuses

These come from the network rather than from us. We recognise the message, attach a code you can branch on, and say what actually fixes it. Anything we do not recognise is passed through unchanged, because a confident wrong instruction is worse than none.

CodeNetworkWhat to do
account_unknownAnyA connected-account id does not resolve, or is no longer connected. List them with GET /v1/social-accounts, or retrieve one with GET /v1/social-accounts/{id} to check it is still connected.
aspect_ratio_unsupportedInstagramInstagram only accepts aspect ratios between 4:5 and 1.91:1. Crop the image and try again.
branded_content_not_enabledAnyThe Page has not turned on branded content partner allowlisting. In Meta Business Suite open the Page, go to branded content settings and turn on partner allowlisting. Reconnecting will not change it.
branded_content_privateAnyA TikTok post is marked as a paid partnership and set to private at the same time. Branded content has to be visible to somebody. Pick PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR, or drop the paid-partnership disclosure. TikTok's own composer greys the private option out for the same reason, and its API refuses the pair.
channel_goneAnyA target was accepted and the channel was disconnected before it published. This target failed and was not charged. Reconnect the channel and post again; the other targets on the post are unaffected.
creator_marketplace_not_onboardedAnyThe brand has not accepted the creator marketplace terms. In Meta Business Suite, open Creator Marketplace, choose Get Started and accept the terms. Nothing in this API can do it: it is a one-off consent a person has to give.
creator_marketplace_unavailableAnyThe network has not published this edge at all. Search creators through an Instagram account connected the Facebook way instead. Reconnecting or re-requesting the permission will not change it.
creator_search_unsupportedAnyThe network has no creator marketplace at all, so there is nothing to search. Check discovers.creators on GET /v1/social-accounts/{id} first. A false there is a fact about the network, not something a reconnect or a permission changes.
duplicate_postFacebook, XFacebook refuses two identical posts in a row. Change the wording and post again.
field_unknownAnyThe request carried a field we do not recognise, usually a typo. Remove it or fix the spelling. A mistyped field would otherwise be ignored silently, which is how a setting you thought you sent goes missing.
field_wrong_typeAnyA field was sent as the wrong shape, e.g. a string where an array was expected. `param` names it. Check the field named in `param` against the schema at GET /openapi.json.
handle_not_a_usernameAnyA numeric id was passed where a username was expected. Instagram looks people up by username only. If the id came from a hashtag result, stop: Meta strips the author from those and publishes no way back from a media id or permalink to the person. Creator search is the only route from a post-shaped thing to a contactable account.
handle_not_foundAnyThe network has no account with that handle. Check the spelling. Instagram only answers for professional (business or creator) accounts, so a personal handle comes back as not found. A media or post id is not a handle: Meta publishes no way to get from a post to its author, so a hashtag result cannot be turned into one.
image_too_largeTikTokTikTok rejected an image's dimensions after upload. Images must be between 540x960 and 1080x1920.
instagram_facebook_door_requiredAnyThe feature exists only on an Instagram connected through Facebook, and this one was connected with Instagram Login. Reconnect through Facebook: GET /v1/connect-link with platform=instagram and variant=facebook. Creator search, branded content and messages exist only on that door, and no permission change reaches them from the other one. Check connectVariant on GET /v1/social-accounts/{id} before calling.
instagram_messages_switched_offInstagramThis Instagram account has message access switched off, which is separate from the permission. In the Instagram app open Settings, then Messages and story replies, then Connected tools, and turn on Allow access to messages. Reconnecting will not fix it.
instagram_reply_window_closedInstagramInstagram only allows a reply within 24 hours of the person's last message, and that window has closed. A reply a person writes can use humanAgent: true for 7 days; otherwise answer from the Instagram app.
invalid_return_urlAnyThe connect-link returnUrl is missing, too long, not a URL, or not https (except localhost). Send an absolute https URL, e.g. https://yourapp.com/social-connected. http is allowed only for localhost during development.
invalid_webhook_eventsAnyWebhook events were not sent as an array. Send an array of supported event names.
invalid_webhook_urlAnyThe webhook URL cannot be parsed as a complete URL. Provide a complete HTTPS URL.
linkedin_video_unsupportedLinkedInLinkedIn does not accept video posts to a personal profile through the API. Post it as an image or text, or use a Company Page.
media_requiredYouTubeYouTube needs the video file itself. Attach a video and try again.
media_too_largeBlueskyBluesky caps images at roughly 1MB. Compress the image and try again.
media_type_unsupportedInstagram, XInstagram rejected this file. Use JPEG for images, or MP4 with H.264 video and AAC audio.
media_unknownAnyA media id in the request does not resolve. Upload with POST /v1/media and use the med_… id it returns. A media id is scoped to your account, so one from somewhere else will not resolve.
media_unreadablePinterestPinterest could not read the image. Make sure it is a public JPEG or PNG under 20MB.
meta_identity_checkFacebookFacebook wants this Page's identity confirmed before it will publish. Open Meta Business Suite, finish the prompt on the Page, then post again. It is a one-time step.
missing_permissionFacebook, InstagramFacebook has not granted a permission this post needs. Reconnect the Page and accept every permission on the consent screen.
missing_scopeTikTokThe TikTok connection is missing a permission it needs. Reconnect TikTok and accept every permission on the consent screen.
needs_reauthTikTok, Facebook, Instagram, X, LinkedIn, Bluesky, ThreadsThe TikTok connection has expired. Reconnect TikTok, then post again.
no_channelsAnyThe account has no connected channels at all, so there is nowhere to publish. Connect one first. GET /v1/connect-link returns a signed link a person can open to authorise a network; the API cannot complete an OAuth consent on its own.
no_matching_accountsAnyThe requested platforms are real, but nothing is connected on them. Connect that network first, or pick one you already have from GET /v1/social-accounts.
no_targetAnyThe request named no destination: no accounts, and either no profile or a profile with nothing connected. Pass `accounts` with acc_… ids from GET /v1/social-accounts, or `profile` with a username that has channels connected.
option_invalidYouTubeThat YouTube category id does not exist. Pick one YouTube lists for your region.
pinterest_board_missingPinterestThat Pinterest board no longer exists or is not visible to this account. Pick another board.
pinterest_site_blockedPinterestPinterest could not fetch a media URL from our host. Republish: images and covers are uploaded as bytes now, not fetched by Pinterest.
post_state_invalidAnyThe post is not in a state that allows this operation. Check `state` on GET /v1/posts/{id}. Drafts are edited and deleted; scheduled posts are moved and cancelled; published posts are retracted.
profile_requiredAnyThe account has more than one profile, so which to post to is ambiguous. Pass `profile` with one of the usernames from GET /v1/profiles.
profile_unknownAnyThe named profile does not exist on this account. List them with GET /v1/profiles and use a `username` from there.
quota_exceededYouTubeThe daily YouTube API quota is spent. It resets at midnight Pacific time.
rate_limitedTikTok, BlueskyTikTok allows 6 requests a minute per account. Wait a minute and try again.
schedule_in_pastAnyscheduledAt is not in the future. Send a future ISO timestamp, or omit `scheduledAt` to publish now.
temporarily_blockedFacebookFacebook has temporarily blocked this action, usually after too much activity too quickly. Wait a few hours and try again.
text_invalidYouTubeYouTube rejected the description. Remove any < or > characters and keep it under 5,000.
text_requiredAnyThe post has no text. Send a non-empty `text`. If the post is media-only, send a space or a caption: every network we publish to stores a caption field.
text_too_longXThe post is over X's character limit. Shorten it and try again.
tiktok_account_restrictedTikTokTikTok has restricted this account from creating posts. Only TikTok can lift that, so check the account's status in the TikTok app.
tiktok_privacy_levelTikTokPick a privacy setting this account actually offers. Call the publish-info endpoint to see which ones are available to it.
tiktok_spam_riskTikTokTikTok's anti-spam checks blocked this post. Slow the posting rate down and try again later.
tiktok_too_many_pendingTikTokTikTok caps you at 5 uploads awaiting action in any 24 hours. Finish or delete the pending ones in the TikTok app, then post again.
tiktok_unauditedTikTokThis TikTok app is still in limited mode: the account must be set to Private and the post visibility set to Only me. Change both in the TikTok app and try again.
tiktok_url_unverifiedTikTokThis is ours to fix, not yours. Our media domain needs re-verifying with TikTok. Contact support and we will sort it.
timezone_invalidAnyThe timezone is not an IANA zone name. Use an IANA zone name, e.g. Europe/London or America/New_York. Abbreviations like GMT or PST are not accepted because several of them are ambiguous.
timezone_without_scheduleAnyA timezone was sent with no scheduledAt to interpret. Drop `timezone`, or add `scheduledAt`. A timezone only means something when there is a time to interpret.
title_invalidYouTubeYouTube rejected the title. It cannot be empty, and cannot contain < or >.
unknown_webhook_eventAnyAt least one requested webhook event is unsupported. Use one of the documented webhook event names.
webhook_https_requiredAnyThe webhook endpoint does not use HTTPS. Use an HTTPS endpoint.
x_account_lockedXX has locked this account. Sign in at x.com and complete their checks, then try again.
x_unavailableXX posting is briefly unavailable on our side. Nothing is wrong with your post or your account. Try again shortly, and contact support if it persists.
youtube_no_channelYouTubeThis Google account has no YouTube channel. Create one at youtube.com, then reconnect.
youtube_upload_limitYouTubeThis channel has hit YouTube's upload limit for now. Wait a few hours and try again.

Checking before you publish costs nothing: POST /v1/posts/validate runs the same checks and returns the same codes and fixes per target, without creating anything.

Error types

TypeHTTPMeaningRetry?
invalid_request400Malformed body or invalid field/option.Fix & resend
unauthorized401Missing or invalid API key.No
entitlement_exceeded402Out of credits, or your plan doesn't allow this (e.g. X/TikTok on free).After topping up / upgrading
rate_limited429Too many requests.Yes, after a short back-off
platform errorsA specific network rejected the target (see platform).Depends on the message

Error codes

Alongside type, a validation error carries a granular code, the request field at fault in param, a plain-language fix, and a docs link back to this table. Branch on code: it is stable, and it is the only field specific enough to act on. type stays coarse on purpose.

{
  "type": "invalid_input",
  "message": "TikTok requires at least one media item",
  "code": "media_required",
  "fix": "TikTok cannot publish text on its own. Attach an image or a video and post again.",
  "docs": "https://docs.postlake.dev/errors#media_required",
  "param": "media",
  "platform": "tiktok",
  "retryable": false
}

Codes we return before publishing

These are caught before anything is charged or published, so a post that fails them never costs a credit. Call POST /v1/posts/validate to get them without creating a post.

CodeparamWhat it meansHow to fix it
media_requiredmediaThe network cannot publish text on its own.Attach at least one image or video. TikTok, Instagram, YouTube and Pinterest all require media.
video_requiredmediaThe network publishes video only.Attach a video file. YouTube will not take a text or image post.
video_not_supportedmediaThe network will not take a video on this post type.Remove the video, or send an image or text-only version to that network.
image_not_supportedmediaThe network will not take images on this post type.Remove the images, or send the post to a network that supports them.
media_type_unsupportedmediaThe file format is rejected outright by that network.Convert the file. TikTok takes JPEG and WebP, and rejects PNG.
image_too_largemediaThe image is above the network's pixel ceiling.Resize it. TikTok's ceiling is 1080x1920. Oversized carousels are accepted at upload and fail minutes later, which is why this is checked up front.
image_too_smallmediaThe image is below the network's pixel floor.Use a larger source image. Scaling up to clear the floor will look soft.
text_too_longtextThe caption is over that network's character limit.Shorten the text, or set a shorter caption for just that network with textOverrides.
title_too_longplatformOptions.titleThe title is over that network's limit.Shorten the title. TikTok photo posts allow 90 characters.
option_invalidplatformOptions.*A platformOptions value is not one this network accepts.Set it to one of the listed values. GET /v1/platforms returns the valid set for every option.
needs_reauthaccountsThe connection to that network has expired.Reconnect the channel in the dashboard, then post again. No amount of retrying will fix it.

Codes the networks return

These come back after we have tried to publish. They carry a fix only where we can identify the error with confidence: an unrecognised rejection returns the network's own message and no advice, rather than a guess.

CodeparamWhat it meansHow to fix it
missing_scopeaccountsThe connection lacks a permission the post needs.Reconnect and accept every permission on the network's consent screen.
duplicate_posttextThe network refuses a post identical to a recent one.Change the wording and post again.
rate_limitedThe network is throttling this account.Back off and retry. TikTok allows 6 requests a minute per account.
temporarily_blockedMeta has temporarily blocked the action after too much activity.Wait a few hours, then try again.
quota_exceededThe daily API quota for that network is spent.Wait for the reset. YouTube's is midnight Pacific.
tiktok_too_many_pendingTikTok caps you at 5 uploads awaiting action per 24 hours.Finish or delete the pending uploads in the TikTok app.
tiktok_account_restrictedTikTok has restricted the account from posting.Only TikTok can lift this. Check the account in their app.
tiktok_spam_riskTikTok's anti-spam checks blocked the post.Slow the posting rate and try again later.
tiktok_unauditedThe TikTok app is in limited mode.Set the account to Private and the post to Only me.
media_too_largemediaThe file is over the network's byte limit.Compress it. Bluesky caps images near 1MB.
media_unreadablemediaThe network could not read the file.Check it is a public, valid image or video.

Retrying safely

When retryable is true (e.g. rate_limited), back off and retry. Always send the same Idempotency-Key on retries so a post that actually went out isn't duplicated. On 429, honour any Retry-After header. Non-retryable errors (unauthorized, invalid_request) need a fix, not a retry.

A 403 that isn't from us

If you get a 403 whose body is a Cloudflare page mentioning error 1010, that is our edge rejecting the request before it reaches the API. It is not an authentication problem, so checking your key will not help.

The usual cause is Python's standard library: urllib.request sends User-Agent: Python-urllib/3.x, which the bot rules block. Set any other agent and it goes through:

req.add_header("User-Agent", "my-app/1.0")

requests, httpx, curl, fetch and the Go client all send their own agent and are unaffected.