# LinkedIn

Publish text, images, video, documents, polls and reposts to LinkedIn profiles and company pages, target page audiences, edit posts and read analytics.

One LinkedIn connection can post as the member's personal profile or as any company page they administer. You can publish text, images, video, PDF and slide documents, polls and reposts, tag people and companies, target page posts at specific audiences, and edit the text after publishing. The platform id is `linkedin`.

## What you can do

| Capability | Supported | Where |
| --- | --- | --- |
| Publish | Text, up to 20 images, one video, one document, polls, reposts | [Create a post](https://pozy.ai/docs/publishing) |
| Schedule | Yes | [Scheduling](https://pozy.ai/docs/publishing/scheduling) |
| Edit after publishing | Text, no time limit. [Unpublish](https://pozy.ai/docs/api/unpublishPost) removes the post. | [Edit published posts](https://pozy.ai/docs/publishing/editing) |
| Mentions | People and companies by profile URL | [Mentions](#mentions) |
| Analytics | Post metrics for profiles and pages, page follower and visitor stats, demographics, reactions | [Platform insights](https://pozy.ai/docs/analytics/platform-insights) |
| Comments | Company pages: list, reply, delete. Reactions on comments and posts from either account type. | [Comments](https://pozy.ai/docs/inbox/comments) |
| DMs | No. LinkedIn does not open messaging to third-party apps. | |
| Ads | Through a LinkedIn Ads connection | [LinkedIn Ads](https://pozy.ai/docs/ads/linkedin-ads) |

## Connect

Start with [`GET /v1/connect/linkedin`](https://pozy.ai/docs/api/getConnectUrl) and a `profileId`. After LinkedIn's consent screen, the user chooses whether this account posts as their personal profile or as one of the company pages they administer. Pozy hosts that choice by default; headless mode lets you build it with [List LinkedIn orgs](https://pozy.ai/docs/api/listLinkedInOrganizations) and [Select LinkedIn org](https://pozy.ai/docs/api/selectLinkedInOrganization).

To switch an existing account between personal and page posting later, without another login, call [Switch LinkedIn account type](https://pozy.ai/docs/api/updateLinkedInOrganization).

The two account types behave differently:

| | Personal profile | Company page |
| --- | --- | --- |
| Video length | Up to 10 minutes | Up to 30 minutes |
| Analytics | Posts published through Pozy only, plus saves and sends | Every post, clicks, follower and page-view stats |
| Comments API | Reactions only | Read, reply, delete |
| Audience targeting and geo-restriction | No | Yes |

LinkedIn asks for a broad set of permissions because one connection covers profile posting, pages and ads. Accounts connected before Pozy added the comment and reaction permissions get `403` `linkedin_reconnect_required` on those calls until they reconnect.

## Post types

A plain request makes a text post. Media in `mediaItems` makes an image, video or document post. A post carries one media type only.

```bash cURL
curl -X POST https://api.pozy.ai/v1/posts \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "We cut our onboarding from 14 steps to 4.\n\nThe three things that mattered most are in the comments.",
    "platforms": [{
      "platform": "linkedin",
      "accountId": "6720c1a94f3b2e0018d4a7f5",
      "platformSpecificData": {
        "firstComment": "Full write-up: https://example.com/blog/onboarding"
      }
    }],
    "publishNow": true
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "We cut our onboarding from 14 steps to 4.\n\nThe three things that mattered most are in the comments.",
    platforms: [{
      platform: "linkedin",
      accountId: "6720c1a94f3b2e0018d4a7f5",
      platformSpecificData: { firstComment: "Full write-up: https://example.com/blog/onboarding" },
    }],
    publishNow: true,
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);

console.log(data.post.platforms[0].platformPostUrl);
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/posts",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={
        "content": "We cut our onboarding from 14 steps to 4.\n\nThe three things that mattered most are in the comments.",
        "platforms": [{
            "platform": "linkedin",
            "accountId": "6720c1a94f3b2e0018d4a7f5",
            "platformSpecificData": {"firstComment": "Full write-up: https://example.com/blog/onboarding"},
        }],
        "publishNow": True,
    },
)
print(resp.json()["post"]["platforms"][0]["platformPostUrl"])
```

Putting links in `firstComment` rather than the post body is a common LinkedIn habit, because posts with links in the text tend to get less reach.

| Post type | How to send it |
| --- | --- |
| Text | `content`, up to 3,000 characters. A URL with no media gets a preview card unless `disableLinkPreview: true`. |
| Images | 1 to 20 images in `mediaItems` |
| Video | One video. Optional `thumbnail` on the item (best effort). |
| Document | One PDF, PPT, PPTX, DOC or DOCX as `type: "document"`, shown as a swipeable carousel. Set `documentTitle`. |
| Poll | `poll` with a question and 2 to 4 options. No media. |
| Repost | `reshareUrl` with the post link or URN. No media. |

### Document post

LinkedIn requires a title on documents. Pozy uses `documentTitle`, then the media item's `title`, then the filename. The first page is the cover. Password-protected PDFs do not work.

```json
"mediaItems": [{ "type": "document", "url": "https://cdn.example.com/2027-benchmarks.pdf" }],
"platforms": [{
  "platform": "linkedin",
  "accountId": "6720c1a94f3b2e0018d4a7f5",
  "platformSpecificData": { "documentTitle": "2027 hiring benchmarks" }
}]
```

### Poll

A question of 1 to 140 characters, 2 to 4 options of up to 30 characters each, and a `duration` of `ONE_DAY`, `THREE_DAYS`, `SEVEN_DAYS` (default) or `FOURTEEN_DAYS`. Polls cannot be edited after publishing and cannot be promoted as ads.

```json
"platformSpecificData": {
  "poll": {
    "question": "How does your team run standups?",
    "options": ["Daily, live", "Async in chat", "We don't"],
    "duration": "THREE_DAYS"
  }
}
```

### Repost

`reshareUrl` takes a post link (from "Copy link to post") or a `urn:li:share`, `urn:li:ugcPost` or `urn:li:groupPost` URN. Your `content` becomes the commentary on top; with no text it is a plain reshare. The repost is always authored by your account with the original embedded. LinkedIn's API cannot produce the one-click repost where the original author stays the author.

### Post to several company pages

List the pages the member administers with [List LinkedIn orgs](https://pozy.ai/docs/api/getLinkedInOrganizations), then repeat the entry with the same `accountId` and a different `organizationUrn` (`urn:li:organization:` plus the id). Entries without `organizationUrn` go to the account's default.

### Audience targeting and geo-restriction

On company pages, `audience` limits who sees the post: by country, region, industry, job function, seniority, company size, interface language, degree, field of study or school. Values inside one facet are combined with OR, and facets are combined with AND. LinkedIn rejects the post if the target audience is under 300 members. Find ids with [Search targeting options](https://pozy.ai/docs/api/searchAdTargeting) using `platform=linkedin`.

```json
"platformSpecificData": {
  "organizationUrn": "urn:li:organization:84512033",
  "audience": {
    "countries": ["ES", "DE"],
    "jobFunctions": ["urn:li:function:8"],
    "seniorities": ["urn:li:seniority:6"],
    "staffCountRanges": ["SIZE_51_TO_200", "SIZE_201_TO_500"]
  }
}
```

`geoRestriction.countries` (up to 25 ISO codes) is the simpler form: only followers in those countries see the post. It is merged with `audience.countries`, and codes LinkedIn cannot map are skipped.

### Platform fields

Schema: `LinkedInPlatformData`.

| Field | Notes |
| --- | --- |
| `organizationUrn` | Company page to post as. |
| `documentTitle` | Title for document posts. |
| `firstComment` | Comment posted right after publishing. Mentions in it are posted as plain names. |
| `disableLinkPreview` | `true` suppresses the automatic link card. |
| `poll` | `{ question, options, duration }`. Not with media or `reshareUrl`. |
| `reshareUrl` | Post link or URN to repost. Not with media. |
| `audience` | Page audience targeting (see above). |
| `geoRestriction` | `{ countries }`, pages only. |

## Mentions

Write a mention as a profile or company URL inside `@[...]` in `content` or in the LinkedIn entry's `customContent`:

```text
Thanks to @[https://www.linkedin.com/in/maria-lopez] and the team at @[https://www.linkedin.com/company/acme] for the help.
```

Pozy resolves each URL and publishes LinkedIn's own name as the link text. You can choose the visible text with `@[Maria](https://www.linkedin.com/in/maria-lopez)`, but it must be the person's first, last or full name (or the company's exact name), case-sensitive; anything else is replaced and a `warnings` entry comes back.

- To resolve ahead of time, call [Resolve LinkedIn mention](https://pozy.ai/docs/api/getLinkedInMentions) and paste the returned `@[Name](urn:li:person:...)` markup, which is published exactly as written.
- Looking up a person by URL needs one of your LinkedIn accounts to administer a page. Company lookups do not.
- A post can resolve up to 10 mentions (`linkedin_mentions_limit_exceeded`). An unresolvable URL rejects a scheduled or published post with `linkedin_mentions_invalid` and details per mention; drafts save and return warnings instead.
- Other platforms in the same post get plain text: the vanity name, or your custom text.

## Edit a published post

[Edit published post](https://pozy.ai/docs/api/editPost) with `platform: "linkedin"` replaces the text of a live post on a profile or page. Up to 3,000 characters, no time window, no edit limit. The post keeps its id and LinkedIn shows an "edited" marker. Media, polls and repost targets cannot change. Pass `accountId` when the post went to several LinkedIn accounts.

```bash
curl -X POST https://api.pozy.ai/v1/posts/6721a3b84f3b2e0018d4b902/edit \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "linkedin", "accountId": "6720c1a94f3b2e0018d4a7f5", "content": "We cut our onboarding from 14 steps to 4 (updated with Q3 numbers)." }'
```

## Media requirements

Use direct file URLs; sharing links from cloud drives return a web page and fail.

**Images:** JPEG, PNG or GIF, up to 8 MB each, up to 20 per post, 552 x 276 to 8192 x 8192 px. Good sizes are 1200 x 627 (landscape), 1080 x 1080 (square) and 1080 x 1350 (portrait).

**Video**

| Property | Requirement |
| --- | --- |
| Formats | MP4, MOV, AVI |
| Max size | 5 GB |
| Duration | 3 seconds to 10 minutes (profile) or 30 minutes (page) |
| Resolution | 256 x 144 to 4096 x 2304 |
| Aspect ratio | 1:2.4 to 2.4:1 |
| Frame rate | 10 to 60 fps |
| Recommended | 1920 x 1080, 30 fps, H.264, AAC 192 kbps |

**Documents:** PDF, PPT, PPTX, DOC or DOCX, up to 100 MB and 300 pages, one per post.

## Limits

| Limit | Value |
| --- | --- |
| Post text | 3,000 characters |
| Images per post | 20 |
| Mentions resolved per post | 10 |
| Poll question / option | 140 / 30 characters |
| Minimum targeted audience | 300 members |

LinkedIn rejects duplicate content with `422`, even when the earlier post is weeks old, and a light rephrase is often not enough. The API cannot publish long-form articles, newsletters or events, send InMail, or mix images with a video or document.

## Analytics

[`GET /v1/analytics?platform=linkedin`](https://pozy.ai/docs/api/getAnalytics) returns `impressions`, `reach`, `likes`, `comments` and `shares` for every post. `saves` (and sends) exist for personal profiles only, `clicks` for pages only, and `views` for video posts only. For personal profiles LinkedIn only reports posts the member published through Pozy.

| Endpoint | What you get |
| --- | --- |
| [LinkedIn aggregate stats](https://pozy.ai/docs/api/getLinkedInAggregateAnalytics) | Totals or a daily series across a personal profile's posts. |
| [LinkedIn org analytics](https://pozy.ai/docs/api/getLinkedInOrgAggregateAnalytics) | Page impressions, clicks, reactions, engagement rate, organic and paid follower gains, page views. The member must be a page administrator. |
| [LinkedIn post stats](https://pozy.ai/docs/api/getLinkedInPostAnalytics) | Per-post metrics by URN, for either account type. |
| [Post reactions](https://pozy.ai/docs/api/getLinkedInPostReactions) | Individual reactions with reactor profiles. Pages only. |
| [Follower demographics](https://pozy.ai/docs/api/getLinkedInDemographics) | Page follower demographics, one `breakdown` per call. |

## Inbox

There are no LinkedIn DMs. On company pages you can list comments, reply and delete through [Comments](https://pozy.ai/docs/inbox/comments). [Like comment](https://pozy.ai/docs/api/likeInboxComment) takes the comment URN the comments endpoints return, plus an optional `reactionType` instead of the default like. [Like post](https://pozy.ai/docs/api/likePost) reacts to any post from any connected LinkedIn account, so a founder's profile can react to the company's post.

Comments are picked up by polling, so a comment automation reply can lag by a few minutes.

## Errors you may see

| Message | Cause | Fix |
| --- | --- | --- |
| Content is a duplicate of urn:li:share:... (`422`) | Same or very similar text already posted | Change the text substantially. |
| Cannot mix media types | Images with a video or a document | One media type per post. |
| Video processing failed | Codec, length or shape out of range | H.264, within the duration limit, ratio between 1:2.4 and 2.4:1. |
| `linkedin_reconnect_required` (`403`) | Account connected before reaction permissions existed | Reconnect. |
| `linkedin_mentions_invalid` | A mention URL could not be resolved | Check the URLs in `details.mentions`. |
| Publishing failed during preflight checks | Posting too fast, or a validation problem | Space posts out and check [account health](https://pozy.ai/docs/connect/account-health). |

A rejected `publishNow: true` post returns `207` with `post.status: "failed"` and the reason in `platforms[].errorMessage`.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/accounts/{accountId}/linkedin-mentions`](https://pozy.ai/docs/api/getLinkedInMentions) | Resolve LinkedIn mention |
| [`GET /v1/connect/linkedin/organizations`](https://pozy.ai/docs/api/listLinkedInOrganizations) | List LinkedIn orgs |
| [`POST /v1/connect/linkedin/select-organization`](https://pozy.ai/docs/api/selectLinkedInOrganization) | Select LinkedIn org |
| [`GET /v1/accounts/{accountId}/linkedin-organizations`](https://pozy.ai/docs/api/getLinkedInOrganizations) | List LinkedIn orgs |
| [`PUT /v1/accounts/{accountId}/linkedin-organization`](https://pozy.ai/docs/api/updateLinkedInOrganization) | Switch LinkedIn account type |
| [`GET /v1/accounts/{accountId}/linkedin-aggregate-analytics`](https://pozy.ai/docs/api/getLinkedInAggregateAnalytics) | Get LinkedIn aggregate stats |
| [`GET /v1/analytics/linkedin/org-aggregate-analytics`](https://pozy.ai/docs/api/getLinkedInOrgAggregateAnalytics) | Get LinkedIn org analytics |
| [`GET /v1/accounts/{accountId}/linkedin-post-analytics`](https://pozy.ai/docs/api/getLinkedInPostAnalytics) | Get LinkedIn post stats |
| [`GET /v1/accounts/{accountId}/linkedin-post-reactions`](https://pozy.ai/docs/api/getLinkedInPostReactions) | Get LinkedIn post reactions |
| [`GET /v1/analytics/linkedin/demographics`](https://pozy.ai/docs/api/getLinkedInDemographics) | Get LinkedIn organization follower demographics |
| [`POST /v1/posts/{postId}/edit`](https://pozy.ai/docs/api/editPost) | Edit published post |
| [`POST /v1/inbox/comments/{postId}/{commentId}/like`](https://pozy.ai/docs/api/likeInboxComment) | Like comment |
| [`POST /v1/inbox/posts/{postId}/like`](https://pozy.ai/docs/api/likePost) | Like post |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`post.published`](https://pozy.ai/docs/webhooks/events#post-published) | Post published. |
| [`post.failed`](https://pozy.ai/docs/webhooks/events#post-failed) | Post failed. |
| [`post.platform.published`](https://pozy.ai/docs/webhooks/events#post-platform-published) | Post platform published. |
| [`post.platform.failed`](https://pozy.ai/docs/webhooks/events#post-platform-failed) | Post platform failed. |
| [`post.platform.deleted`](https://pozy.ai/docs/webhooks/events#post-platform-deleted) | Post platform deleted. |
| [`account.disconnected`](https://pozy.ai/docs/webhooks/events#account-disconnected) | Account disconnected. |
| [`comment.received`](https://pozy.ai/docs/webhooks/events#comment-received) | Comment received. |
