LinkedIn document carousels: how the PDF swipe post actually works
Every few months someone asks how to post a LinkedIn carousel through the API. The answer is that there is no such thing, and the thing they actually want has a different name.
The swipe post is a PDF
What people call a LinkedIn carousel is a document post. You upload a PDF, LinkedIn renders each page as a slide, and the feed shows it as a swipeable card. That is the entire mechanism.
So the API you want is the Documents API, not anything with "carousel" in the name.
Three calls
- Initialise the upload.
POST /rest/documents?action=initializeUploadwith the author URN. LinkedIn returns anuploadUrland adocumentURN. - PUT the bytes to that
uploadUrl. This is a plain upload to a LinkedIn-owned URL, not an API call in the usual sense. - Create the post.
POST /rest/postswith the document URN in thecontent.mediafield.
Between two and three, the document has to finish processing. Poll
GET /rest/documents/{urn} until its status is AVAILABLE.
The versioned endpoints have their own rules
The /rest/* endpoints are not the old /v2/* ones, and mixing them up
produces errors that look like authentication problems.
Every versioned call needs two headers:
LinkedIn-Version: 202506
X-Restli-Protocol-Version: 2.0.0
The version is a YYYYMM string and LinkedIn retires old ones on a schedule. If
you hard-code a single version, your integration has an expiry date you did not
choose. Keep a small ladder of acceptable versions in configuration, and make it
loud when the newest one you know about is rejected.
The empty 201
This is the one that costs an afternoon.
POST /rest/posts succeeds with 201 Created and an empty body. There is no
JSON, no id, nothing. A client written the obvious way parses the body, finds
nothing, and reports a failure for a post that went out perfectly.
The id is in a response header:
x-restli-id: urn:li:share:7231...
Read it from there. And if the header is missing but the status was 201, the post still succeeded — treat the missing id as an unknown id, not as an error, because retrying will publish a duplicate.
A document is posted alone
LinkedIn will not accept a post carrying a PDF and images together. One document, nothing else.
This is worth validating before the upload rather than after. Otherwise you discover it at step three, having already pushed a 20MB file across the wire.
Polling, and the duplicate-post trap
The same arithmetic that applies to Instagram containers applies here, and it is worth repeating because the failure is identical and equally expensive.
If your scheduler reclaims a job it thinks is stuck after five minutes, and your document-processing poll runs for five minutes, the two race. A second worker claims the post while the first is still polling, and the customer's audience gets the same carousel twice.
Give the poll a budget that is a fraction of the reclaim timeout. Half is fine. The cost of giving up early is a failed post the user can retry; the cost of giving up late is a duplicate they cannot delete from everyone's feed.
Summary
Upload a PDF, wait for AVAILABLE, create the post, read the id from
x-restli-id. Post the document on its own. Keep your API version in config,
and keep your poll shorter than your reclaim.
Frequently asked questions
Does LinkedIn have a carousel API?
No. LinkedIn removed carousel ads as a native format and never offered a carousel endpoint for organic posts. The swipeable post you see in the feed is a PDF uploaded through the Documents API and rendered by LinkedIn as pages you can swipe.
Why is the response body empty when I create a LinkedIn post?
The versioned /rest/posts endpoint answers 201 Created with no body. The id of the created post is returned in the x-restli-id header, so a client that only reads the body will conclude the call failed.
Can a LinkedIn post contain both a PDF and images?
No. A document post carries exactly one document and nothing else. If you need both, they have to be separate posts.