Publish and schedule LinkedIn text, image, video, document, and reshare posts to a personal profile or a company page with PostZen.
PostZen publishes text, multi-image, video, document (PDF carousel), and quote-reshare posts to LinkedIn — either as the connected member or as a company page they administer. Every post goes through LinkedIn's versioned Posts API, and the same request shape covers immediate publishing, scheduling, and drafts.
Quick reference
| Item | LinkedIn through PostZen |
|---|---|
| Platform value | linkedin |
| Destinations | Personal (member) profile, or any company page the member administers |
| Text | Up to 3,000 characters (optional when media or a reshare is attached) |
| Images per post | 1–20; 2 or more render as a LinkedIn multi-image post |
| Maximum image size | 8 MB per image |
| Video | Exactly 1 MP4; 75 KB–500 MB; at least 3 seconds |
| Video duration ceiling | 10 minutes on a personal profile, 30 minutes on a company page |
| Document | Exactly 1 PDF, DOC, DOCX, PPT, or PPTX; up to 100 MB and 300 pages |
| Mixed media kinds | Not supported — a post is text, images, one video, or one document |
| Quote reshare | Supported through settings.reshareUrl; cannot be combined with media |
| First comment | Supported; up to 1,250 characters |
| Visibility | PUBLIC or CONNECTIONS; company pages are PUBLIC only |
| Geo restriction | Up to 25 countries; company pages only |
| Comment and reaction reads | Company-page posts only |
| Scheduling | Supported |
Before you start
- Create a PostZen profile and keep its ID available. The connect endpoint attaches the LinkedIn account to that profile.
- Connect a LinkedIn account. PostZen manages the OAuth 2.0 / OpenID Connect scopes internally.
- To publish as a company page, the connected member must be an administrator of that page, and the connection must have been authorized with the organization scopes. See Post as a company page.
- The free tier allows 2 connected accounts before a payment method is required. A connection beyond that limit returns
402. - A single post carries exactly one media kind. Never mix images, video, and documents in one LinkedIn target.
Media URLs must be publicly accessible direct links to the files. Google Drive, Dropbox, OneDrive, and iCloud share links return HTML pages instead of the media file. Upload the file through POST /v1/media/presign when you do not have a direct URL.
Connect your account
Request a LinkedIn connect URL for the PostZen profile that should own the account. PostZen returns { authUrl, state } — send the user to authUrl to complete OAuth. The state expires after 10 minutes.
import PostZen from '@postzen/node';
const postzen = new PostZen({
apiKey: process.env.POSTZEN_API_KEY,
});
const { data } = await postzen.connect.createConnectUrl({
path: { platform: 'linkedin' },
query: {
profileId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
redirectUrl: 'https://yourapp.com/connected',
},
});
console.log(data.authUrl); // redirect the user here
console.log(data.state); // expires after 10 minutesfrom postzen import PostZen
client = PostZen() # reads POSTZEN_API_KEY
response = client.connect.create_connect_url(
"linkedin",
profile_id="jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
redirect_url="https://yourapp.com/connected",
)
print(response.authUrl) # redirect the user here
print(response.state) # expires after 10 minutescurl "https://api.postzen.dev/v1/connect/linkedin?profileId=jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e&redirectUrl=https://yourapp.com/connected" \
-H "Authorization: Bearer $POSTZEN_API_KEY"After the user completes the flow, list accounts with GET /v1/accounts?profileId=...&platform=linkedin and use the connected account's ID as accountId when you create a post.
Publishing to a personal profile needs the openid, profile, and w_member_social scopes, which every LinkedIn connection requests. email is requested too but never required — denying it costs the connection nothing.
LinkedIn's refresh-token grant returns the scope set the original authorization was granted with, so widening the requested scopes never widens an existing connection. Whenever PostZen starts requesting new LinkedIn scopes — company-page posting and analytics are both in this category — an account connected before that change has to run the connect flow again before the new capabilities appear.
Quick start
Publish a plain text post to a connected LinkedIn account. A create request must set exactly one of publishNow, scheduledFor, or isDraft — this example uses publishNow.
const { data } = await postzen.posts.createPost({
body: {
content: 'We just shipped a new release.',
publishNow: true,
platforms: [
{
platform: 'linkedin',
accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
},
],
},
});
console.log(data.post._id);response = client.posts.create_post(
content="We just shipped a new release.",
publish_now=True,
platforms=[
{
"platform": "linkedin",
"account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
},
],
)
print(response.post.field_id)curl -X POST https://api.postzen.dev/v1/posts \
-H "Authorization: Bearer $POSTZEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "We just shipped a new release.",
"publishNow": true,
"platforms": [
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e"
}
]
}'To schedule the post, replace publishNow with scheduledFor and provide an ISO-8601 timestamp at least 60 seconds in the future. Use isDraft instead to save a draft.
Every LinkedIn setting below is accepted in camelCase and in snake_case, so firstComment and first_comment are the same field.
Post types
Text posts
Set content to no more than 3,000 characters and omit mediaItems. The quick-start example shows the complete request.
PostZen escapes the characters LinkedIn reserves in its "little text" commentary format, so parentheses, brackets, and @, #, *, and _ in your text publish literally instead of breaking the post.
Image posts
Attach 1–20 images through mediaItems. One image publishes as a single-image post; 2 or more publish as a LinkedIn multi-image post in the order you send them.
| Requirement | Value |
|---|---|
| Images per post | 1–20 |
| Maximum file size | 8 MB per image |
Upload types through /v1/media/presign | JPEG/JPG, PNG, WebP, GIF |
| Recommended dimensions | 1200×627 for a single link or image post |
| Mixing with video or a document | Not allowed |
const { data } = await postzen.posts.createPost({
body: {
content: 'A look at our latest release.',
publishNow: true,
mediaItems: [
{ url: 'https://cdn.example.com/release-1.png' },
{ url: 'https://cdn.example.com/release-2.png' },
],
platforms: [
{
platform: 'linkedin',
accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
settings: {
visibility: 'CONNECTIONS',
},
},
],
},
});
console.log(data.post._id);response = client.posts.create_post(
content="A look at our latest release.",
publish_now=True,
media_items=[
{"url": "https://cdn.example.com/release-1.png"},
{"url": "https://cdn.example.com/release-2.png"},
],
platforms=[
{
"platform": "linkedin",
"account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"visibility": "CONNECTIONS",
},
},
],
)
print(response.post.field_id)curl -X POST https://api.postzen.dev/v1/posts \
-H "Authorization: Bearer $POSTZEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "A look at our latest release.",
"publishNow": true,
"mediaItems": [
{ "url": "https://cdn.example.com/release-1.png" },
{ "url": "https://cdn.example.com/release-2.png" }
],
"platforms": [
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"visibility": "CONNECTIONS"
}
}
]
}'External media URLs can be up to 100 MB when PostZen downloads and re-hosts them, but every image in a LinkedIn post must still be no larger than 8 MB.
Video posts
Attach exactly one MP4 through mediaItems. content is optional for a video post. Use settings.videoTitle to set the title shown on the LinkedIn video player.
| Requirement | Value |
|---|---|
| Videos per post | Exactly 1 |
| Format | MP4 |
| File size | 75 KB–500 MB |
| Minimum duration | 3 seconds |
| Maximum duration | 10 minutes as a personal profile, 30 minutes as a company page |
| Mixing with images or a document | Not allowed |
| Processing | Asynchronous on LinkedIn's side; PostZen polls until it finishes, then creates the post |
// 1. Request a presigned upload URL for the MP4.
const { data: presign } = await postzen.media.createMediaPresign({
body: { filename: 'launch.mp4', contentType: 'video/mp4', size: fileBuffer.byteLength },
});
// 2. Upload the file bytes to the presigned URL.
await fetch(presign.uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': 'video/mp4' },
body: fileBuffer,
});
// 3. Create the post referencing the returned publicUrl.
const { data } = await postzen.posts.createPost({
body: {
content: 'A look at our latest release.',
publishNow: true,
mediaItems: [{ url: presign.publicUrl }],
platforms: [
{
platform: 'linkedin',
accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
settings: {
videoTitle: 'Launch highlights',
},
},
],
},
});
console.log(data.post._id);import os
import requests
# 1. Request a presigned upload URL for the MP4.
presign = client.media.create_media_presign(
filename="launch.mp4",
content_type="video/mp4",
size=os.path.getsize("launch.mp4"),
)
# 2. Upload the file bytes to the presigned URL.
with open("launch.mp4", "rb") as f:
requests.put(
presign.uploadUrl,
data=f,
headers={"Content-Type": "video/mp4"},
)
# 3. Create the post referencing the returned publicUrl.
response = client.posts.create_post(
content="A look at our latest release.",
publish_now=True,
media_items=[{"url": presign.publicUrl}],
platforms=[
{
"platform": "linkedin",
"account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"video_title": "Launch highlights",
},
},
],
)
print(response.post.field_id)# 1. Request a presigned upload URL for the MP4.
curl -X POST https://api.postzen.dev/v1/media/presign \
-H "Authorization: Bearer $POSTZEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "filename": "launch.mp4", "contentType": "video/mp4", "size": 52428800 }'
# 2. Upload the file bytes to the returned uploadUrl.
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/mp4" \
--data-binary @launch.mp4
# 3. Create the post referencing the returned publicUrl.
curl -X POST https://api.postzen.dev/v1/posts \
-H "Authorization: Bearer $POSTZEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "A look at our latest release.",
"publishNow": true,
"mediaItems": [
{ "url": "https://media.postzen.dev/.../launch.mp4" }
],
"platforms": [
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"videoTitle": "Launch highlights"
}
}
]
}'LinkedIn processes uploaded videos asynchronously. PostZen uploads the file, then polls LinkedIn until processing finishes before creating the post, so publishing a video can take a minute or two longer than a text or image post.
You can also pass an external MP4 URL directly in mediaItems instead of presigning. External URL ingest is capped at 100 MB; presigned uploads support larger files up to LinkedIn's 500 MB video limit.
Document posts (PDF carousels)
Attach exactly one document through mediaItems. LinkedIn renders it as a swipeable carousel in the feed.
| Requirement | Value |
|---|---|
| Documents per post | Exactly 1 |
| Formats | PDF, DOC, DOCX, PPT, PPTX |
| Maximum file size | 100 MB |
| Maximum pages | 300 (enforced by LinkedIn, not by PostZen) |
| Title | settings.documentTitle, up to 200 characters; falls back to the uploaded file's name |
| Mixing with images or video | Not allowed |
POST /v1/media/presign accepts application/pdf as its only document content type. To publish a DOC, DOCX, PPT, or PPTX post, host the file yourself and pass a publicly accessible direct URL in mediaItems.
const { data } = await postzen.posts.createPost({
body: {
content: 'Our 2026 benchmark report, in 12 slides.',
publishNow: true,
mediaItems: [{ url: 'https://cdn.example.com/benchmark-report.pdf' }],
platforms: [
{
platform: 'linkedin',
accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
settings: {
documentTitle: '2026 Benchmark Report',
},
},
],
},
});
console.log(data.post._id);response = client.posts.create_post(
content="Our 2026 benchmark report, in 12 slides.",
publish_now=True,
media_items=[{"url": "https://cdn.example.com/benchmark-report.pdf"}],
platforms=[
{
"platform": "linkedin",
"account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"document_title": "2026 Benchmark Report",
},
},
],
)
print(response.post.field_id)curl -X POST https://api.postzen.dev/v1/posts \
-H "Authorization: Bearer $POSTZEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Our 2026 benchmark report, in 12 slides.",
"publishNow": true,
"mediaItems": [
{ "url": "https://cdn.example.com/benchmark-report.pdf" }
],
"platforms": [
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"documentTitle": "2026 Benchmark Report"
}
}
]
}'Quote reshares
Set settings.reshareUrl to an existing LinkedIn post to publish a quote reshare with your own commentary on top. PostZen accepts a public post permalink or a raw URN:
https://www.linkedin.com/posts/jane-doe_some-slug-activity-7123456789012345678-Ab1curn:li:activity:7123456789012345678urn:li:share:7123456789012345678urn:li:ugcPost:7123456789012345678
A reshare cannot carry its own media — send reshareUrl or mediaItems, never both.
{
"content": "Worth reading if you run a support team.",
"publishNow": true,
"platforms": [
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"reshareUrl": "https://www.linkedin.com/posts/jane-doe_support-activity-7123456789012345678-Ab1c"
}
}
]
}Post as a company page
Set settings.organizationUrn to publish as a company page instead of the connected member. Both forms are accepted:
- The full URN:
"urn:li:organization:12345" - The bare numeric page id:
"12345"(PostZen expands it to the URN)
The connected member must be an administrator of the page, and the connection must hold LinkedIn's Community Management scopes. PostZen requests all six together:
| Scope | What it unlocks |
|---|---|
w_organization_social | Publishing as the page |
rw_organization_admin | Listing the pages the member administers, and page reporting |
r_organization_social | Reading the page's own posts |
w_organization_social_feed | The first comment on a page post |
w_member_social_feed | The first comment on a personal post |
r_organization_social_feed | The comments and reactions read endpoints |
All six belong to LinkedIn's Community Management API, which is approval-gated, so they share one on/off switch: PostZen only requests them on deployments where company-page posting is enabled. An account connected before that point must run the connect flow again — a token refresh keeps the scopes the original authorization was granted with.
{
"content": "We are hiring three more engineers this quarter.",
"publishNow": true,
"platforms": [
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"organizationUrn": "urn:li:organization:12345",
"visibility": "PUBLIC"
}
}
]
}In the PostZen dashboard, the LinkedIn options panel of the composer shows a Post as picker listing every page the connected member administers, with the member's own profile as the default.
Personal profile vs. company page
| Capability | Personal profile | Company page |
|---|---|---|
| Text, image, video, and document posts | Yes | Yes |
| Quote reshares | Yes | Yes |
| First comment | Yes | Yes |
| Video duration ceiling | 10 minutes | 30 minutes |
visibility: "CONNECTIONS" | Yes | No — company pages are PUBLIC only |
geoRestrictionCountries | No | Yes, up to 25 countries |
Read comments (GET /v1/posts/{postId}/comments) | No | Yes |
Read reactions (GET /v1/posts/{postId}/reactions) | No | Yes |
| Analytics | Coming soon, pending LinkedIn review | Coming soon; every metric except saves, and no page follower counts |
Platform settings
Place LinkedIn settings inside the matching target in the platforms array. Each key is also accepted in snake_case.
| Name | Type | Notes |
|---|---|---|
visibility | "PUBLIC" | "CONNECTIONS" | Who can see the post. Defaults to PUBLIC. CONNECTIONS is rejected on a company-page post. |
videoTitle | string | Title shown on the LinkedIn video player, up to 200 characters. Ignored for non-video posts. |
documentTitle | string | Title for a document post, up to 200 characters. Falls back to the uploaded file's name. Ignored for non-document posts. |
organizationUrn | string | urn:li:organization:<id> or the bare numeric page id. Publishes as that company page. Also accepted as organizationId. |
firstComment | string | Comment posted by the same author right after the post goes live, up to 1,250 characters. |
disableLinkPreview | boolean | Set to true to keep a post with a link as plain text with no preview card. Also accepted as disableLinkCard. |
reshareUrl | string | LinkedIn post URL or URN to quote-reshare. Mutually exclusive with mediaItems. |
geoRestrictionCountries | string[] | Up to 25 uppercase ISO 3166-1 alpha-2 country codes. Company-page posts only. |
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"organizationUrn": "urn:li:organization:12345",
"visibility": "PUBLIC",
"firstComment": "Full write-up on the blog: https://example.com/blog/launch",
"disableLinkPreview": true,
"geoRestrictionCountries": ["US", "CA", "GB"]
}
}First comment
settings.firstComment posts a comment as the same author immediately after the post goes live. LinkedIn's comment composer caps it at 1,250 characters, tighter than the 3,000-character post body. Use it to keep links out of the post itself.
The first comment is best-effort by design: the post is already published when PostZen attempts it, so a failure is logged and never fails or retries the post. Check the post on LinkedIn if the comment does not appear.
Writing a comment goes through LinkedIn's versioned social-action API, which checks w_member_social_feed for a personal post and w_organization_social_feed for a company-page post. Both ship with the Community Management scopes described above, so on a deployment without them the post still publishes and only the comment is skipped.
Link previews
LinkedIn's Posts API never scrapes URLs, so a bare link in your text renders as plain text. When a post has no media and no reshareUrl, PostZen attaches a link card for the first URL in the text so a preview renders.
LinkedIn requires a title on that card and PostZen has no scraped page metadata to fill it with, so the card is titled with the URL's hostname — example.com rather than the page's real title. Set disableLinkPreview: true when you would rather have plain text than a hostname-titled card.
A post that already carries images, a video, a document, or a reshare never gets a link card, whatever disableLinkPreview is set to.
Geo restriction
settings.geoRestrictionCountries limits who sees the post by country. Pass up to 25 uppercase ISO 3166-1 alpha-2 codes; PostZen resolves each one to the LinkedIn geo URN the Posts API expects.
Geo restriction is a company-page feature: sending it without organizationUrn is a validation error.
{
"platform": "linkedin",
"accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
"settings": {
"organizationUrn": "urn:li:organization:12345",
"geoRestrictionCountries": ["US", "CA"]
}
}Read comments and reactions
Two read endpoints return the engagement LinkedIn holds on a post published through PostZen:
Both are LinkedIn company-page posts only and require the connection to hold r_organization_social_feed. postId is the PostZen post id, not a LinkedIn URN. Add ?accountId= when one post published to several LinkedIn accounts.
A personal-profile post always returns 403 with code: "personalPostUnsupported". Reading a member's own comments and reactions needs LinkedIn's r_member_social_feed scope, which LinkedIn grants to select developers only — this is a platform limit, not a missing feature.
curl "https://api.postzen.dev/v1/posts/jx58t2kqm4wr9v3n7c1zp6bs0dh5fg8y/comments" \
-H "Authorization: Bearer $POSTZEN_API_KEY"
curl "https://api.postzen.dev/v1/posts/jx58t2kqm4wr9v3n7c1zp6bs0dh5fg8y/reactions" \
-H "Authorization: Bearer $POSTZEN_API_KEY"A comments response carries the comment body, the author URN, and — when LinkedIn returns them — the author's display name and like and reply counts:
{
"comments": [
{
"id": "7123456789012345679",
"commentUrn": "urn:li:comment:(urn:li:share:7123456789012345678,7123456789012345679)",
"text": "Congratulations on the launch!",
"authorUrn": "urn:li:person:AbC1dEfGhI",
"authorName": "Jane Doe",
"createdAt": "2026-07-29T16:04:11.000Z",
"likeCount": 4,
"replyCount": 1
}
],
"nextCursor": "25"
}A reactions response carries the individual reactions plus a per-type tally:
{
"reactions": [
{ "reactionType": "LIKE", "actorUrn": "urn:li:person:AbC1dEfGhI", "createdAt": "2026-07-29T16:02:47.000Z" },
{ "reactionType": "PRAISE", "actorUrn": "urn:li:person:JkL2mNoPqR", "createdAt": "2026-07-29T16:03:02.000Z" }
],
"totalsByType": { "LIKE": 1, "PRAISE": 1 },
"nextCursor": "25"
}totalsByType counts only the reactions in that response, so sum the pages yourself for a whole-post total. When nextCursor is present, pass it back as ?cursor= to fetch the next page — the default page size is 25 and the maximum is 100. Reaction types are an open set; LinkedIn adds new ones over time, so do not hard-code the list.
Failures carry a machine-readable code alongside the message:
| Status | code | Meaning |
|---|---|---|
403 | personalPostUnsupported | The post was authored by a member, not a company page. |
403 | forbidden | The API key cannot access the post's profile. |
403 | orgScopesDisabled | Organization engagement reads are not enabled on this deployment. |
403 | platformCapabilityMissing | The connection is missing r_organization_social_feed. Reconnect the account. |
404 | notFound | No such post, not visible to this key, or it has no LinkedIn target. |
409 | postNotPublished | The LinkedIn target has not published yet. |
424 | notConnected | The LinkedIn account behind the post is no longer connected. |
429 | rateLimited | LinkedIn rate limited the upstream request. |
502 | requestFailed | LinkedIn could not answer the request. |
What you can't do
- Mix media kinds in one post. A LinkedIn post is text, 1–20 images, one video, or one document — never a combination.
- Attach media to a reshare.
reshareUrlandmediaItemsare mutually exclusive. - Use
CONNECTIONSvisibility on a company page. Company-page posts are alwaysPUBLIC. - Restrict a personal post by country.
geoRestrictionCountriesrequiresorganizationUrn. - Read comments or reactions on a personal post. LinkedIn has no API for member-post engagement.
- Publish polls, articles, newsletters, or events.
- Read or send DMs or InMail, or manage an inbox.
- Reply to, moderate, or delete comments. The comment endpoints are read-only, and
firstCommentis the only comment PostZen writes. - Edit or delete a published post.
- Read analytics or insights yet. Post metrics are coming soon, pending LinkedIn review, and will cover only posts published through PostZen — LinkedIn does not let applications list a member's other posts. Company-page posts will report every metric except saves, and page follower counts are not collected.
- Manage ads or receive engagement webhooks. Best-time-to-post suggestions arrive with analytics.
Common errors
| Error | Meaning | Fix |
|---|---|---|
| LinkedIn posts cannot mix images, video, and documents | One target attaches more than one media kind. | Split the media across separate posts. |
| LinkedIn supports up to 20 images | The target attaches more than 20 images. | Reduce mediaItems to 20 images or fewer. |
| Image over 8 MB | An image exceeds LinkedIn's per-image limit. | Resize or compress the image to 8 MB or smaller. |
| LinkedIn videos must be MP4 | The attached video is not an MP4. | Re-encode the video to MP4 before uploading. |
| LinkedIn videos are limited to 500 MB | The video exceeds LinkedIn's feed ceiling. | Compress the file below 500 MB. |
| LinkedIn videos must be at least 3 seconds long | The video is shorter than LinkedIn's minimum. | Use a video of at least 3 seconds. |
| LinkedIn videos must be 10 minutes or shorter | A personal-profile video exceeds the member ceiling. | Trim the video, or publish it as a company page (30 minutes). |
| LinkedIn organization videos must be 30 minutes or shorter | A company-page video exceeds the page ceiling. | Trim the video to 30 minutes or less. |
| LinkedIn documents are limited to 100 MB | The document exceeds LinkedIn's limit. | Compress the file below 100 MB. |
| LinkedIn documents must be a PDF, DOC, DOCX, PPT, or PPTX file | The attached file is not a supported document format. | Convert the file to one of the supported formats. |
| LinkedIn document posts require a documentTitle | Neither documentTitle nor a filename was available. | Set settings.documentTitle. |
| LinkedIn organizationUrn must look like "urn:li:organization:12345" | The value is neither a valid organization URN nor a bare numeric id. | Use urn:li:organization:<digits> or the numeric page id. |
| LinkedIn organization posts cannot use CONNECTIONS visibility | visibility: "CONNECTIONS" was combined with organizationUrn. | Use PUBLIC, or drop organizationUrn. |
| LinkedIn first comment must be 1,250 characters or fewer | firstComment exceeds the comment composer limit. | Shorten the comment to 1,250 characters. |
| A LinkedIn reshare cannot include uploaded media | reshareUrl was combined with mediaItems. | Send the reshare and the media as separate posts. |
| LinkedIn reshareUrl must be a post URL or a urn:li:activity / urn:li:share / urn:li:ugcPost URN | The reshare reference could not be parsed. | Copy the post's permalink from LinkedIn, or pass its URN. |
| LinkedIn geo targeting requires organizationUrn | geoRestrictionCountries was set on a personal post. | Add organizationUrn, or drop the country list. |
| LinkedIn geo targeting supports up to 25 countries | More than 25 country codes were supplied. | Reduce the list to 25 codes. |
| LinkedIn geoRestrictionCountries must be uppercase ISO 3166-1 alpha-2 codes | A code is not a two-letter uppercase country code. | Use codes such as US, CA, GB. |
| Text over 3,000 characters | The post exceeds LinkedIn's commentary limit. | Shorten content, or set a shorter customContent on the LinkedIn target. |
| Company pages are missing from the picker | The connection predates the organization scopes, or the deployment has company-page posting disabled. | Run the connect flow again to grant the new scopes. |
402 while connecting | The free-tier limit of 2 connected accounts has been reached and a payment method is required. | Add a payment method before connecting another account. |
| OAuth state expired | The connect flow was not completed within the 10-minute state lifetime. | Request a new LinkedIn connect URL and complete OAuth within 10 minutes. |