Publishing to LinkedIn is one POST https://api.linkedin.com/rest/posts call, with the created post’s URN returned in the x-restli-id response header rather than the body. Media works by registering an upload first, uploading the bytes, then referencing the returned URN. The complications are the permission model and LinkedIn’s monthly API versioning.
What can the Posts API publish, and what cannot it publish?
For organic (non-sponsored) posts, LinkedIn’s content type table is explicit.
| Content type | Organic support |
|---|---|
| Text only | Yes |
| Images | Yes |
| Videos | Yes |
| Documents (PDF, DOC, DOCX, PPT, PPTX) | Yes |
| Article | Yes |
| MultiImage | Yes |
| Poll | Yes |
| Carousel | No, sponsored only |
Document posts are the pleasant surprise: PDF carousels, the format that performs well on LinkedIn, are fully supported through the Documents API. The limits are a file no larger than 100 MB and no more than 300 pages. Organic carousel posts, in LinkedIn’s ads sense of the word, are not supported; the organic equivalent is MultiImage. Article posts do not scrape the URL for you, so you must supply the title, description and a thumbnail image URN yourself.
The structural gap is the permission split. Posting as a person needs w_member_social. Posting as a company page needs w_organization_social, with r_organization_social to read back, and the authenticated member must hold an ADMINISTRATOR, CONTENT_ADMIN or DIRECT_SPONSORED_CONTENT_POSTER role on that page. In practice these are different API products with separate access requests, which is why a tool that posts to both a profile and a page ends up maintaining two separate LinkedIn apps. Our own integration does exactly that.
Note: Figures here were verified against LinkedIn’s Posts API, Images API and Documents API documentation on Microsoft Learn as of September 2026. Platforms change these without notice.
How does auth work, and how long is access review?
Three-legged OAuth for a member token, then every request carries three headers: Authorization: Bearer, X-Restli-Protocol-Version: 2.0.0, and LinkedIn-Version in YYYYMM form. That last one is not optional and it is not permanent. LinkedIn ships monthly versions and sunsets old ones, with a deprecation notice currently on the page stating that Marketing Version 202508 will be sunset on 17 August 2026. A version you pinned last year becomes an outage.
Access to the community management products is granted by application. LinkedIn does not publish a review turnaround in these developer docs, so treat the duration as unknown. Note also that r_member_social is a restricted permission “available to approved users only”, so reading a member’s own posts back is a separate ask from writing them.
We could not verify LinkedIn’s access token lifetimes from the Posts, Images or Documents API pages, which do not state them. Read the current figure from LinkedIn’s own authentication docs rather than a number in a blog post, including this one.
What is the publishing call sequence?
For a text post it is one call. For media it is three:
POST /rest/images?action=initializeUpload(or/rest/documents?action=initializeUpload) with aninitializeUploadRequest.ownerset to the person or organization URN.- Read
uploadUrland the asset URN (imageordocument) from the responsevalueobject. - Upload the bytes to
uploadUrl. A successful document upload returns 201. POST /rest/postswithauthor,commentary,visibility,distribution,lifecycleState: "PUBLISHED", andcontent.media.idset to the asset URN.- Read the created post URN from the
x-restli-idresponse header on the 201. It is not in the body.
# 1. register the upload
curl -X POST 'https://api.linkedin.com/rest/documents?action=initializeUpload' \
-H "Authorization: Bearer $TOKEN" \
-H 'X-Restli-Protocol-Version: 2.0.0' -H 'LinkedIn-Version: 202608' \
-d '{"initializeUploadRequest":{"owner":"urn:li:organization:5515715"}}'
# -> value.uploadUrl, value.document = urn:li:document:...
# 2. upload the bytes
curl -i --upload-file ./deck.pdf -H "Authorization: Bearer $TOKEN" "$UPLOAD_URL"
# 3. create the post, then read x-restli-id from the 201
curl -i -X POST 'https://api.linkedin.com/rest/posts' \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-H 'X-Restli-Protocol-Version: 2.0.0' -H 'LinkedIn-Version: 202608' \
-d '{
"author": "urn:li:organization:5515715",
"commentary": "Our Q3 teardown, 12 pages.",
"visibility": "PUBLIC",
"distribution": {"feedDistribution":"MAIN_FEED","targetEntities":[],
"thirdPartyDistributionChannels":[]},
"content": {"media": {"title":"deck.pdf","id":"urn:li:document:..."}},
"lifecycleState": "PUBLISHED",
"isReshareDisabledByAuthor": false
}'
Assets have their own status field: WAITING_UPLOAD, PROCESSING, PROCESSING_FAILED or AVAILABLE. Referencing one that has not reached AVAILABLE is a common source of a post that goes out without its media.
What are the rate limits?
LinkedIn’s Posts API error table documents 429 TOO_MANY_REQUESTS with the guidance to reduce request frequency and retry after a delay, but the page states no specific per-app or per-member request numbers. We could not verify concrete throttle figures from the Posts, Images or Documents API pages. LinkedIn publishes application-level and member-level daily throttles in the developer portal for your own app, which is the only accurate source for your quota, so read them there rather than assuming a shared number.
Two limits are stated concretely and are worth designing around: documents are capped at 100 MB and 300 pages, and images must be under 36,152,320 pixels, in JPG, GIF or PNG, with GIFs capped at 250 frames.
Note: Figures here were verified against LinkedIn’s Posts API, Images API and Documents API documentation on Microsoft Learn as of September 2026. Platforms change these without notice.
What will actually cost you three weeks?
Two apps, two reviews. Personal-profile posting and company-page posting are separate products with separate approvals. If your product promises both, you are running two OAuth flows, two credential sets and two review processes, and one can be approved while the other is not.
Monthly versioning. LinkedIn-Version needs a plan: a scheduled bump, a test that exercises the current version, and someone who reads the deprecation notices. This is the maintenance cost people forget when they estimate the build.
Media pipeline. Two-step upload for images, documents and video, each with its own endpoint and its own asset status to wait on, plus format and size validation before you waste an upload.
Async and partial failure. A 201 from /rest/posts is the strongest confirmation any of the big platforms give you, but the asset step in front of it is asynchronous, and lifecycleState can come back as PUBLISH_FAILED requiring an edit to retry. Model a processing state, as covered in our social media scheduling API guide, and never report success on the upload call alone.
If you are drafting the copy as well, the LinkedIn character counter shows where the “see more” cut falls.
The short version
- One
POST /rest/postscall; the post URN comes back inx-restli-id, not the body. - Media needs
initializeUpload, an upload, then the asset URN incontent.media.id. - Documents (PDF and Office files) are supported organically, up to 100 MB and 300 pages.
- Personal profiles and company pages use different permissions and, in practice, two apps.
LinkedIn-Versionis mandatory and versions get sunset. Schedule the bumps.
Doing this once instead of once per platform
If LinkedIn is one of several networks you need, the per-platform work multiplies rather than adds. BulkPublish publishes to 15 platforms through one REST API, including LinkedIn personal profiles and company pages (both apps, both reviews, already done) and LinkedIn document posts. Version headers, asset polling and token refresh sit on our side, and a post targeting several networks reports partial when one of them fails instead of pretending it worked. The API reference is at /developers/ and the REST integration overview at /integrations/rest-api/.