posting-automation
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
| Feature | LinkedIn Posts API | Twitter/X API |
|---|---|---|
| Authentication | OAuth 2.0 (60-day tokens) | OAuth 2.0 (configurable) |
| Token Refresh | Limited partner access | Supported for all |
| Text Limit | 3,000 characters | 280 chars (free), 25K (Premium) |
| Media Support | Images, videos, documents, articles | Images, videos, GIFs |
| Rate Limits | Daily app limit (varies) | Per-endpoint limits |
| API Versioning | YYYYMM header required | Not required |
| Base URL | https://api.linkedin.com/rest/ | https://api.twitter.com/2/ |
| Official Node.js Client | âś… linkedin-api-client | âś… twitter-api-v2 |
LinkedIn Posts API (Recommended)
API Endpoint
POST https://api.linkedin.com/rest/postsRequired Headers
Authorization: Bearer {ACCESS_TOKEN}X-Restli-Protocol-Version: 2.0.0LinkedIn-Version: 202511Content-Type: application/jsonImportant: LinkedIn-Version must be in YYYYMM format (e.g., 202511 for November 2025).
Success Response
HTTP/1.1 201 Createdx-restli-id: urn:li:share:6844785523593134080The x-restli-id header contains the Post ID.
API Versioning
| Date | Base Path | Status |
|---|---|---|
| June 2022+ | https://api.linkedin.com/rest/ | âś… Current (versioned) |
| Pre-2022 | https://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
- Go to LinkedIn Developers Portal
- Create new application
- Obtain API Key, Client Secret, and OAuth 2.0 credentials
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:
# Step 1: Redirect user to LinkedIn authorization URLhttps://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 tokenPOST https://www.linkedin.com/oauth/v2/accessTokenContent-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 Type | Validity | Refresh Support |
|---|---|---|
| Access Token | 60 days | ⚠️ Limited (partner-only) |
| Refresh Token | N/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
POST https://api.linkedin.com/rest/postsAuthorization: Bearer {ACCESS_TOKEN}X-Restli-Protocol-Version: 2.0.0LinkedIn-Version: 202511Content-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
- Upload video via Videos API → get
urn:li:video:{id} - Use video URN in post creation
Post with Document
- Upload document via Documents API → get
urn:li:document:{id} - 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"}Multi-Image Posts (Carousel)
| Post Type | API |
|---|---|
| Organic posts | MultiImage API |
| Sponsored posts | Carousel 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
npm install linkedin-api-clientGitHub: linkedin-api-client
Built with Axios and TypeScript.
Basic Usage
import { RestliClient } from 'linkedin-api-client';
// Initialize clientconst client = new RestliClient();
// Get user IDconst userInfo = await client.get({ resourcePath: '/userinfo', accessToken: ACCESS_TOKEN});
const personId = userInfo.sub;
// Create postconst 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
| Library | Description |
|---|---|
| gfiocco/linkedin-node-api | Share on LinkedIn with Node.js |
| chris287/linkedin-share-api-v2 | Complete 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 Code | Meaning | Action |
|---|---|---|
| 201 | Created | Success, read x-restli-id header |
| 400 | Bad Request | Check request body format |
| 401 | Unauthorized | Token expired, re-authenticate |
| 403 | Forbidden | Missing w_member_social permission |
| 429 | Too Many Requests | Back off, reduce request rate |
| 500 | Server Error | Retry 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-Versionheader 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
| Aspect | Twitter/X | |
|---|---|---|
| Token longevity | 60 days max | Configurable, refreshable |
| Character limit | 3,000 | 280 (free), 25,000 (Premium) |
| API versioning | Required header | Optional |
| Link previews | Manual (no scraping) | Automatic scraping |
| Rate limits | Opaque (daily app limit) | Transparent per-endpoint |
| Refresh tokens | Partner-only | Available to all |
Workflow Comparison
Twitter/X (with x-cli):
bun src/main.ts tweet "Hello world!"LinkedIn (proposed linkedin-cli):
linkedin post "Hello LinkedIn!" --access-token=$LINKEDIN_TOKENBuilding a LinkedIn CLI Tool
Similar to x-cli for Twitter, you could build linkedin-cli:
Suggested Features
# Text postlinkedin post "Just shipped a feature!"
# Post with imagelinkedin post "Check this out" --image ./screenshot.png
# Article postlinkedin article https://blog.example.com/post \ --title "My Article" \ --description "Summary here"
# Authenticationlinkedin login # Opens OAuth flowImplementation 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 updateslinkedin post "Day 15: Implemented caching layer. 30% faster response times."Commit-to-Post Workflow
# Similar to commit-to-tweetgit log -1 --format="%s%n%n%b" | linkedin post --stdinCross-posting
# Post to both platforms./scripts/post-update.sh "New release v2.0!" --twitter --linkedinCompany Page Automation
Use Pages API to post on behalf of company pages.
Sources
- Posts API - LinkedIn | Microsoft Learn
- Posting to LinkedIn via the API - Marcus Noble
- Build a LinkedIn Automation Stack with Official APIs in 2025
- LinkedIn API Documentation - Microsoft Learn
- UGC Post API - LinkedIn | Microsoft Learn
- linkedin-api-client (Official Node.js Client)
- LinkedIn’s New Posts API: The Good, The Bad, and The Ugly
- How to Post to LinkedIn via API - Ezz