How to Schedule LinkedIn Posts with an API (2026 Guide)


LinkedIn is one of the highest-value platforms for B2B content. It also has one of the most restrictive APIs in the social media landscape. If you want to schedule LinkedIn posts programmatically — whether for a SaaS product, an internal tool, an agency dashboard, or an AI content agent — you are going to hit walls that Instagram, TikTok, and X developers never encounter.
This guide covers two approaches: the native LinkedIn API (with all its approval hurdles and token headaches) and a unified alternative that handles LinkedIn publishing in a single HTTP request. Both come with working code examples.
Table of Contents
- Why LinkedIn's API Is Different
- Option 1: The Native LinkedIn API
- Authentication: OAuth 2.0 Three-Legged Flow
- Creating a Post with the LinkedIn API
- Uploading Media (Images, Videos, Documents)
- Document and Carousel Posts
- The Missing Piece: No Native Scheduling
- Option 2: PostEverywhere API
- Side-by-Side Comparison
- LinkedIn API Approval Tips
- Personal Profile vs Company Page API Access
- FAQs
Why LinkedIn's API Is Different
Most social media APIs let you create a developer app and start posting within minutes. LinkedIn does not work that way. Here is what makes it unusual:
- Marketing Developer Platform (MDP) approval required — you cannot post to Company Pages without it, and approval can take weeks
- Restrictive scopes — publishing to a personal profile requires
w_member_social, while Company Pages needw_organization_socialandr_organization_social - Token expiry — OAuth 2.0 access tokens expire after 60 days, and refresh tokens expire after 365 days
- No native scheduling — the API only supports immediate publishing; there is no
scheduled_atparameter - Rate limits — 100 API calls per day per user for most endpoints, with additional throttling on content creation
If you have built integrations with the Meta Graph API or Twitter API v2, LinkedIn's approval process and permission model will feel significantly more locked down.
For context on how every major platform API compares, see the social media scheduling API guide.
Option 1: The Native LinkedIn API
This section walks through everything you need to post to LinkedIn using their official API. It is a non-trivial amount of setup.
Prerequisites
Before you write a single line of code, you need:
- A LinkedIn Company Page — you must be an admin of the page you want to post to
- A LinkedIn Developer App — create one at developer.linkedin.com
- Marketing Developer Platform access — apply through your app's "Products" tab and wait for approval
- OAuth 2.0 credentials — your app's Client ID and Client Secret
The Marketing Developer Platform approval is the bottleneck. LinkedIn reviews each application manually, and they reject apps that look like spam tools, scraping operations, or anything that does not clearly articulate a legitimate business use case. More on this in the approval tips section.
Required Scopes
Once approved, you need to request these OAuth scopes:
| Scope | What It Grants |
|---|---|
openid |
OpenID Connect authentication |
profile |
Read the authenticated user's basic profile |
w_member_social |
Post on behalf of the authenticated user (personal profile) |
r_organization_social |
Read Company Page posts and engagement |
w_organization_social |
Create and delete Company Page posts |
rw_organization_admin |
Manage Company Page settings (optional) |
Not all scopes are available to all apps. w_organization_social specifically requires MDP approval.
Authentication: OAuth 2.0 Three-Legged Flow
LinkedIn uses a standard OAuth 2.0 authorization code flow. Here is the step-by-step process:
Step 1: Redirect the user to LinkedIn's authorization endpoint
https://www.linkedin.com/oauth/v2/authorization?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
scope=openid%20profile%20w_member_social%20w_organization_social%20r_organization_social&
state=RANDOM_STATE_VALUE
Step 2: Exchange the authorization code for an access token
import requests
response = requests.post(
"https://www.linkedin.com/oauth/v2/accessToken",
data={
"grant_type": "authorization_code",
"code": "AUTHORIZATION_CODE_FROM_CALLBACK",
"redirect_uri": "https://yourapp.com/callback",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
},
)
token_data = response.json()
access_token = token_data["access_token"]
# Expires in 5,184,000 seconds (60 days)
expires_in = token_data["expires_in"]
Step 3: Use the access token for API calls
The access token goes in the Authorization header as a Bearer token. It expires after 60 days. If you have refresh tokens enabled (available with MDP access), the refresh token lasts 365 days. You need to build token refresh logic into your application or your posts will silently fail after two months.
This is the first place where things start to get complicated for production systems. You need persistent token storage, expiry tracking, and a refresh mechanism that runs before the token dies. Compare that to an API key that never expires and you start to see why many teams look for alternatives to the native API.
Creating a Post with the LinkedIn API
LinkedIn has two posting endpoints. The legacy /ugcPosts endpoint still works but is deprecated. The current endpoint is /posts. Use the new one.
Text Post to a Company Page
import requests
access_token = "YOUR_ACCESS_TOKEN"
organization_id = "YOUR_ORGANIZATION_ID" # numeric ID
response = requests.post(
"https://api.linkedin.com/rest/posts",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"LinkedIn-Version": "202401",
"X-Restli-Protocol-Version": "2.0.0",
},
json={
"author": f"urn:li:organization:{organization_id}",
"commentary": "We just shipped our new API integration. Here is what changed and why it matters for your workflow.",
"visibility": "PUBLIC",
"distribution": {
"feedDistribution": "MAIN_FEED",
"targetEntities": [],
"thirdPartyDistributionChannels": [],
},
"lifecycleState": "PUBLISHED",
},
)
# Returns 201 Created with the post URN in the x-restli-id header
print(response.status_code)
print(response.headers.get("x-restli-id"))
Text Post to a Personal Profile
The only difference is the author field:
# For personal profiles, use the person URN
"author": "urn:li:person:YOUR_PERSON_ID"
You can get the person ID by calling GET /me with the profile scope.
Key Gotchas
- The
LinkedIn-Versionheader is required. Without it, you will get a 403 error that tells you nothing useful. - The
commentaryfield has a maximum length of 3,000 characters. lifecycleStatemust bePUBLISHED. There is noSCHEDULEDoption — LinkedIn's API does not support deferred publishing.- The response does not include the post body in the response. You get a 201 status and a post URN. That is it.
Uploading Media (Images, Videos, Documents)
LinkedIn's media upload is a multi-step process. You cannot just send a URL. You need to register the upload, send the binary data to LinkedIn's storage, and then reference the asset in your post.
Image Upload Flow
Step 1: Register the upload
register_response = requests.post(
"https://api.linkedin.com/rest/images?action=initializeUpload",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"LinkedIn-Version": "202401",
},
json={
"initializeUploadRequest": {
"owner": f"urn:li:organization:{organization_id}",
}
},
)
upload_data = register_response.json()["value"]
upload_url = upload_data["uploadUrl"]
image_urn = upload_data["image"]
Step 2: Upload the binary
with open("image.jpg", "rb") as f:
upload_response = requests.put(
upload_url,
headers={
"Authorization": f"Bearer {access_token}",
},
data=f.read(),
)
# Returns 201 Created
Step 3: Create the post with the image
post_response = requests.post(
"https://api.linkedin.com/rest/posts",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"LinkedIn-Version": "202401",
"X-Restli-Protocol-Version": "2.0.0",
},
json={
"author": f"urn:li:organization:{organization_id}",
"commentary": "Sharing our Q1 results. Full breakdown in the comments.",
"visibility": "PUBLIC",
"distribution": {
"feedDistribution": "MAIN_FEED",
"targetEntities": [],
"thirdPartyDistributionChannels": [],
},
"content": {
"media": {
"title": "Q1 Results",
"id": image_urn,
}
},
"lifecycleState": "PUBLISHED",
},
)
That is three API calls to post a single image. For video, the process is even more involved because LinkedIn requires chunked uploads for files over 200 MB, and you need to poll for processing status before the post goes live.
Document and Carousel Posts
LinkedIn carousel posts are actually PDF documents displayed as slideable cards. The API treats them as document uploads, not a special carousel type.
The upload flow mirrors the image process but uses the /documents endpoint:
# Step 1: Initialize document upload
doc_response = requests.post(
"https://api.linkedin.com/rest/documents?action=initializeUpload",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"LinkedIn-Version": "202401",
},
json={
"initializeUploadRequest": {
"owner": f"urn:li:organization:{organization_id}",
}
},
)
doc_data = doc_response.json()["value"]
doc_upload_url = doc_data["uploadUrl"]
document_urn = doc_data["document"]
# Step 2: Upload the PDF
with open("carousel.pdf", "rb") as f:
requests.put(
doc_upload_url,
headers={"Authorization": f"Bearer {access_token}"},
data=f.read(),
)
# Step 3: Create the post referencing the document
requests.post(
"https://api.linkedin.com/rest/posts",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"LinkedIn-Version": "202401",
"X-Restli-Protocol-Version": "2.0.0",
},
json={
"author": f"urn:li:organization:{organization_id}",
"commentary": "5 things we learned scaling to 10,000 users. Swipe through.",
"visibility": "PUBLIC",
"distribution": {
"feedDistribution": "MAIN_FEED",
"targetEntities": [],
"thirdPartyDistributionChannels": [],
},
"content": {
"media": {
"title": "Scaling Lessons",
"id": document_urn,
}
},
"lifecycleState": "PUBLISHED",
},
)
If you are building carousel posts programmatically, you need to generate the PDF first. Tools like Puppeteer, wkhtmltopdf, or dedicated carousel generators can help. PostEverywhere also has a free LinkedIn carousel maker if you need a quick way to create them visually.
For a deeper dive into carousel strategies and formatting, see the guide to scheduling LinkedIn carousels.
The Missing Piece: No Native Scheduling
Here is the fundamental limitation: LinkedIn's API does not support scheduling. There is no scheduled_at parameter, no deferred publish state, no queue. Every post you create through the API is published immediately.
If you need to schedule posts for a future time, you have exactly two options:
- Build your own scheduler — store posts in a database, run a cron job or task queue (Celery, Bull, SQS) that fires at the scheduled time, and call the LinkedIn API at that exact moment. You also need to handle token refresh, retries on failure, timezone conversion, and queue management.
- Use a scheduling API that supports LinkedIn — send a single request with a
scheduled_attimestamp and let someone else handle the infrastructure.
Option 1 is a significant engineering investment. You are building job scheduling infrastructure, token management, error handling, and monitoring — all for a single platform. Multiply that by every other social network you need to support and you have a full-time maintenance burden.
Option 2 is what most teams end up choosing.
PostEverywhere's API handles LinkedIn scheduling, token refresh, and Company Page authentication in a single request. No cron jobs, no token storage, no MDP approval required. See the API docs
Option 2: PostEverywhere API
The PostEverywhere API wraps LinkedIn (and 7 other platforms) into a single REST endpoint. Here is the same LinkedIn post — with scheduling — in 5 lines of cURL:
curl -X POST https://app.posteverywhere.ai/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "We just shipped our new API integration. Here is what changed and why it matters for your workflow.",
"platforms": ["linkedin"],
"scheduled_at": "2026-04-15T09:00:00Z"
}'
That is it. No OAuth flow. No token refresh. No Marketing Developer Platform approval. No three-step media upload. The API key does not expire, and scheduling is a first-class feature.
Image Post
curl -X POST https://app.posteverywhere.ai/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Sharing our Q1 results. Full breakdown in the comments.",
"platforms": ["linkedin"],
"media": [{"url": "https://yourcdn.com/q1-results.jpg"}],
"scheduled_at": "2026-04-15T09:00:00Z"
}'
One request. The API downloads the image, uploads it to LinkedIn's storage, and publishes at the scheduled time.
Document / Carousel Post
curl -X POST https://app.posteverywhere.ai/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "5 things we learned scaling to 10,000 users. Swipe through.",
"platforms": ["linkedin"],
"media": [{"url": "https://yourcdn.com/carousel.pdf", "type": "document"}],
"scheduled_at": "2026-04-15T09:00:00Z"
}'
Cross-Platform Scheduling
The real power shows up when you schedule to multiple platforms at once:
import requests
response = requests.post(
"https://app.posteverywhere.ai/api/v1/posts",
headers={"Authorization": f"Bearer {api_key}"},
json={
"content": "We just shipped our new API integration.",
"platforms": ["linkedin", "x", "facebook", "threads"],
"scheduled_at": "2026-04-15T09:00:00Z",
},
)
print(response.json())
One request, four platforms, scheduled publishing. The API handles the per-platform formatting differences, character limits, and authentication automatically.
For the full API reference, code samples in Python, Node.js, and Go, and webhook configuration, see the developer documentation.
Side-by-Side Comparison
Here is how the two approaches compare for a production LinkedIn scheduling integration:
| Capability | LinkedIn Native API | PostEverywhere API |
|---|---|---|
| Setup time | Days to weeks (MDP approval) | Minutes (API key) |
| Authentication | OAuth 2.0, 3-legged flow | API key (Bearer token) |
| Token management | You build it (60-day expiry) | Handled automatically |
| Scheduling | Not supported (build your own) | Native scheduled_at parameter |
| Image upload | 3 API calls (register, upload, post) | 1 API call (URL reference) |
| Document/carousel | 3 API calls (register, upload, post) | 1 API call (URL reference) |
| Video upload | Chunked upload + polling | 1 API call (URL reference) |
| Cross-platform | LinkedIn only | 8 platforms in one request |
| Rate limits | 100 calls/day per user | Higher limits, platform-managed |
| Personal profiles | Limited (w_member_social) |
Supported |
| Company Pages | Requires MDP approval | Connect in dashboard |
| Cost | Free (API access) | Starts at $19/mo (pricing) |
| Maintenance | Token refresh, error handling, retries | Managed infrastructure |
The native API is free in terms of LinkedIn charges, but the engineering time to build and maintain a production-quality scheduler around it is not. Most teams find that the total cost of ownership heavily favours a managed solution, especially when they need more than just LinkedIn.
Skip the MDP approval queue. PostEverywhere connects to LinkedIn in 30 seconds and gives you a scheduling API that works across all major platforms. Start your free trial — no credit card required.
LinkedIn API Approval Tips
If you do need direct API access — for analytics, ad management, or deep integrations that go beyond posting — here is what gets approved and what gets rejected.
What Gets Approved
- Clear business use case — "We are an agency managing LinkedIn content for 50+ clients and need to publish on their behalf" works. Vague descriptions do not.
- Company verification — LinkedIn favours established companies with a real web presence, active LinkedIn Page, and verifiable employees.
- Specific scope requests — only ask for the scopes you need. Requesting everything raises flags.
- Compliance documentation — show that you understand LinkedIn's terms of service, data handling requirements, and content policies.
- Demo or product URL — if you have a live product or even a staging environment, include it. It dramatically improves approval odds.
What Gets Rejected
- Personal projects without a clear commercial application
- Bulk automation tools marketed as spam or mass outreach solutions
- Scraping-adjacent language — anything that implies harvesting LinkedIn data
- Vague applications — "We want to explore the API" is not a use case
- Apps without a privacy policy or terms of service
Timeline
Expect 1-4 weeks for Marketing Developer Platform approval. Some developers report faster turnaround (under a week), while others wait a month or longer. There is no expedited process. If you are rejected, you can reapply with a stronger application, but there is no guaranteed timeline for re-review.
During this waiting period, you can use LinkedIn's community management API products for testing with limited scope. But you will not be able to post to Company Pages until MDP access is granted.
Personal Profile vs Company Page API Access
This distinction trips up a lot of developers. LinkedIn treats personal profiles and Company Pages as fundamentally different entities with different permissions, different scopes, and different approval requirements.
Personal Profile Posting
- Scope needed:
w_member_social - Approval: available through the "Share on LinkedIn" product (easier to get than MDP)
- Author URN:
urn:li:person:{person_id} - Limitations: cannot tag Company Pages in some post types; limited analytics access
- Use case: tools that let individual users post from their own accounts
Company Page Posting
- Scope needed:
w_organization_social+r_organization_social - Approval: requires Marketing Developer Platform access (harder, longer wait)
- Author URN:
urn:li:organization:{organization_id} - Requirements: the authenticated user must be a Super Admin or Content Admin of the Company Page
- Use case: social media management tools, agency dashboards, multi-brand publishing
The Practical Impact
If you are building a product that lets users schedule LinkedIn posts, you need to decide early whether you are supporting personal profiles, Company Pages, or both. Each path has a different approval process and different technical requirements.
With PostEverywhere, users connect their LinkedIn accounts (personal or Company Page) through the dashboard, and the API handles both entity types through the same endpoint. You do not need to manage separate OAuth flows or scope requests for each.
For teams that manage multiple LinkedIn accounts, see the best LinkedIn scheduler tools for a comparison of dashboard options alongside API access.
FAQs
Can I schedule LinkedIn posts with the native LinkedIn API?
No. LinkedIn's REST API only supports immediate publishing. There is no scheduled_at parameter or deferred publish state. To schedule posts, you either need to build your own scheduling infrastructure (database, cron jobs, token refresh) or use a third-party API like PostEverywhere that handles scheduling natively.
How long does LinkedIn Marketing Developer Platform approval take?
Typically 1-4 weeks. Some applications are approved within days, while others take a month or longer. The key factors are a clear business use case, a verifiable company, and a specific description of how you will use the API. There is no expedited process.
Do LinkedIn API access tokens expire?
Yes. Access tokens expire after 60 days. Refresh tokens (available with MDP access) expire after 365 days. You need to build token refresh logic into your application to maintain uninterrupted posting. If you use PostEverywhere's API, token management is handled automatically.
What is the difference between /ugcPosts and /posts on LinkedIn?
/ugcPosts is LinkedIn's legacy posting endpoint. /posts is the current, recommended endpoint introduced with the versioned API. Both still work as of 2026, but LinkedIn has deprecated /ugcPosts and new applications should use /posts exclusively. The request format differs significantly between the two.
Can I post to a personal LinkedIn profile via API?
Yes, but with limitations. You need the w_member_social scope, which is available through the "Share on LinkedIn" product (separate from MDP). Personal profile posting supports text, images, and link posts. Some features available on Company Pages (like document posts and analytics) may have restricted access on personal profiles.
How do I post LinkedIn carousel posts via API?
LinkedIn carousels are PDF documents. Upload a PDF using the /documents endpoint (register, upload binary, reference in post), and LinkedIn renders each page as a swipeable slide. The PostEverywhere API simplifies this to a single request with a PDF URL. See the LinkedIn carousel scheduling guide for detailed examples.
What are LinkedIn API rate limits?
LinkedIn enforces a limit of 100 API calls per day per member token for most endpoints. Content creation endpoints may have additional throttling. The exact limits vary by product and are not always publicly documented. If you hit rate limits frequently, a managed API that handles batching and retry logic can help.
Can I use PostEverywhere's API for LinkedIn without MDP approval?
Yes. When you connect a LinkedIn account through PostEverywhere's dashboard, PostEverywhere handles the OAuth flow and permissions using its own approved application. You do not need your own MDP approval. Your API key gives you access to schedule and publish to any LinkedIn account connected in your dashboard.
Wrap Up
LinkedIn's API is powerful but heavily gated. Between the Marketing Developer Platform approval, OAuth 2.0 token management, multi-step media uploads, and the complete absence of native scheduling — building a production LinkedIn posting integration from scratch is a significant project.
If you need direct API access for analytics, ads, or deep LinkedIn-specific features, the native route is worth the investment. But if your goal is scheduling and publishing LinkedIn content programmatically — especially alongside other platforms — a unified API eliminates weeks of setup and ongoing maintenance.
PostEverywhere's API gives you LinkedIn scheduling (plus 7 other platforms) with a single API key and a single HTTP request. No approval queues, no token refresh, no cron jobs.
Check out the developer documentation for the full API reference, or read the complete social media scheduling API guide for a broader look at how unified APIs compare across all platforms.

Founder & CEO of PostEverywhere. Writing about social media strategy, publishing workflows, and analytics that help brands grow faster.