LinkedIn API for posting
Post to LinkedIn from code without the Restli homework.
LinkedIn's Posts API wants a versioned header, a protocol header, a person URN and its own text format. Here is all of it, from LinkedIn's docs, and the one-call version.
The short answer · Updated Nov 1, 2026
LinkedIn's API lets an app create posts on a member's behalf through the Posts API (POST https://api.linkedin.com/rest/posts) with the self-serve w_member_social permission, or on a Company Page with w_organization_social, which needs Community Management API approval. Every request needs a Linkedin-Version header in YYYYMM format and X-Restli-Protocol-Version: 2.0.0. Posts can be up to 3,000 characters.
LinkedIn: live. Posting to LinkedIn works on the API today.
| Permission | What it allows | How you get it |
|---|---|---|
| w_member_social | Post, comment and like as the signed-in member | Self-serve: add the Share on LinkedIn product in the Developer Portal |
| openid, profile | Identify the member (gives the id for the person URN) | Self-serve: Sign In with LinkedIn using OpenID Connect |
| w_organization_social | Post, comment and like as a Company Page | Community Management API approval; member must be ADMINISTRATOR, CONTENT_ADMIN or DIRECT_SPONSORED_CONTENT_POSTER |
| r_member_social | Read a member's posts | Closed: LinkedIn isn't accepting requests |
From LinkedIn's Posts API, Share on LinkedIn and Community Management docs on Microsoft Learn, read 2026-11-01.
What can you post with the LinkedIn API?
LinkedIn's Posts API creates organic posts with text, images, video, documents, articles, multi-image sets, polls and celebrations. Carousels are sponsored-only: LinkedIn's docs say organic carousels are not currently supported.
The Posts API replaces the older ugcPosts API. Some LinkedIn pages, including Share on LinkedIn, still show ugcPosts examples, so check which one a tutorial uses before copying it.
Through SocialsForAI you can post text and up to 20 images to personal profiles today. Video, documents, polls and Company Pages aren't supported yet.
How do you get a LinkedIn API access token?
LinkedIn uses a standard 3-legged OAuth flow: send the member to https://www.linkedin.com/oauth/v2/authorization with your scopes, receive a code on your redirect URL, and exchange it at https://www.linkedin.com/oauth/v2/accessToken. The authorization code lives 30 minutes.
Access tokens currently last 60 days. Programmatic refresh tokens are only available to a limited set of partners; everyone else sends the member back through the authorization flow, which skips the consent screen if they're still logged in and the old token hasn't expired.
Two gotchas from LinkedIn's OAuth page: requesting a different set of scopes invalidates the member's previous tokens, and you should store tokens with room for at least 1,000 characters even though they are about 500 today.
What does a raw LinkedIn post request look like?
A text post is a JSON body with author (a urn:li:person:{id} URN), commentary, visibility, a distribution object and lifecycleState: PUBLISHED. The person id is the sub field from the OpenID Connect userinfo endpoint (GET https://api.linkedin.com/v2/userinfo).
LinkedIn answers 201 and puts the post id, such as urn:li:share:6844785523593134080, in the x-restli-id response header rather than the body, so a client that only parses the body misses it.
curl -X POST https://api.linkedin.com/rest/posts \
-H "Authorization: Bearer $LINKEDIN_TOKEN" \
-H "Linkedin-Version: 202610" \
-H "X-Restli-Protocol-Version: 2.0.0" \
-H "Content-Type: application/json" \
-d '{
"author": "urn:li:person:782bbtaQ",
"commentary": "Shipped webhooks today \\(finally\\).",
"visibility": "PUBLIC",
"distribution": {
"feedDistribution": "MAIN_FEED",
"targetEntities": [],
"thirdPartyDistributionChannels": []
},
"lifecycleState": "PUBLISHED",
"isReshareDisabledByAuthor": false
}'
# 201 Created, header x-restli-id: urn:li:share:...Why does LinkedIn drop parentheses and @ signs?
The commentary field uses LinkedIn's "little text" format, where ( ) [ ] { } < > @ | * _ ~ \ are reserved. A mention is written @[Name](urn:li:organization:123), so a stray parenthesis or @ can be read as markup and the text around it lost or rejected. Escape each reserved character with a backslash.
We escape them all automatically and leave hashtags intact. The trade-off: because @ is escaped, LinkedIn @mentions aren't supported through our API yet. Preview how a post will look with the LinkedIn text formatter, and check length against the LinkedIn character limit.
How do images work in the LinkedIn API?
Images are a separate two-step upload. Call POST /rest/images?action=initializeUpload with the owner URN, upload the bytes to the returned uploadUrl, then reference the returned urn:li:image:... in the post's content.media.id.
The Images API accepts JPG, GIF and PNG under 36,152,320 pixels, and GIFs up to 250 frames. Several images in one organic post use the MultiImage format. A token with only w_member_social can upload but can't GET the image back on the versioned API.
POST https://api.linkedin.com/rest/images?action=initializeUpload
{ "initializeUploadRequest": { "owner": "urn:li:person:782bbtaQ" } }
200 →
{
"value": {
"uploadUrlExpiresAt": 1650567510704,
"uploadUrl": "https://www.linkedin.com/dms-uploads/...",
"image": "urn:li:image:C4E10AQFoyyAjHPMQuQ"
}
}What errors does the LinkedIn Posts API return?
LinkedIn documents its Posts API errors by HTTP status and message. The ones you hit in practice, and what each becomes when you post through us:
- 400
FIELD_LENGTH_TOO_LONG: commentary over the limit. We catch it first astext_too_longat 3,000 characters. - 400
MISSING_FIELDorINVALID_URN_TYPE: a malformed body or the wrong URN inauthor. We build the body, so you shouldn't see these. - 401
EMPTY_ACCESS_TOKENor an expired token: becomesauth_expired; we try one refresh, and without a refresh token the account moves toneeds_reauth. - 403
ACCESS_DENIED: the scope or the Company Page role is missing. - 429
TOO_MANY_REQUESTS: becomesplatform_rate_limitedand retries on our backoff schedule. - 500 and 503: become
platform_unavailableand retry.
What are the LinkedIn API rate limits?
LinkedIn doesn't publish standard rate limits. They are per day, reset at midnight UTC, and come in two kinds: per application and per member per application. You see your app's limits in the Developer Portal's Analytics tab, only for endpoints you've called at least once that day.
Two published numbers exist. The Share on LinkedIn page lists 150 requests per member and 100,000 per application per day. The Community Management API's development tier allows 500 requests per app and 100 per member. Over a limit, LinkedIn returns 429.
Separately, LinkedIn Help says it may limit how many posts a member or Page makes in a period, without giving a number. Our own cap is 50 posts per LinkedIn account per 24 hours.
What does the Linkedin-Version header do?
Linkedin-Version pins the monthly API version your request is written against, in YYYYMM format. LinkedIn retires old versions: the November 2025 version (202511) sunsets on November 16, 2026, per the deprecation notice on the Posts API page.
If you call LinkedIn directly, keep the version in one config value and schedule a check before each sunset. We send the header on every request and keep the version in one place on our side.
How do you post to LinkedIn with one call?
Send plain text and public image URLs to POST /v1/posts. We escape reserved characters, upload images through the Images API, add both headers, read the post id from x-restli-id and return the post URL.
When a 60-day token expires, the next publish attempt fails with account_needs_reauth, the account flips to needs_reauth and an account.needs_reauth webhook fires, so you can send the owner a fresh connect link before the next scheduled post. To plan posts ahead, see schedule LinkedIn posts; for agents, the LinkedIn MCP server.
curl https://socialsforai.vercel.app/v1/posts \
-H "Authorization: Bearer $SOCIALS_API_KEY" \
-H "Idempotency-Key: launch-0412" \
-d '{
"accounts": ["acc_bluesky", "acc_linkedin", "acc_x"],
"text": "We just shipped webhooks.",
"wait": true
}'Posting to LinkedIn with the raw API
- 01
Create a developer app
Create an app in the LinkedIn Developer Portal and add an absolute HTTPS redirect URL on the Auth tab. Note the client id and secret.
- 02
Add two products
On the Products tab add Share on LinkedIn (grants w_member_social) and Sign In with LinkedIn using OpenID Connect (grants openid and profile).
- 03
Run OAuth
Send the member to the authorization URL with scope openid profile w_member_social, then exchange the code within 30 minutes for a 60-day access token.
- 04
Build the person URN
Call the userinfo endpoint with the token and prefix its sub value: urn:li:person:{sub}.
- 05
Create the post
POST to /rest/posts with the Linkedin-Version and X-Restli-Protocol-Version headers, escaped commentary, and read the post id from the x-restli-id header.
Questions
What developers ask
Is the LinkedIn API free?
LinkedIn doesn't charge per call for posting. Member posting through Share on LinkedIn is self-serve and free. The cost is access: Company Page posting needs Community Management API approval, reading member posts is closed, and tokens expire every 60 days without partner-level refresh tokens.
How do I get LinkedIn API access for posting?
Create an app in the LinkedIn Developer Portal, open the Products tab and add Share on LinkedIn, which grants w_member_social, plus Sign In with LinkedIn using OpenID Connect to identify the member. Both are self-serve. Company Page posting needs a separate Community Management API application.
Can I post to a LinkedIn Company Page with the API?
Yes, with w_organization_social, when the authenticated member is an administrator, content admin or direct sponsored content poster of the page and the app has Community Management API access, which LinkedIn vets in two tiers. Our API posts to personal profiles only for now.
How long is a LinkedIn post allowed to be?
Up to 3,000 characters, per LinkedIn Help. Over the limit, the Posts API returns 400 FIELD_LENGTH_TOO_LONG. Our preflight check rejects longer text with text_too_long before anything is sent, so a too-long post never reaches LinkedIn and you can fix it in the same request cycle.
Why did my LinkedIn post lose its parentheses?
Commentary uses LinkedIn's little text format, where parentheses, brackets, braces, angle brackets, @, pipe, asterisk, underscore, tilde and backslash are reserved and must be escaped with a backslash. Unescaped, LinkedIn can treat them as markup. Our API escapes them for you.
How long do LinkedIn API access tokens last?
LinkedIn currently issues access tokens with a 60-day lifespan. Programmatic refresh tokens are limited to approved partners. Without one, the member goes through the authorization flow again; if they're still logged in and the token is valid, LinkedIn skips the consent screen.
What is the LinkedIn API rate limit for posting?
LinkedIn doesn't publish standard limits; you see your app's limits in the Developer Portal Analytics tab. The Share on LinkedIn page lists 150 requests per member and 100,000 per app per day. Limits reset at midnight UTC and over-limit calls return 429.
Get an API key and send your first post in five minutes.