Executive Summary

LinkedIn offers official REST APIs for programmatic posting, similar to Twitter/X. The Posts API (recommended) replaced the legacy UGC/Shares APIs in 2022. Key requirements: OAuth 2.0 authentication, w_member_social permission, and versioned API headers. Access tokens are valid for 60 days.

Comparison: LinkedIn API vs Twitter/X API

FeatureLinkedIn Posts APITwitter/X API
AuthenticationOAuth 2.0 (60-day tokens)OAuth 2.0 (configurable)
Token RefreshLimited partner accessSupported for all
Text Limit3,000 characters280 chars (free), 25K (Premium)
Media SupportImages, videos, documents, articlesImages, videos, GIFs
Rate LimitsDaily app limit (varies)Per-endpoint limits
API VersioningYYYYMM header requiredNot required
Base URLhttps://api.linkedin.com/rest/https://api.twitter.com/2/
Official Node.js Clientâś… linkedin-api-clientâś… twitter-api-v2

API Endpoint

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

Required Headers

Authorization: Bearer {ACCESS_TOKEN}
X-Restli-Protocol-Version: 2.0.0
LinkedIn-Version: 202511
Content-Type: application/json

Important: LinkedIn-Version must be in YYYYMM format (e.g., 202511 for November 2025).

Success Response

HTTP/1.1 201 Created
x-restli-id: urn:li:share:6844785523593134080

The x-restli-id header contains the Post ID.

API Versioning

DateBase PathStatus
June 2022+https://api.linkedin.com/rest/âś… Current (versioned)
Pre-2022https://api.linkedin.com/v2/❌ Sunset

Support Window: Versioned APIs are stable for minimum 1 year before sunset.

OAuth 2.0 Authentication

Setup Steps

1. Create LinkedIn Developer Application

2. Request Required Permissions

  • w_member_social - Post on behalf of a member (required)
  • Share On LinkedIn product
  • Login With LinkedIn product

3. Obtain Access Token

LinkedIn uses OAuth 2.0 authorization code flow:

Terminal window
# Step 1: Redirect user to LinkedIn authorization URL
https://www.linkedin.com/oauth/v2/authorization?
response_type=code&
client_id={CLIENT_ID}&
redirect_uri={REDIRECT_URI}&
scope=w_member_social
# Step 2: Exchange authorization code for access token
POST https://www.linkedin.com/oauth/v2/accessToken
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code={AUTHORIZATION_CODE}&
client_id={CLIENT_ID}&
client_secret={CLIENT_SECRET}&
redirect_uri={REDIRECT_URI}

Response:

{
"access_token": "AQV8...",
"expires_in": 5184000,
"scope": "w_member_social"
}

Token Validity

Token TypeValidityRefresh Support
Access Token60 days⚠️ Limited (partner-only)
Refresh TokenN/A⚠️ Case-by-case basis

Key Limitation: Unlike Twitter/X, LinkedIn does not provide refresh tokens for most developers. You can post programmatically for up to 60 days before requiring manual re-authentication.

Creating Posts

Text Post

Terminal window
POST https://api.linkedin.com/rest/posts
Authorization: Bearer {ACCESS_TOKEN}
X-Restli-Protocol-Version: 2.0.0
LinkedIn-Version: 202511
Content-Type: application/json
{
"author": "urn:li:person:{PERSON_ID}",
"commentary": "Just shipped a new feature! 🚀",
"visibility": "PUBLIC",
"distribution": {
"feedDistribution": "MAIN_FEED",
"targetEntities": [],
"thirdPartyDistributionChannels": []
},
"lifecycleState": "PUBLISHED",
"isReshareDisabledByAuthor": false
}

Text Limit: Maximum 3,000 characters (vs Twitter’s 280).

Post with Image

Step 1: Upload image to get Image URN

Use Images API to upload and obtain urn:li:image:{id}.

Step 2: Create post with image

{
"author": "urn:li:person:{PERSON_ID}",
"commentary": "Check out this visualization!",
"visibility": "PUBLIC",
"content": {
"media": {
"id": "urn:li:image:123456789"
}
},
"distribution": {
"feedDistribution": "MAIN_FEED"
},
"lifecycleState": "PUBLISHED"
}

Post with Video

  1. Upload video via Videos API → get urn:li:video:{id}
  2. Use video URN in post creation

Post with Document

  1. Upload document via Documents API → get urn:li:document:{id}
  2. Use document URN in post creation

Article Post (Link Preview)

Important: Posts API does NOT support URL scraping for link previews.

You must manually set:

  • Thumbnail image (upload via Images API first)
  • Article title
  • Article description
{
"author": "urn:li:person:{PERSON_ID}",
"commentary": "New blog post on LinkedIn automation",
"visibility": "PUBLIC",
"content": {
"article": {
"source": "https://example.com/blog/linkedin-automation",
"thumbnail": "urn:li:image:123456789",
"title": "Building LinkedIn Automation in 2025",
"description": "A comprehensive guide to LinkedIn's REST APIs"
}
},
"distribution": {
"feedDistribution": "MAIN_FEED"
},
"lifecycleState": "PUBLISHED"
}
Post TypeAPI
Organic postsMultiImage API
Sponsored postsCarousel API

Mentions/Tags

Tag LinkedIn entities (members, organizations) in posts:

{
"commentary": "Great collaboration with @CompanyName!",
"content": {
"contentEntities": [
{
"entityLocation": "https://www.linkedin.com/company/123456",
"entity": "urn:li:organization:123456"
}
]
}
}

Node.js Implementation

Official Client Library

Terminal window
npm install linkedin-api-client

GitHub: linkedin-api-client

Built with Axios and TypeScript.

Basic Usage

import { RestliClient } from 'linkedin-api-client';
// Initialize client
const client = new RestliClient();
// Get user ID
const userInfo = await client.get({
resourcePath: '/userinfo',
accessToken: ACCESS_TOKEN
});
const personId = userInfo.sub;
// Create post
const post = await client.create({
resourcePath: '/posts',
accessToken: ACCESS_TOKEN,
entity: {
author: `urn:li:person:${personId}`,
commentary: 'Hello LinkedIn API!',
visibility: 'PUBLIC',
distribution: {
feedDistribution: 'MAIN_FEED'
},
lifecycleState: 'PUBLISHED'
}
});
console.log('Post ID:', post.headers['x-restli-id']);

Community Libraries

LibraryDescription
gfiocco/linkedin-node-apiShare on LinkedIn with Node.js
chris287/linkedin-share-api-v2Complete flow: OAuth → Image upload → Post creation

Legacy APIs (Deprecated)

UGC Post API

Status: Legacy, migrate to Posts API

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

Why it’s deprecated:

  • Part of unversioned API (/v2/)
  • Overlapping features with Shares API
  • No long-term support guarantee

Migration Path: Use Posts API (/rest/posts) instead.

Shares API

Status: Deprecated

Replaced by Posts API for better consistency and feature parity.

Rate Limits & Best Practices

Rate Limiting

LinkedIn enforces daily request limits per application:

  • Limits vary by partnership tier
  • 429 Too Many Requests error when exceeded
  • No published public rate limits (unlike Twitter’s transparent limits)

Best Practices:

  • Implement exponential backoff for 429 errors
  • Cache user profile data (person URNs)
  • Batch image uploads when possible

API Guidelines

âś… Safe & Compliant:

  • Use official LinkedIn APIs
  • Official SDK/client libraries
  • Build server-side integrations

❌ Avoid (Risk of Account Ban):

  • Browser automation/bots
  • Unofficial scrapers
  • Chrome extensions that bypass API
  • Violating rate limits

Error Handling

Status CodeMeaningAction
201CreatedSuccess, read x-restli-id header
400Bad RequestCheck request body format
401UnauthorizedToken expired, re-authenticate
403ForbiddenMissing w_member_social permission
429Too Many RequestsBack off, reduce request rate
500Server ErrorRetry with exponential backoff

Rest.li Framework

LinkedIn built and uses the Rest.li framework for building RESTful architectures at scale.

Key Characteristics:

  • Custom REST semantics (not pure REST)
  • X-Restli-Protocol-Version header required
  • Response headers contain entity metadata (x-restli-id)
  • Typed responses with URN identifiers

Learn more: Rest.li GitHub

Comparison with Twitter/X Automation

Similarities

  • OAuth 2.0 authentication
  • REST API architecture
  • Official Node.js clients
  • Rate limiting
  • Media upload separate from post creation

Key Differences

AspectLinkedInTwitter/X
Token longevity60 days maxConfigurable, refreshable
Character limit3,000280 (free), 25,000 (Premium)
API versioningRequired headerOptional
Link previewsManual (no scraping)Automatic scraping
Rate limitsOpaque (daily app limit)Transparent per-endpoint
Refresh tokensPartner-onlyAvailable to all

Workflow Comparison

Twitter/X (with x-cli):

Terminal window
bun src/main.ts tweet "Hello world!"

LinkedIn (proposed linkedin-cli):

Terminal window
linkedin post "Hello LinkedIn!" --access-token=$LINKEDIN_TOKEN

Building a LinkedIn CLI Tool

Similar to x-cli for Twitter, you could build linkedin-cli:

Suggested Features

Terminal window
# Text post
linkedin post "Just shipped a feature!"
# Post with image
linkedin post "Check this out" --image ./screenshot.png
# Article post
linkedin article https://blog.example.com/post \
--title "My Article" \
--description "Summary here"
# Authentication
linkedin login # Opens OAuth flow

Implementation Considerations

Challenges compared to Twitter:

  • 60-day token expiry - Need to handle re-authentication
  • No refresh tokens - Can’t auto-refresh like Twitter
  • Manual link previews - Need to fetch og:title/og:image yourself
  • Image upload first - Can’t post image inline, must upload separately

Advantages:

  • 3,000 character limit (vs Twitter’s 280)
  • Professional audience
  • Better long-form content support
  • Official Node.js client

Use Cases for LinkedIn API

Build in Public (Similar to Twitter)

# Post daily updates
linkedin post "Day 15: Implemented caching layer. 30% faster response times."

Commit-to-Post Workflow

Terminal window
# Similar to commit-to-tweet
git log -1 --format="%s%n%n%b" | linkedin post --stdin

Cross-posting

Terminal window
# Post to both platforms
./scripts/post-update.sh "New release v2.0!" --twitter --linkedin

Company Page Automation

Use Pages API to post on behalf of company pages.

Sources

  1. Posts API - LinkedIn | Microsoft Learn
  2. Posting to LinkedIn via the API - Marcus Noble
  3. Build a LinkedIn Automation Stack with Official APIs in 2025
  4. LinkedIn API Documentation - Microsoft Learn
  5. UGC Post API - LinkedIn | Microsoft Learn
  6. linkedin-api-client (Official Node.js Client)
  7. LinkedIn’s New Posts API: The Good, The Bad, and The Ugly
  8. How to Post to LinkedIn via API - Ezz