Case StudyJuly 27, 20269 min readingMarcel Czuryszkiewicz

How to Post to LinkedIn via API: Profiles, Company Pages and Media

Posting to LinkedIn via API involves much more than sending text. This guide covers the versioned Posts API, w_member_social vs w_organization_social permissions, handling media URNs, link previews, and building a reliable scheduling queue.

Posting to LinkedIn through an API looks straightforward in a demo:

  1. get an access token,
  2. send text to an endpoint,
  3. receive a post ID.

That flow is real. It is also about 20% of the work required for a production integration.

The remaining 80% is permissions, company page roles, author URNs, media registration, upload state, API version headers, scheduling, token handling, error recovery, and explaining to customers why they can see a Page in LinkedIn but cannot publish to it through your application.

This guide explains the current LinkedIn posting workflow for personal profiles and company pages, shows where the implementation becomes annoying, and demonstrates a simpler route through bundle.social.

TL;DR: LinkedIn's current versioned Posts API uses POST https://api.linkedin.com/rest/posts. Personal publishing generally requires w_member_social. Company page publishing requires w_organization_social and an eligible Page role. Images, videos, and documents must be uploaded first and referenced by their LinkedIn URNs. LinkedIn does not become a scheduler just because you can create a post; your application still needs a queue and reliable worker system.

Which LinkedIn API should you use?

LinkedIn currently documents the versioned Posts API as the main API for creating and retrieving organic and sponsored posts:

POST https://api.linkedin.com/rest/posts

  LinkedIn also states that the Posts API replaces the older ugcPosts API for current Marketing API integrations.

You will still find many tutorials built around:

POST https://api.linkedin.com/v2/ugcPosts

  Those tutorials can help explain the old data model, but new production work should follow LinkedIn's latest documentation and supported API version.

Every versioned request needs these headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
 Linkedin-Version: 202607
 X-Restli-Protocol-Version: 2.0.0
 Content-Type: application/json

  The exact supported version changes over time. LinkedIn releases Marketing API versions monthly and supports them for a limited lifecycle. Hard-coding a version and forgetting about it is a reliable way to create a future incident.

Personal profiles and company pages are different authors

A LinkedIn post has an author.

For a personal profile, the author is a person URN:

urn:li:person:PERSON_ID

For a company page, the author is an organization URN:

urn:li:organization:ORGANIZATION_ID

Your UI should make this difference explicit. “Connect LinkedIn” is not enough information for users managing both their own profile and several company pages.

A personal profile connection answers:

Which member is publishing?

A company page connection must also answer:

Which organization is publishing, and does this member have a role that allows it?

If your data model stores only one generic LinkedIn account ID, you will eventually have trouble with page selection, permissions, analytics, and reconnection.

Permissions required for LinkedIn posting

Personal profile posting

Publishing on behalf of the authenticated member generally requires:

w_member_social

  LinkedIn provides this through the Share on LinkedIn product.

The permission allows the application to post, comment, and react on behalf of the authenticated member. It does not automatically grant broad read access to the member's entire content history. Some read permissions are restricted and require approval.

Company page posting

Publishing on behalf of an organization requires:

w_organization_social

  The authenticated member must also hold an eligible role on that Page. LinkedIn currently lists roles such as:

  • ADMINISTRATOR,
  • DIRECT_SPONSORED_CONTENT_POSTER,
  • CONTENT_ADMIN.

A valid token is not enough if the person lacks the required organization role.

This distinction causes many support tickets. Customers say, “I am connected to LinkedIn, so why can I not post to the Page?” The answer is often that OAuth succeeded for the member, but organization access is missing or insufficient.

Creating a basic text post

A direct organization text post can look like this:

const response = await fetch("https://api.linkedin.com/rest/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LINKEDIN_ACCESS_TOKEN}`,
    "Linkedin-Version": "202607",
    "X-Restli-Protocol-Version": "2.0.0",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    author: "urn:li:organization:123456789",
    commentary: "We just shipped a cleaner reporting workflow.",
    visibility: "PUBLIC",
    distribution: {
      feedDistribution: "MAIN_FEED",
      targetEntities: [],
      thirdPartyDistributionChannels: [],
    },
    lifecycleState: "PUBLISHED",
    isReshareDisabledByAuthor: false,
  }),
});

if (!response.ok) {
  const errorBody = await response.text();
  throw new Error(`LinkedIn post failed: ${response.status} ${errorBody}`);
}

const postUrn = response.headers.get("x-restli-id");

  The request is not especially complicated. Correctly obtaining the author, permission, Page role, token, and version is the bigger job.

Images, videos and documents require separate uploads

LinkedIn does not accept a random public image URL inside the post request and handle everything for you.

For rich media, the general flow is:

  1. initialize or register an upload,
  2. upload the binary file,
  3. wait for or verify the asset state where required,
  4. receive an asset URN,
  5. reference that URN in the post.

LinkedIn uses different media identifiers:

urn:li:image:...
 urn:li:video:...
 urn:li:document:...

  A video post references a video URN. A document post references a document URN. An image post references an image URN.

This means your application needs a media state machine in addition to a post state machine. A file can fail before the post is created. A video can upload but still be processing. The final post request can fail even after the media upload succeeds.

You need to store enough context to distinguish these failures. Returning “LinkedIn post failed” for all of them will make your users hate the integration and your support team hate you.

Supported LinkedIn post types

The current Posts API documentation includes organic support for:

  • text,
  • images,
  • videos,
  • documents,
  • articles,
  • multi-image posts,
  • polls,
  • celebration posts.

There are important exceptions. For example, organic carousel posts are not supported in the same way as sponsored carousels. LinkedIn's terminology can also be confusing because a multi-image organic post and an advertising carousel are different products.

Do not expose every content type in one generic form and hope LinkedIn accepts it. Build platform-aware validation.

Link previews are not just URL scraping

Older social integrations often paste a URL and wait for the platform to scrape a title, description, and image.

LinkedIn's current Posts API documentation says article post creation does not support URL scraping through the API because the final appearance would be unpredictable. API partners should provide article fields such as:

  • source URL,
  • title,
  • description,
  • thumbnail image URN.

This gives you more control, but it also means your backend may need to fetch Open Graph metadata, let the user edit it, upload a thumbnail, and construct the article object.

Again, the final post request is the easy part.

LinkedIn scheduling is your responsibility

The Posts API publishes posts. It does not remove the need for your own scheduling infrastructure.

To schedule a post for next Tuesday, your system normally needs to:

  1. store the draft and intended publication time,
  2. validate the content before the deadline,
  3. enqueue a job,
  4. run the job at the correct time zone-aware moment,
  5. confirm the token and Page are still valid,
  6. upload or reference media,
  7. call LinkedIn,
  8. store the resulting post URN,
  9. retry only when the failure is safe to retry,
  10. notify the user if publication ultimately fails.

A cron expression plus one database row is fine for a prototype. It becomes risky when thousands of customer posts are scheduled for the same hour.

Common production failures

The member is connected but the Page is missing

The member may not have an eligible Page role, the application may lack organization permissions, or your integration may not be fetching and presenting available organizations correctly.

OAuth succeeded but publishing returns 403

Check the token scopes, organization role, requested product access, and whether the endpoint belongs to an approval-gated program.

Media uploaded but the post fails

Store the media URN and upload state separately. Validate title, description, content type, and ownership. Do not automatically upload the same large video again unless necessary. Read on errors here.

An API version suddenly stops working

LinkedIn versions Marketing APIs monthly and sunsets older versions. Maintain a version upgrade process and monitor LinkedIn's migration notices.

Link preview looks different from the website

Do not assume native URL scraping behavior. Build the article payload explicitly when using the current Posts API.

Posting through bundle.social

bundle.social handles the native LinkedIn OAuth, token storage, company page selection, media workflow, scheduling, queueing, and status tracking behind one social media API.

A scheduled LinkedIn post can be created like this:

const response = await fetch("https://api.bundle.social/api/v1/post", {
  method: "POST",
  headers: {
    "x-api-key": process.env.BUNDLE_SOCIAL_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    teamId: "team_123",
    title: "LinkedIn launch announcement",
    postDate: "2026-08-18T09:00:00.000Z",
    status: "SCHEDULED",
    socialAccountTypes: ["LINKEDIN"],
    data: {
      LINKEDIN: {
        text: "We just shipped a cleaner reporting workflow.",
        uploadIds: ["upload_123"],
      },
    },
  }),
});

if (!response.ok) {
  const body = await response.text();
  throw new Error(`Could not schedule post: ${response.status} ${body}`);
}

  The same endpoint can schedule other supported platforms by adding their platform-specific data blocks.

Your application still needs a customer-facing product and sensible validation. It does not need to rebuild every LinkedIn media upload, token, Page selection, queue, and retry detail.

Connecting LinkedIn accounts for SaaS users

For a multi-tenant application, bundle.social can generate a hosted account connection link:

POST /api/v1/social-account/create-portal-link

  The request identifies the team, redirect URL, and allowed platform types. Your user completes the OAuth flow in a hosted page and returns to your application.

LinkedIn company pages require channel selection after the initial connection. This is important when one member manages several organizations. Store the selected Page in the correct customer team rather than treating every Page under the OAuth identity as one account.

Direct API or unified API?

Build directly when LinkedIn is the core of your product, you need native features not exposed by a provider, and your team is prepared to maintain product access, media workflows, and monthly API versions.

Use a unified API when LinkedIn is one of several requested platforms, customers connect their own accounts, and you need scheduling, retries, consistent post statuses, and one account model across the product.

Before launch, test personal profiles, members managing several Pages, insufficient Page roles, revoked tokens, media processing failures, duplicate retry protection, time zones, and version migration. Happy-path text posts are not a production test.

Frequently asked questions

Can I post to a personal LinkedIn profile through the API?

Yes, with the appropriate member authorization and w_member_social permission.

Can I post to a LinkedIn company page?

Yes, but the application needs organization publishing access and the authenticated member must hold an eligible Page role.

Can I upload PDFs?

LinkedIn supports document posts. The file must go through the document asset workflow and the resulting document URN is referenced in the post.

Can I schedule a LinkedIn post through the native API?

You can build scheduling around the publishing API, but your application owns the queue, timing, retries, and status workflow. A provider such as bundle.social can supply that infrastructure.