Short version: LinkedIn posting errors are plain HTTP status codes with a short message, and most of them map to one fix. A 400 is a malformed request (often a wrong URN type, a missing field or a missing Linkedin-Version header), a 401 is a token problem, a 403 is a missing scope or company page role, a 422 is a request LinkedIn understood but refused on meaning, a 426 is an API version that has been sunset, and a 429 is a daily quota that resets at midnight UTC. Media failures usually happen before the post is created: the image, video or document asset is still processing, failed processing, or its upload URL expired. One date matters this week: LinkedIn's docs say Marketing version 202511 will be sunset on 16 November 2026, and calls still pinned to it will start failing. This guide is built only from LinkedIn's developer documentation on Microsoft Learn, read on 11 November 2026.
What a LinkedIn error looks like
LinkedIn's error-handling guide documents three fields:
{
"message": "Empty oauth2_access_token",
"serviceErrorCode": 401,
"status": 401
}
status is the HTTP status, serviceErrorCode is "A subcode that further classifies the error", and message is the description. LinkedIn has also introduced a newer schema with a constant code string plus errorDetailType and errorDetails, but its page says these are "available for adCampaignGroups, adCampaigns and adAccounts endpoints only". LinkedIn does not document the exact body shape for Posts API errors, so log the full body and match on both the status and any constant such as INVALID_URN_TYPE, whether it appears in message or code. Version errors are documented with a code field (see below). Log the response headers too: for 500s LinkedIn asks you to report x-li-uuid, x-li-fabric and x-li-request-id. Our JSON formatter is handy for reading captured bodies.
Summary table
| Status | Usual cause on LinkedIn | Bucket |
|---|---|---|
| 400 | Bad URN, missing or invalid field, missing version header, expired upload URL, asset not ready | Fix the request |
| 401 | Missing, invalid, expired or revoked token | Needs the user (or a refresh) |
| 403 | Missing scope or company page role | Needs the user or app review |
| 404 | Wrong path or URN; sometimes a restricted API | Fix the request |
| 409 | Write conflict | Retry |
| 411 | POST with empty body and no Content-Length | Fix the request |
| 422 | Semantically invalid request | Fix the request |
| 426 | Sunset API version | Upgrade the version header |
| 429 | Daily rate limit reached | Wait for the reset |
| 500, 503, 504 | LinkedIn-side failure or timeout | Retry with backoff |
1. Fix the request: 400, 404, 411 and 422
The Posts API page lists the specific 400 messages you will see when creating posts:
| Message | What LinkedIn says | What to do |
|---|---|---|
| INVALID_URN_TYPE | "{field} value {value} must be a {urnType} URN." | Check author is an organization or person URN and content.media.id is the right media URN type (image, video, document). |
| INVALID_URN_ID | The URN ID provided is invalid | Check the format of every URN in the body. |
| MISSING_FIELD | A required field is missing | LinkedIn names author, visibility, distribution and lifecycleState as fields to check. |
| INVALID_VALUE_FOR_FIELD | "{field} cannot be set to {value}." | Check enum values. The Post schema says PUBLISHED is the only lifecycleState accepted on creation. |
| FIELD_LENGTH_TOO_LONG | "{field} length exceeds the allowed maximum." | Shorten the commentary or other text field. The Posts API page does not state the maximum. |
| INVALID_VALUE_BLANK_FIELD | "{field} cannot be blank." | Do not send empty strings for required fields. |
The generic error guide adds that the most common cause of a 400 is "bad syntax in the request URL or body", and its first suggested check is an invalid character in the URL. That matters because Rest.li protocol 2.0 requires URL-encoding the whole resource key: the protocol page says (, ), : and , must become %28, %29, %3A and %2C. Putting a raw urn:li:share:... into a GET path breaks that rule; encode it first.
Escape the commentary. Post text uses LinkedIn's "little" text format, where mentions look like @[Name](urn:li:organization:123) and hashtags can use a template. LinkedIn's page is explicit: "All reserved characters need to be escaped with a backslash, even if those characters are not used in one of the supported elements or templates." The grammar lists the reserved characters as | { } @ [ ] ( ) < > # \ * _ ~. The docs do not say what happens if you skip this, so treat escaping as mandatory input sanitisation rather than finding out in production.
The rest of this bucket:
- 404 NOT_FOUND: the post or entity does not exist at that URN, or the path is wrong. LinkedIn notes that in some cases a 404 is returned "when attempting to access a restricted API".
- 411 Length Required: "make sure POST requests with an empty body have a Content-Length header specified." This bites action-style calls with no body.
- 422 UNPROCESSABLE_ENTITY: "The request was well-formed but could not be processed due to semantic errors. Review field values for logical consistency." In other words, each field is valid on its own but the combination is not. Do not retry it unchanged.
2. Needs the user: 401 and 403
LinkedIn's guide lists five 401 cases: an unknown authentication schema, an empty token, an invalid token, an expired token, and a token "revoked by the member from their privacy settings on LinkedIn's website". The header must be Authorization: Bearer followed by the token; anything else is the first case. The Posts API page names the empty-token case EMPTY_ACCESS_TOKEN. One trap: "Access token downstream verification failures return a 500 Internal Server Error", so a token problem can occasionally masquerade as a server error.
Token lifetimes explain most 401s in schedulers. LinkedIn's refresh-token page says "By default, access tokens are valid for 60 days and programmatic refresh tokens are valid for a year", and that programmatic refresh tokens are available to approved Marketing Developer Platform partners. Refreshing does not extend the refresh token: its 365-day lifetime keeps counting down from the original login, so on day 360 both tokens expire within five days and the member must reauthorise. A second, less obvious cause comes from the authorization guide: "If you request a different scope than the previously granted scope, all the previous access tokens are invalidated." Adding a scope to your app and re-running login for one feature can silently kill tokens you stored earlier. LinkedIn also recommends allowing for tokens of at least 1,000 characters; truncating a token column produces invalid-token errors that look random.
A 403 means the token lacks permission. For posting, the Posts API lists w_organization_social (post on behalf of an organization, restricted to members with the ADMINISTRATOR, DIRECT_SPONSORED_CONTENT_POSTER or CONTENT_ADMIN page role) and w_member_social (post on behalf of the member). The ACCESS_DENIED description says to check both the scope and the company page role. Media has its own check: the Images and Videos APIs return "Accessing this image resource is forbidden" (or "video resource") with status 403 when the caller lacks admin or DSC rights on the company page, or is not the owner of a member-owned asset. Also from the Images page: w_member_social is write-only, so a token holding only that scope cannot GET /rest/images on the versioned gateway. If you poll image status with a member token, that 403 is expected.
3. Upgrade: versioning and 426
LinkedIn's versioning page sets the rules. Every call to the versioned base path https://api.linkedin.com/rest/ must send Linkedin-Version in YYYYMM format, and the Posts API page adds that all calls also need X-Restli-Protocol-Version: 2.0.0. Versions ship monthly and are "supported and stable for a minimum of one year before sunset". There are no unversioned calls: "the latest version is not applied by default."
| Situation | Response LinkedIn documents |
|---|---|
| Header missing | 400, "code": "VERSION_MISSING", "A version must be present. Please specify a version by adding the Linkedin-Version header." |
| Header names a sunset version | 426, "code": "NONEXISTENT_VERSION", "Requested version yyyymmdd is not active" |
As of 11 November 2026 the versioning page lists October 2026 (202610) as the latest version (the recent-changes page already refers to an upcoming 202611 release, so check the versioning page for the current latest value before you pin), and LinkedIn's recent-changes page lists two sunsets: 202510 on 15 October 2026 and 202511 on 16 November 2026. If your integration is pinned to either, move it now. LinkedIn's migration advice is literally to change the header value, though you still need to read the changelog for every version you skip, since behaviour can change while paths stay the same. One documentation quirk: the generic error guide calls the header X-LinkedIn-Version in its 426 section, while the versioning page and every code sample use Linkedin-Version. Follow the versioning page.
The same applies to the endpoint itself. The Posts API page says it "replaces the ugcPosts API", and the UGC Post page is now titled "UGC Post API (Legacy)". Under the site-wide 202511 sunset banner it adds that "The above deprecation is applicable for partners with Marketing permissions only", which qualifies the version sunset, not the ugcPosts endpoint. The self-serve Share on LinkedIn guide still documents POST https://api.linkedin.com/v2/ugcPosts with w_member_social, so check which product your app actually has before migrating.
4. Wait: 429 rate limits
LinkedIn's rate-limit page says limits are counted per 24 hours and "reset at midnight UTC every day", at two levels: per application and per member per application. LinkedIn does not publish the numbers: "Standard rate limits are not published in documentation." You find yours in the Developer Portal under your app's Analytics tab, which only shows endpoints you have called at least once that UTC day. Developer admins get an email when the app passes 75% of its quota, but only for app-level breaches and with "an expected delay of approximately 1-2 hours".
The 429 message is "Resource level throttle limit for calls to this resource is reached." Because the quota window is a UTC calendar day, short backoff will not help once the daily quota is truly exhausted (our inference; LinkedIn's Posts API table only says to "retry after a delay"). If retries keep returning 429, reschedule the job for after midnight UTC and tell the user. LinkedIn also says that "In rare cases, LinkedIn may also return a 429 response as part of infrastructure protection", which clears on its own, so a single 429 early in the day is worth one delayed retry before you park the queue.
5. Media upload failures
LinkedIn media goes up in a separate flow before the post: initialise the upload, PUT the bytes to the returned URL, then reference the asset URN in the post. Each step has its own failure.
Images
The Images API takes "Images with less than 36,152,320 pixels" in JPG, GIF and PNG, with GIFs up to 250 frames, and does not support SYNCHRONOUS_UPLOAD. Initialising returns an uploadUrl and an uploadUrlExpiresAt timestamp. Assets move through WAITING_UPLOAD, PROCESSING, then AVAILABLE or PROCESSING_FAILED, which LinkedIn says covers causes such as "file size too large, unsupported file format, internal error". The documented 400s are INVALID_IMAGE_ID, INVALID_URN_ID, INVALID_URN_TYPE and NOT_FOUND. Our image resize tool keeps files inside the pixel limit.
Videos
The Videos API page gives specs of "Three seconds to 30 minutes", "Between 75kb and 500MB" and MP4. Note that the same page's schema says "Maximum allowed Videos size is 5GB" for fileSizeBytes, so the two figures conflict; validate against the stricter 500MB unless LinkedIn tells you otherwise. Uploads are multipart: parts are byte ranges of 4,194,304 bytes (the first is 0 to 4194303), and you must collect the ETag from each part's response and pass them to finalizeUpload in the same order as the upload instructions. Video-specific errors:
| Error | What LinkedIn says | What to do |
|---|---|---|
| EXPIRED_UPLOAD_URL | The video upload URL is expired | Initialise a new upload. The schema says upload URLs "Typically" expire 30 days after initialising, and that uploading to an expired URL "would throw a 401 error", while the error table lists this as a 400; handle both. |
| MEDIA_ASSET_WAITING_UPLOAD | Media asset is waiting upload | The bytes or the finalize step never completed. Check every part uploaded and finalize was called. |
| MEDIA_ASSET_PROCESSING_FAILED | Media asset failed processing | Read processingFailureReason on the video, fix the file, upload a new asset. |
| UPDATING_ASSET_FAILED | "Please recreate the asset and try again." | Create a new asset. |
| INVALID_VIDEO_ID, INVALID_URN_ID, INVALID_URN_TYPE | Bad identifier | Check the URN you are referencing. |
Captions are optional, and LinkedIn says each video "can include only one caption file, and only English-language captions are supported". For a feed video, the video can still serve if caption processing fails.
Documents
The Documents API says "The file size can't exceed 100MB and 300 pages", and accepts PPT, PPTX, DOC, DOCX and PDF.
The rule that prevents most media errors: do not create the post until the asset reports AVAILABLE. Poll the asset status with a ceiling, rather than sleeping a fixed time. If your token holds only w_member_social, the versioned GET /rest/images call returns 403 (the Images API says legacy GET calls still work), so poll through the legacy call or wait a bounded time before creating the post.
6. Retry it: 409, 429, 500, 503, 504
LinkedIn's Posts API table tells you to retry 409 CONFLICT ("A write conflict occurred. Retry the request."), 429 TOO_MANY_REQUESTS ("Reduce request frequency and retry after a delay."), 500 and 503, and the generic guide asks for "caching and retry patterns" for 504 timeouts. Use exponential backoff with a cap, and before retrying a post creation after a timeout, check whether the post was actually created, so you do not publish twice.
A classifier you can copy
Branch on status first, then on the message or code. Retry with backoff: 409, 500, 503, 504, plus one delayed retry for an isolated 429. Wait until after midnight UTC: repeated 429. Recreate the media asset: EXPIRED_UPLOAD_URL, MEDIA_ASSET_PROCESSING_FAILED, UPDATING_ASSET_FAILED, and a 401 on an upload URL. Fix the request, do not retry: every other 400, 404, 411 and 422. Upgrade, do not retry: 400 VERSION_MISSING and 426 NONEXISTENT_VERSION. Needs the user, pause the account: 401 on an API call (after one refresh attempt if you hold a refresh token) and 403. Anything in the last bucket should produce a clear message such as "reconnect LinkedIn" or "you need the Content admin role on this page" and stop the queue for that account.
FAQ
Why does LinkedIn return 426 Upgrade Required?
Your Linkedin-Version header names a version that has been sunset. LinkedIn documents the response as NONEXISTENT_VERSION. Change the header to a current version, such as 202610, after checking the changelog for what changed.
What is LinkedIn's API rate limit?
LinkedIn does not publish standard limits. Each endpoint has a daily app-level and member-level quota that resets at midnight UTC, visible in your app's Analytics tab in the Developer Portal after you have called the endpoint at least once that day.
Why do I get 403 when posting to a company page?
Either the token lacks w_organization_social, or the member who authorised it does not hold the ADMINISTRATOR, DIRECT_SPONSORED_CONTENT_POSTER or CONTENT_ADMIN role on that page. Both are required.
Should I still use the ugcPosts API?
LinkedIn says the Posts API replaces ugcPosts and labels the UGC Post page as legacy. The self-serve Share on LinkedIn product still documents /v2/ugcPosts, so the answer depends on which LinkedIn product your app holds.
How does this compare to Meta's errors?
LinkedIn leans on HTTP status codes; Meta puts the meaning in numeric code and error_subcode fields inside the error body. See our Instagram Graph API error codes guide for the Meta side.
How this guide is built
Every status code, error message, limit and date above was read on 11 November 2026 from LinkedIn's developer documentation on Microsoft Learn: the error-handling guide, rate-limit guide, Marketing API versioning page, recent-changes page, updated error-responses page, Posts API, Post API schema, little text format, Images, Videos and Documents APIs, UGC Post API (Legacy), Share on LinkedIn guide, Rest.li protocol versions page, the authorization code flow and the programmatic refresh token guide. The grouping into buckets and the retry advice are ours. We re-check these pages monthly and record any change in this page's changelog.
Comments 0
No comments yet. Be the first to share your thoughts.
Leave a comment