# LinkedIn Posting API: Post to Profiles and Pages

How the LinkedIn posting API works: which product grants which scope, the Posts API request for text, images, video and documents, Page posts, and errors.

Canonical page: https://www.tryadeli.com/blog/linkedin-posting-api
Last updated: 2026-11-02

By Nick Reyes, Co-founder and CTO, Adeli. Published 2026-11-02.

## The short answer

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](https://www.tryadeli.com/blog/linkedin-api-integration).

## 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](https://learn.microsoft.com/en-us/linkedin/marketing/increasing-access) and its [open permissions list](https://learn.microsoft.com/en-us/linkedin/shared/authentication/getting-access). 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](https://www.tryadeli.com/blog/linkedin-community-management-api) 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](https://community.n8n.io/t/linkedin-oauth-unauthorized-scope-w-organization-social-when-connecting-organization-page/127405) (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](https://community.n8n.io/t/persistent-linkedin-unauthorized-scope-error-on-render-instance/140368) (June 2025)

> "Basically, permission w_organization_social is required, but **not available in LinkedIn application settings**."
> Miquel_Colomer, [n8n community forum](https://community.n8n.io/t/linkedin-publish-post-in-organization-page/4467) (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](https://stackoverflow.com/questions/78179707/not-enough-permissions-for-sharesv2-create-despite-having-w-organization-social) (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](https://stackoverflow.com/questions/78755965/error-when-publishing-to-linkedin-via-api-code-422) (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](https://stackoverflow.com/questions/75210404/linkedin-community-management-api-create-an-article-post-with-a-link-preview) (January 2023)

The pattern:

- **Asking for a scope the app doesn't have.** If your OAuth URL requests `w_organization_social` and the app only holds Share on LinkedIn, LinkedIn rejects the whole consent with `unauthorized_scope_error`, even for a personal post. Request only the scopes your products grant.
- **The wrong ID in the author URN.** The `author` has to be `urn:li:person:{id}` built from the member's LinkedIn ID, or `urn:li:organization:{id}`. Your own user ID, or any other identifier, fails validation.
- **Old endpoints.** `/v2/shares` and `/v2/ugcPosts` predate 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`, plus `rw_organization_admin` if you need to look up which Pages the member manages.

The [LinkedIn API integration guide](https://www.tryadeli.com/blog/linkedin-api-integration#step-1-connect-each-user-with-oauth) 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](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/post-api-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](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api) 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:

```bash
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)` inside `commentary`. 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`, or `CONTAINER`.
- **Link previews are not scraped.** For an article post, you set `content.article` with the `source` URL, a `title`, 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](https://dev.to/jnv/linkedins-new-posts-api-the-good-the-bad-and-the-ugly-5e53) (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](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/images-api), [Videos API](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/videos-api), and [Documents API](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/documents-api) pages. Video is the one with extra steps:

1. Call `initializeUpload` with `owner` and `fileSizeBytes`. The response holds a `video` URN and an `uploadInstructions` array with one URL per 4 MB part.
2. `PUT` each byte range to its `uploadUrl` and keep the `ETag` from every response.
3. Call `finalizeUpload` with the `video` URN, the `uploadToken`, and the ETags in part order as `uploadedPartIds`.
4. 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](https://www.tryadeli.com/blog/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 header `X-RestLi-Method: PARTIAL_UPDATE`. Only `commentary`, the call-to-action label, the landing page, `lifecycleState`, and ad fields can change; the media can't. Success is `204`.
- **Delete** with `DELETE /rest/posts/{encoded id}`. It's idempotent: deleting an already deleted post still returns `204`. 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](https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/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](https://www.tryadeli.com/blog/linkedin-api-integration#is-the-linkedin-api-free) 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`:

```bash
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](https://www.tryadeli.com/docs/api/posts/publish-linkedin):

- **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 `201` with the post's `providerId` and `permalink`.
- **Errors.** `403 permission_required` when the member lost their Page role, `429 rate_limited` with 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](https://www.tryadeli.com/social-media-posting-api), and LinkedIn post and Page stats come back through the [analytics API](https://www.tryadeli.com/social-media-analytics-api). [Pricing](https://www.tryadeli.com/pricing) is free to start.

## Frequently asked questions

### Can I post to LinkedIn without the Community Management API?

Yes, for personal profiles. The Share on LinkedIn product is self-serve in the LinkedIn Developer Portal and grants w_member_social, which lets your app post on behalf of the member who authorized it. Posting as a company Page needs w_organization_social, which only vetted products such as the Community Management API grant.

### Does the LinkedIn API support carousel posts?

Not as organic posts. LinkedIn's Posts API documentation lists carousels as sponsored-only. For an organic post with several images, use the MultiImage API, which LinkedIn supports for non-sponsored posts only.

### Why does LinkedIn return 403 ACCESS_DENIED when I create a post?

A 403 ACCESS_DENIED from the Posts API means the token lacks the scope for that author or the member lacks a Page role. A Page post needs w_organization_social and an ADMINISTRATOR, CONTENT_ADMIN or DIRECT_SPONSORED_CONTENT_POSTER role on that Page. A member post needs w_member_social.

## Related Adeli pages

- [Social Media Posting & Scheduling API](https://www.tryadeli.com/social-media-posting-api.md): Adeli's social media posting and scheduling API publishes to Instagram, TikTok, YouTube, Facebook, X, and Bluesky in one request, now or at a set time. Free to start.
- [Social Media Analytics API](https://www.tryadeli.com/social-media-analytics-api.md): Adeli's analytics API: Instagram, TikTok, Facebook Page, and YouTube insights endpoints, plus per-post engagement on every post an account has made.
- [Social Media API for Scheduling and Management Tools](https://www.tryadeli.com/use-cases/social-media-scheduling.md): Build your scheduler on one API for publishing, analytics, comments, and DMs across Instagram, TikTok, YouTube, Facebook, X, and Bluesky.
- [All posts on the Adeli blog](https://www.tryadeli.com/blog.md)

## About the author

Nick Reyes is the CTO behind Adeli and builds its API, its platform integrations, and the docs. He is a product engineer with 10 years of experience at Phantom, Airbnb, and Google.
