Skip to content
SocialsForAI
Menu

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.

LinkedIn posting permissions
PermissionWhat it allowsHow you get it
w_member_socialPost, comment and like as the signed-in memberSelf-serve: add the Share on LinkedIn product in the Developer Portal
openid, profileIdentify the member (gives the id for the person URN)Self-serve: Sign In with LinkedIn using OpenID Connect
w_organization_socialPost, comment and like as a Company PageCommunity Management API approval; member must be ADMINISTRATOR, CONTENT_ADMIN or DIRECT_SPONSORED_CONTENT_POSTER
r_member_socialRead a member's postsClosed: 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.

bash
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.

json
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 as text_too_long at 3,000 characters.
  • 400 MISSING_FIELD or INVALID_URN_TYPE: a malformed body or the wrong URN in author. We build the body, so you shouldn't see these.
  • 401 EMPTY_ACCESS_TOKEN or an expired token: becomes auth_expired; we try one refresh, and without a refresh token the account moves to needs_reauth.
  • 403 ACCESS_DENIED: the scope or the Company Page role is missing.
  • 429 TOO_MANY_REQUESTS: becomes platform_rate_limited and retries on our backoff schedule.
  • 500 and 503: become platform_unavailable and 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.

bash
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

  1. 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.

  2. 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).

  3. 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.

  4. 04

    Build the person URN

    Call the userinfo endpoint with the token and prefix its sub value: urn:li:person:{sub}.

  5. 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.