# LinkedIn API: Posting Reference

LinkedIn appears as two independent channels: one for a personal profile and one for a company page. A single workspace can connect both at the same time and post to each separately.

| Channel ID | Target |
|------------|--------|
| `linkedin` | Personal profile (the individual who connected the account) |
| `linkedin_page` | Company page (the organization whose page was selected during OAuth) |

Both channels share a single LinkedIn OAuth token under the hood. Reconnecting either refreshes credentials for both.

## Supported content types

| Type | `linkedin` (Profile) | `linkedin_page` (Page) |
|------|----------------------|------------------------|
| Feed post | ✅ | ✅ |
| Story | - | - |
| Reel | - | - |

LinkedIn does not support stories or reels through its API. Only feed posts work.

## Minimal example

**Posting to a personal profile:**

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": { "default": "Excited to share our latest update." },
    "accounts": ["your-linkedin-profile-account-id"]
  }'
```

**Posting to a company page:**

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": { "default": "Company announcement." },
    "accounts": ["your-linkedin-page-account-id"]
  }'
```

**Posting the same content to both simultaneously:** include both account IDs in the `accounts` array. OmniSocials publishes to each independently.

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": {
      "default": "Same content on both",
      "linkedin": "Personal voice for the profile",
      "linkedin_page": "Official company announcement"
    },
    "accounts": [
      "your-linkedin-profile-account-id",
      "your-linkedin-page-account-id"
    ]
  }'
```

## Platform-specific options

Set a first comment under the channel's own key: `linkedin` for the profile, `linkedin_page` for the company page:

| Field | Type | Description |
|-------|------|-------------|
| `linkedin.first_comment` | string | Text auto-posted as the first comment on the profile post right after it publishes (max 1250 characters). Common for "link in first comment" to avoid the in-caption link reach penalty. |
| `linkedin_page.first_comment` | string | Same, for the company page post (max 1250 characters). |
| `linkedin_poll` | object | Non-sponsored poll; see [Polls](#polls) below. |
| `video_cover` | object | Top-level. Video thumbnail for a video post, applied to both channels; use `video_cover.overrides.linkedin` or `.linkedin_page` to give one channel its own frame or image. See [Video covers](/creating-posts#video-covers). |

Aside from the optional first comment and poll, LinkedIn has no required option fields; the default create-post body works for both channels.

Video thumbnails go through LinkedIn's Videos API (`initializeUpload` with `uploadThumbnail`, then the image is uploaded before the video is finalized). A thumbnail that cannot be uploaded never fails the post; LinkedIn then picks its own frame.

## Polls

LinkedIn's Poll API lets you post a non-sponsored poll: a question with 2-4 answer options and a duration. Pass a top-level `linkedin_poll` object keyed by channel: `linkedin` (personal profile) and `linkedin_page` (company page) each carry their own **independent** poll, or none. `content` is still the post's caption, shown above the poll.

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": { "default": "What should we build next?" },
    "accounts": ["your-linkedin-account-id"],
    "linkedin_poll": {
      "linkedin": {
        "question": "What should we build next?",
        "options": ["Mobile app", "Public API", "More integrations"],
        "duration": "SEVEN_DAYS"
      }
    }
  }'
```

| Field | Type | Description |
|-------|------|--------------|
| `linkedin_poll.linkedin` | object \| null | Poll for the personal profile post. Omit or null = not a poll. |
| `linkedin_poll.linkedin_page` | object \| null | Poll for the company page post. Omit or null = not a poll. |

Each poll object:

| Field | Type | Description |
|-------|------|--------------|
| `question` | string | The poll question (max 140 characters). |
| `options` | string[] | 2-4 answer options (max 30 characters each). |
| `duration` | string | `ONE_DAY` \| `THREE_DAYS` \| `SEVEN_DAYS` \| `FOURTEEN_DAYS`. |

A poll is mutually exclusive with media and a link share on that channel's post; if the request also carries `media_urls`/`media_ids` or a link share, the poll silently takes priority at publish time (same precedence LinkedIn's own media-vs-article rule uses), so don't send both.

Post a **different** poll to the profile and the page in one call by setting both keys:

```json
{
  "linkedin_poll": {
    "linkedin": { "question": "What should WE build?", "options": ["A", "B"], "duration": "SEVEN_DAYS" },
    "linkedin_page": { "question": "What should our COMPANY build?", "options": ["A", "B", "C"], "duration": "FOURTEEN_DAYS" }
  }
}
```

`linkedin_poll` is also accepted on `PATCH /posts/{id}`; it replaces the whole object wholesale (both channels), so send the full desired state, not just the channel you're changing. `GET /posts/{id}` echoes it back. Set a channel's key to `null` (or send `linkedin_poll: null`) to clear that channel's poll (or every poll) and revert to a normal post.

## Media requirements

| Media | Requirement |
|-------|-------------|
| Image | JPEG or PNG, max 10 MB |
| Video | MP4 or MOV, up to 10 minutes, max 5 GB |

LinkedIn supports text-only posts, a single image, a multi-image carousel, or video. Mixing image and video in the same post is not supported.

Posting 2 or more images publishes LinkedIn's native document carousel: OmniSocials renders the images into a single PDF and uploads it as a document, the same swipeable format LinkedIn's own composer produces, instead of a plain multi-image gallery. There's no image-count limit enforced on our side.

Prefer the plain multi-image gallery for a specific post? Set `carousel_as_images: true` on the `linkedin` and/or `linkedin_page` option object (the same objects that carry `first_comment`). The flag is independent per channel and is ignored for text-only posts, single images, videos, and polls:

```json
{
  "content": { "default": "Five looks from the spring shoot" },
  "accounts": ["your-linkedin-account-id"],
  "media_urls": ["https://example.com/1.jpg", "https://example.com/2.jpg"],
  "linkedin": { "carousel_as_images": true }
}
```

On `PATCH /posts/{id}` the flag merges per channel: `true` sets it, `false` (or `null`) reverts that channel to the default document carousel, and omitting it keeps the current value. `GET /posts/{id}` echoes it back inside each channel's option object.

## Alt text

Pass an accessibility description per media item by using an object entry instead of a bare string (works in `media_urls` and `media_ids`). It is sent as LinkedIn's `altText` on images: both single-image posts and multi-image galleries, where each image carries its own description. Video and documents don't support alt text (a LinkedIn limitation):

```json
{
  "content": { "default": "Team offsite highlights" },
  "accounts": ["your-linkedin-account-id"],
  "media_urls": [
    { "url": "https://example.com/team.jpg", "alt": "Six people around a whiteboard covered in sticky notes" }
  ]
}
```

The same `alt` also delivers to Mastodon, Bluesky, X, Pinterest, and Instagram (images), so one description covers a cross-platform post.

## Mentions

LinkedIn mentions must reference either another LinkedIn member or a company, using the URN format the LinkedIn API expects. OmniSocials exposes mention handling through the `mentions` field on the post body. See the API reference for the full shape.

## Limitations

- Stories and reels are not available through LinkedIn's public API
- LinkedIn enforces a 3,000 character limit per post. Posts exceeding this are rejected.
- Posting to company pages requires the connecting user to be an admin of the page
- The personal profile channel (`linkedin`) requires the Community Management API product on LinkedIn Developer, which is automatically configured for OmniSocials customers
