LinkedIn Posting API: Post to Profiles and Pages
The LinkedIn posting API is the Posts API: one POST to /rest/posts with an author URN, the post text, and a Linkedin-Version header. Personal profiles need the w_member_social scope, which the self-serve Share on LinkedIn product grants. Company Pages need w_organization_social from a vetted product, usually the Community Management API. Adeli publishes LinkedIn text and image posts through one social media API.
The LinkedIn posting API is LinkedIn's Posts API: a single POST /rest/posts call with an author URN, the post's commentary, and a Linkedin-Version header. The request itself is short. What trips people up is the LinkedIn product behind the app, because that decides whether you can post as a person, as a company Page, or not at all.
Most failed first attempts come from a scope the app was never granted. This guide walks through which product gives which scope, then the requests for each kind of post. For OAuth, token lifetimes, and the full app setup, see our LinkedIn API integration guide.
Which LinkedIn API do you use to post?
Every LinkedIn post created through the API goes through the Posts API; what changes is the scope your token carries. LinkedIn grants posting scopes through three developer products, and only one of them is self-serve:
| LinkedIn product | Posting scope it grants | Post as | How you get it |
|---|---|---|---|
| Share on LinkedIn | w_member_social |
The member who authorized your app | Self-serve, from the app's Products tab |
| Community Management API | w_member_social, w_organization_social |
A member or a company Page | Vetted, with Development and Standard tiers |
| Advertising API | w_member_social, w_organization_social |
A member or a company Page | Vetted, for advertising use cases |
The scope lists come from LinkedIn's permissions table and its open permissions list. Pick the product by use case. LinkedIn reviews each request against what the product is for, and a scheduling or publishing tool belongs on the Community Management API. Our Community Management API explainer covers that application and its tiers.
Common mistakes when posting through the LinkedIn API
The same few errors show up again and again in developer forums. Most of them trace back to the scope table above.
"The personal account connection works fine. But when I try to connect the organization page using OAuth2, I get the following error …" Djagryn, n8n community forum (June 2025)
"I consistently get an unauthorized_scope_error related to w_organization_social, even though I am trying to post as a Person." celso_cunha, n8n community forum (June 2025)
"Basically, permission w_organization_social is required, but not available in LinkedIn application settings." Miquel_Colomer, n8n community forum (February 2021)
"The API call is consistently failing with a permission error, even though I have an authorised connection with what I understand to be the required permissions for the attempted operation." Kedama, Stack Overflow (March 2024)
"ERROR :: /author :: "urn:li:person:aba2390d-7792-4cb2-ad6b-e116d9783606" does not match urn:li:company:\d+|urn:li:member:\d+" usrmetamask, Stack Overflow (July 2024)
"Recently our app was able to create LinkedIn posts via UGC Posts API and the URL preview functionality worked perfectly … But after we migrated to the new API it seems that it is not possible anymore" AndrewK, Stack Overflow (January 2023)
The pattern:
- Asking for a scope the app doesn't have. If your OAuth URL requests
w_organization_socialand the app only holds Share on LinkedIn, LinkedIn rejects the whole consent withunauthorized_scope_error, even for a personal post. Request only the scopes your products grant. - The wrong ID in the author URN. The
authorhas to beurn:li:person:{id}built from the member's LinkedIn ID, orurn:li:organization:{id}. Your own user ID, or any other identifier, fails validation. - Old endpoints.
/v2/sharesand/v2/ugcPostspredate the versioned Posts API, so tutorials built on them behave differently. Step 3 covers the link-preview change.
Step 1: Get a token with the right scope
A LinkedIn post needs a three-legged OAuth token from the member who will be the author, or from an admin of the Page that will be the author. There are no app-only tokens for posting.
- Personal profile: request
w_member_social. - Company Page: request
w_organization_social, plusrw_organization_adminif you need to look up which Pages the member manages.
The LinkedIn API integration guide has the authorization URL and the code exchange.
Step 2: Build the author URN
The author field takes a person URN (urn:li:person:{id}) or an organization URN (urn:li:organization:{id}), and the token's scopes must match it. LinkedIn's Post schema says to get the person ID from the Retrieve Current Member's Profile API, and the organization ID from the member's organization access list.
For a Page, the member also needs a role there. The Posts API reference restricts w_organization_social to members who are ADMINISTRATOR, DIRECT_SPONSORED_CONTENT_POSTER, or CONTENT_ADMIN on the Page. A valid token from a member with no role still gets a 403.
Step 3: Create the post
Send POST https://api.linkedin.com/rest/posts with Linkedin-Version and X-Restli-Protocol-Version: 2.0.0 headers. This one posts as a member, mentions an organization, and adds a hashtag:
curl -X POST https://api.linkedin.com/rest/posts \
-H "Authorization: Bearer <MEMBER_TOKEN>" \
-H "Linkedin-Version: 202610" \
-H "X-Restli-Protocol-Version: 2.0.0" \
-H "Content-Type: application/json" \
-d '{
"author": "urn:li:person:<PERSON_ID>",
"commentary": "Shipped our new release with @[Devtestco](urn:li:organization:2414183) #buildinpublic",
"visibility": "PUBLIC",
"distribution": {
"feedDistribution": "MAIN_FEED",
"targetEntities": [],
"thirdPartyDistributionChannels": []
},
"lifecycleState": "PUBLISHED",
"isReshareDisabledByAuthor": false
}'Details from the Posts API reference:
- Mentions use
@[Name](urn)insidecommentary. The name has to match the entity's name, case-sensitive, or it renders as plain text. A member mention can match the first name, last name, or full name. An organization mention must match the full name. - Visibility accepts
PUBLIC,CONNECTIONS,LOGGED_IN, orCONTAINER. - Link previews are not scraped. For an article post, you set
content.articlewith thesourceURL, atitle, and a thumbnail image you uploaded yourself. A bare URL in the text stays a bare URL. That is the change AndrewK hit above.
Jan Vlnas, a developer who migrated an integration from ugcPosts to the versioned API, summed up the payload change:
"the new Posts API is definitely an overall improvement. Deeply nested structures with confusing properties and arbitrary nesting of arrays – it's all gone." Jan Vlnas, LinkedIn's New Posts API: The Good, The Bad, and The Ugly (April 2023)
So moving off ugcPosts means rewriting the payload as well as the URL. specificContent, shareCommentary, and shareMediaCategory collapse into a flat commentary and content.
Step 4: Add an image, video, or document
Media goes up first through its own API, which returns a URN; the post then references that URN in content.media.id. Each upload names the same owner as the post's author.
| Content | Upload API | What LinkedIn accepts |
|---|---|---|
| Image | POST /rest/images?action=initializeUpload |
JPG, GIF, or PNG under 36,152,320 pixels; GIFs up to 250 frames |
| Video | POST /rest/videos?action=initializeUpload, then finalizeUpload |
MP4, 3 seconds to 30 minutes, 75 KB to 500 MB |
| Document | POST /rest/documents?action=initializeUpload |
PDF, PPT, PPTX, DOC, or DOCX, up to 100 MB and 300 pages |
| Several images | Images API, then a multiImage post |
Organic posts only |
The limits are from LinkedIn's Images API, Videos API, and Documents API pages. Video is the one with extra steps:
- Call
initializeUploadwithownerandfileSizeBytes. The response holds avideoURN and anuploadInstructionsarray with one URL per 4 MB part. PUTeach byte range to itsuploadUrland keep theETagfrom every response.- Call
finalizeUploadwith thevideoURN, theuploadToken, and the ETags in part order asuploadedPartIds. - Create the post with
"content": { "media": { "id": "urn:li:video:…", "title": "…" } }.
A document post needs content.media.title, because the Documents API makes it required.
How do you know the post worked?
LinkedIn returns HTTP 201 Created and puts the new post's ID in the x-restli-id response header, as urn:li:share:… or urn:li:ugcPost:…. There's no response body to parse. Save the ID: you need it to edit, delete, or read analytics later, and it builds the public link https://www.linkedin.com/feed/update/<post-id>/. To read how those posts perform, see the LinkedIn post analytics API.
The errors you'll see most, from the Posts API's error table:
| Status | Code | Usual cause |
|---|---|---|
400 |
MISSING_FIELD |
distribution, visibility, or lifecycleState left out |
400 |
FIELD_LENGTH_TOO_LONG |
commentary or another text field over its limit |
401 |
EMPTY_ACCESS_TOKEN |
No bearer token on the request |
403 |
ACCESS_DENIED |
Missing scope, or no Page role for an organization author |
422 |
UNPROCESSABLE_ENTITY |
Values that don't fit together |
429 |
TOO_MANY_REQUESTS |
A daily rate limit was hit |
Don't retry a create blindly. A timeout after LinkedIn accepted the post means a retry publishes it twice. Check the author's recent posts first.
Can you schedule or edit LinkedIn posts through the API?
The LinkedIn Posts API has no scheduling field: PUBLISHED is the only lifecycleState it accepts on create. To schedule, store the post in your own system and send the create call at the planned time.
After publishing:
- Edit with
POST /rest/posts/{encoded id}and the headerX-RestLi-Method: PARTIAL_UPDATE. Onlycommentary, the call-to-action label, the landing page,lifecycleState, and ad fields can change; the media can't. Success is204. - Delete with
DELETE /rest/posts/{encoded id}. It's idempotent: deleting an already deleted post still returns204. There's no batch delete.
Is the LinkedIn posting API free?
LinkedIn charges no fee for the Posts API; you pay in daily call limits that depend on the product. The Share on LinkedIn page lists 150 requests per member and 100,000 per app each day. The Community Management API's Development tier allows 500 calls per app and 100 per member in 24 hours, and Standard tier lifts those caps. Access, tokens, and upkeep are the real costs, and the integration guide breaks them down.
How do you post to LinkedIn through Adeli?
Adeli publishes LinkedIn text posts and single-image posts to a member's personal profile or a Page they administer, with the same request shape as every other network. Adeli holds the Community Management API access, so your app doesn't need its own LinkedIn product approval. Each of your users connects their LinkedIn account once, and you publish with their accountId:
curl -X POST https://app.tryadeli.com/api/v1/posts \
-H "Authorization: Bearer $ADELI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "linkedin",
"accountId": "<ACCOUNT_ID>",
"commentary": "Our spring schedule is live.",
"image": { "content_type": "image/png", "base64": "<BASE64>" }
}'What that covers, per the LinkedIn publish docs:
- Author. The connected account is either the profile or one Page, chosen on an Adeli-hosted picker when the member administers a Page. You never build URNs.
- Media. One kind per post: one image, 2 to 20 images, one MP4 video, or one PDF document. You upload through Adeli's media uploads endpoint and pass the
upload_id, and Adeli runs LinkedIn's upload flow and waits for a video to finish processing before it creates the post. - Mentions. Up to 20 mentions of Pages or people, by URN and the name's position in the commentary.
- Result. A
201with the post'sproviderIdandpermalink. - Errors.
403 permission_requiredwhen the member lost their Page role,429 rate_limitedwith a retry delay, and no automatic retry of a create, so a post never lands twice.
The same key publishes to Instagram, TikTok, YouTube, X, and Bluesky through the posting API, and LinkedIn post and Page stats come back through the analytics API. Pricing is free to start.