PostPost

LinkedIn

Post to LinkedIn programmatically using the PostPost REST API. A simpler alternative to the LinkedIn Marketing API, LinkedIn Share API, or LinkedIn Community Management API.

LinkedIn API Overview

PostPost provides a unified REST API for professional content publishing to LinkedIn, including text posts, media attachments (images and videos), analytics retrieval (impressions, reactions, comments), and reaction management. No need to manage LinkedIn OAuth flows, handle the LinkedIn API partner program requirements, or implement complex share creation endpoints.

Why Use PostPost Instead of LinkedIn Marketing API?

FeaturePostPost APILinkedIn Marketing API
AuthenticationSingle API keyComplex OAuth 2.0 flow
API accessInstantLinkedIn partner approval
AnalyticsBuilt-inSeparate API calls
Multi-platformPost to 11 platformsLinkedIn only
Setup time5 minutesWeeks (partner approval)
ReactionsFull supportFull support

Keywords: LinkedIn API, LinkedIn posting API, LinkedIn Share API, LinkedIn Marketing API, post to LinkedIn programmatically, LinkedIn REST API, LinkedIn developer API, LinkedIn automation API, LinkedIn content API, LinkedIn bot API, LinkedIn analytics API, LinkedIn UGC API

Platform ID Format

linkedin-{profileId}

Where {profileId} is your LinkedIn profile identifier assigned during account connection via OAuth.

Requirements

  • A LinkedIn account connected via OAuth through the PostPost dashboard
  • API key from PostPost (Pro or Premium plan required)

Note: The Starter plan has apiAccess: false. API key authentication requires a Pro or Premium plan.

Supported Content

TypeSupportedLimits
TextYes3,000 characters
ImagesYesJPEG, PNG (WebP auto-converted to JPEG), multiple supported
VideosYesMP4 format
AnalyticsYesIMPRESSION, MEMBERS_REACHED, RESHARE, REACTION, COMMENT
ReactionsYesLIKE, PRAISE, EMPATHY, INTEREST, APPRECIATION, ENTERTAINMENT
CommentsYesCreate, delete, reply (1,250 characters max)
MentionsYes@mention people and organizations

API Limits

Character Limit

  • 3,000 characters maximum per post
  • First 210 characters visible before "see more" is shown

Image Limits (API)

PropertyLimit
Max size5 MB
Max count10 (multi-image posts)
FormatsJPEG, PNG, GIF, WebP (WebP images are auto-converted to JPEG before upload to LinkedIn)

Note: GIF images are passed through without conversion — if the LinkedIn API rejects a GIF, consider converting to JPEG/PNG before uploading.

Important: Organic carousels (swipeable multi-image posts) are NOT supported via the API. The API only supports multi-image grid layouts. Swipeable carousels are only available for sponsored content.

Video Limits (API)

PropertyLimit
Duration30 minutes (more than native's 15 min desktop / 10 min mobile)
Max size500 MB (native allows 5 GB)
FormatsMP4 only

Important API Restrictions

  • Cannot mix media types: Images cannot be combined with videos or documents in the same post
  • No organic carousels: Swipeable multi-image posts are not available via API — only multi-image grid layout
  • Rate limit: 200+ API calls per hour, based on user count

Common Error Messages

ErrorCause
MEDIA_ASSET_PROCESSING_FAILEDFile too large or unsupported format
Error 429Rate limit exceeded

Mentioning People and Organizations

PostPost supports @mentioning LinkedIn members and organizations in your posts. When the post is published, the mention becomes a clickable link and the mentioned person/organization receives a notification.

Mention Syntax

Use the following format in your post content:

@{urn:li:person:MEMBER_ID|Display Name}       # Mention a person
@{urn:li:organization:ORG_ID|Company Name}    # Mention an organization

Examples

Mentioning a person:

Great insights from @{urn:li:person:12345678|John Doe} on building APIs!

Result: Great insights from @John Doe on building APIs!

Mentioning a company:

Excited to work with @{urn:li:organization:12345678|Acme Corp}!

Result: Excited to work with @Acme Corp!

Both mentions will be rendered as clickable links on LinkedIn.

How to Find LinkedIn URNs

  • Person URN: The numeric ID from LinkedIn's API. You can find this via LinkedIn's API or third-party tools. The format is urn:li:person:{numeric_id}.
  • Organization URN: Found in the company page URL or via LinkedIn's API. Format: urn:li:organization:{numeric_id}.

Note: PostPost automatically converts the person URN format for LinkedIn's API compatibility.

Important: Name Matching Requirements

The display name must exactly match the name on LinkedIn (case-sensitive):

  • For people: Use their exact name as shown on their LinkedIn profile
  • For organizations: Use the exact registered company name including suffixes like "Inc", "LLC", etc.
CorrectIncorrect
@{urn:li:organization:123|Acme Corp Inc}@{urn:li:organization:123|Acme Corp}
@{urn:li:person:456|John Smith}@{urn:li:person:456|john smith}

If the name doesn't match exactly, the mention will appear as plain text instead of a clickable link.

Code Example

JavaScript

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: 'Excited to collaborate with @{urn:li:person:12345678|John Doe} on this project!',
    platforms: ['linkedin-987654321']
  })
});

Python

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': 'Excited to collaborate with @{urn:li:person:12345678|John Doe} on this project!',
        'platforms': ['linkedin-987654321']
    }
)

Analytics

PostPost can retrieve analytics for your LinkedIn posts. Available metrics:

MetricDescription
IMPRESSIONNumber of times the post was displayed
MEMBERS_REACHEDUnique LinkedIn members who saw the post
RESHARENumber of times the post was shared
REACTIONTotal reactions on the post
COMMENTNumber of comments on the post

Reactions

LinkedIn supports a richer set of reactions than a simple "like":

ReactionDescription
LIKEStandard like (thumbs up)
PRAISEClapping hands / applause
EMPATHYHeart / love
INTERESTLightbulb / insightful
APPRECIATIONSupportive
ENTERTAINMENTFunny / laughing

Create a Reaction

Endpoint: POST /api/v1/linkedin-reactions

ParameterTypeRequiredDescription
postedIdstringYesLinkedIn post URN (e.g., urn:li:share:123 or urn:li:ugcPost:123)
reactionTypestringYesOne of: LIKE, PRAISE, EMPATHY, INTEREST, APPRECIATION, ENTERTAINMENT
platformIdstringYesYour LinkedIn platform ID (e.g., linkedin-ABC123)

JavaScript

const response = await fetch('https://api.postpost.dev/api/v1/linkedin-reactions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    postedId: 'urn:li:ugcPost:7429953213384187904',
    reactionType: 'INTEREST',
    platformId: 'linkedin-987654321'
  })
});

const data = await response.json();
// HTTP 201 Created
console.log(data);
// {
//   success: true,
//   reaction: {
//     id: "urn:li:reaction:(urn:li:person:xxx,urn:li:ugcPost:xxx)",
//     reactionType: "INTEREST",
//     ...
//   },
//   urnTranslated: { from: "original-urn", to: "translated-urn" }  // present when URN translation was needed
// }

Note: The create reaction endpoint returns HTTP 201 (not 200). The response may include an urnTranslated field when the provided URN needed to be translated to a different format for the LinkedIn API.

cURL

curl -X POST https://api.postpost.dev/api/v1/linkedin-reactions \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "postedId": "urn:li:ugcPost:7429953213384187904",
    "reactionType": "INTEREST",
    "platformId": "linkedin-987654321"
  }'

Delete a Reaction

Endpoint: DELETE /api/v1/linkedin-reactions

ParameterTypeRequiredDescription
postedIdstringYesLinkedIn post URN
platformIdstringYesYour LinkedIn platform ID

cURL

curl -X DELETE https://api.postpost.dev/api/v1/linkedin-reactions \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "postedId": "urn:li:ugcPost:7429953213384187904",
    "platformId": "linkedin-987654321"
  }'

Note: The delete reaction response includes the reaction field set to null (not the deleted reaction type). It may also include an urnTranslated field ({ from: "original-urn", to: "translated-urn" }) when URN translation was needed.

Response example:

{
  "success": true,
  "reaction": null,
  "urnTranslated": { "from": "original-urn", "to": "translated-urn" }
}

Comments

PostPost supports creating and deleting comments on LinkedIn posts programmatically.

Create a Comment

Endpoint: POST /api/v1/linkedin-comments

ParameterTypeRequiredDescription
postedIdstringYesLinkedIn post URN (e.g., urn:li:share:123 or urn:li:ugcPost:123)
messagestringYesComment text (max 1,250 characters)
platformIdstringYesYour LinkedIn platform ID (e.g., linkedin-ABC123)
parentCommentstringNoComment URN for nested replies

JavaScript

const response = await fetch('https://api.postpost.dev/api/v1/linkedin-comments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    postedId: 'urn:li:share:7434685316856377344',
    message: 'Great post! Thanks for sharing.',
    platformId: 'linkedin-987654321'
  })
});

const data = await response.json();
// HTTP 201 Created
console.log(data);
// {
//   success: true,
//   comment: {
//     id: "7434695495614312448",
//     commentUrn: "urn:li:comment:(urn:li:activity:xxx,7434695495614312448)",
//     message: "Great post! Thanks for sharing.",
//     ...
//   }
// }

Note: The create comment endpoint returns HTTP 201 (not 200).

cURL

curl -X POST https://api.postpost.dev/api/v1/linkedin-comments \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "postedId": "urn:li:share:7434685316856377344",
    "message": "Great post! Thanks for sharing.",
    "platformId": "linkedin-987654321"
  }'

Delete a Comment

Endpoint: DELETE /api/v1/linkedin-comments

ParameterTypeRequiredDescription
postedIdstringYesLinkedIn post URN the comment belongs to
commentIdstringYesComment URN to delete (also accepts plain numeric IDs, e.g., 7434695495614312448)
platformIdstringYesYour LinkedIn platform ID

JavaScript

const response = await fetch('https://api.postpost.dev/api/v1/linkedin-comments', {
  method: 'DELETE',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    postedId: 'urn:li:share:7434685316856377344',
    commentId: 'urn:li:comment:(urn:li:activity:xxx,7434695495614312448)',
    platformId: 'linkedin-987654321'
  })
});

const data = await response.json();
console.log(data);
// { success: true, deleted: "urn:li:comment:(...)" }

cURL

curl -X DELETE https://api.postpost.dev/api/v1/linkedin-comments \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "postedId": "urn:li:share:7434685316856377344",
    "commentId": "urn:li:comment:(urn:li:activity:xxx,7434695495614312448)",
    "platformId": "linkedin-987654321"
  }'

Replying to a Comment

To reply to an existing comment (nested comment), include the parentComment parameter:

const response = await fetch('https://api.postpost.dev/api/v1/linkedin-comments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    postedId: 'urn:li:share:7434685316856377344',
    message: 'I agree with this point!',
    platformId: 'linkedin-987654321',
    parentComment: 'urn:li:comment:(urn:li:activity:xxx,7434695495614312448)'
  })
});

Examples

Post a Text Update

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: 'Excited to announce our Series A funding! We are building the future of social media management for developer teams. More details coming soon.',
    platforms: ['linkedin-987654321']
  })
});

const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123..." }

Python (requests)

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': 'Excited to announce our Series A funding! We are building the future of social media management for developer teams. More details coming soon.',
        'platforms': ['linkedin-987654321']
    }
)

data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123..." }

cURL

curl -X POST https://api.postpost.dev/api/v1/create-post \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "content": "Excited to announce our Series A funding! We are building the future of social media management for developer teams. More details coming soon.",
    "platforms": ["linkedin-987654321"]
  }'
# Response: { "success": true, "postGroupId": "abc123..." }

Node.js (axios)

const axios = require('axios');

const response = await axios.post('https://api.postpost.dev/api/v1/create-post', {
  content: 'Excited to announce our Series A funding! We are building the future of social media management for developer teams. More details coming soon.',
  platforms: ['linkedin-987654321']
}, {
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  }
});

console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123..." }

Post with an Image

JavaScript (fetch)

const response = await fetch('https://api.postpost.dev/api/v1/create-post', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    content: 'Our team just wrapped up an incredible hackathon weekend. Here are some highlights from the event!',
    platforms: ['linkedin-987654321']
  })
});

const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123..." }

Python (requests)

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/create-post',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'content': 'Our team just wrapped up an incredible hackathon weekend. Here are some highlights from the event!',
        'platforms': ['linkedin-987654321']
    }
)

data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123..." }

cURL

curl -X POST https://api.postpost.dev/api/v1/create-post \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "content": "Our team just wrapped up an incredible hackathon weekend. Here are some highlights from the event!",
    "platforms": ["linkedin-987654321"]
  }'
# Response: { "success": true, "postGroupId": "abc123..." }

Node.js (axios)

const axios = require('axios');

const response = await axios.post('https://api.postpost.dev/api/v1/create-post', {
  content: 'Our team just wrapped up an incredible hackathon weekend. Here are some highlights from the event!',
  platforms: ['linkedin-987654321']
}, {
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  }
});

console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123..." }

Note: To attach media to a LinkedIn post, first create the post, then upload media using the media upload workflow with the returned postGroupId.

Check Post Analytics

Endpoint: POST /api/v1/linkedin-post-statistics

ParameterTypeRequiredDescription
postedIdstringYesLinkedIn post URN (e.g., urn:li:share:123 or urn:li:ugcPost:123)
platformIdstringYesYour LinkedIn platform ID (e.g., linkedin-ABC123)
queryTypestringNoSingle metric to retrieve. Valid values: IMPRESSION, MEMBERS_REACHED, RESHARE, REACTION, COMMENT. ALL is only valid when used with the queryTypes array parameter, not with the singular queryType parameter. At least one of queryType or queryTypes is required. Without this parameter (and without queryTypes), the API returns 400: queryType or queryTypes is required.
queryTypesarray of stringsNoMultiple metrics to retrieve in one request. Same valid values as queryType. At least one of queryType or queryTypes is required.

Response format differs by request type:

  • Single metric (using queryType): Returns { success: true, count: 123, cached: true/false }
  • Multiple metrics (using queryTypes): Returns { success: true/false, metrics: { IMPRESSION: 4521, ... }, cached: true/false, cachedMetrics: [...], errors: [...] }

Multi-metric response fields:

  • cached (boolean): Whether any metrics were served from cache.
  • cachedMetrics (array): Only present when non-empty. Lists which metrics came from cache.
  • errors (array): Present when some metrics failed to fetch.
  • success is false when one or more metrics failed to fetch (i.e., errors array is non-empty). The metrics object is still populated with null for failed metrics.

JavaScript (fetch) — multiple metrics

const response = await fetch('https://api.postpost.dev/api/v1/linkedin-post-statistics', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    postedId: 'urn:li:share:7434685316856377344',
    platformId: 'linkedin-987654321',
    queryTypes: ['IMPRESSION', 'MEMBERS_REACHED', 'RESHARE', 'REACTION', 'COMMENT']
  })
});

const analytics = await response.json();
console.log(analytics);
// {
//   success: true,
//   metrics: {
//     IMPRESSION: 4521,
//     MEMBERS_REACHED: 3200,
//     RESHARE: 12,
//     REACTION: 89,
//     COMMENT: 15
//   }
// }

JavaScript (fetch) — single metric

const response = await fetch('https://api.postpost.dev/api/v1/linkedin-post-statistics', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    postedId: 'urn:li:share:7434685316856377344',
    platformId: 'linkedin-987654321',
    queryType: 'IMPRESSION'
  })
});

const analytics = await response.json();
console.log(analytics);
// { success: true, count: 4521, cached: false }

Python (requests)

import requests

response = requests.post(
    'https://api.postpost.dev/api/v1/linkedin-post-statistics',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': 'YOUR_API_KEY'
    },
    json={
        'postedId': 'urn:li:share:7434685316856377344',
        'platformId': 'linkedin-987654321',
        'queryTypes': ['IMPRESSION', 'MEMBERS_REACHED', 'RESHARE', 'REACTION', 'COMMENT']
    }
)

analytics = response.json()
print(analytics)

cURL

curl -X POST https://api.postpost.dev/api/v1/linkedin-post-statistics \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "postedId": "urn:li:share:7434685316856377344",
    "platformId": "linkedin-987654321",
    "queryTypes": ["IMPRESSION", "MEMBERS_REACHED", "RESHARE", "REACTION", "COMMENT"]
  }'

Node.js (axios)

const axios = require('axios');

const response = await axios.post('https://api.postpost.dev/api/v1/linkedin-post-statistics', {
  postedId: 'urn:li:share:7434685316856377344',
  platformId: 'linkedin-987654321',
  queryTypes: ['IMPRESSION', 'MEMBERS_REACHED', 'RESHARE', 'REACTION', 'COMMENT']
}, {
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'YOUR_API_KEY'
  }
});

console.log(response.data);

Additional Analytics Endpoints

Beyond post-level statistics, PostPost provides additional LinkedIn analytics endpoints:

  • Followers Statistics (POST /api/v1/linkedin-followers) — Retrieve follower demographics and growth data for your LinkedIn account. See the LinkedIn Followers endpoint docs.
  • Account Statistics (POST /api/v1/linkedin-account-statistics) — Get aggregate account-level statistics including total impressions, engagement, and reach. See the LinkedIn Statistics endpoint docs.
  • Profile Summary (POST /api/v1/linkedin-profile-summary) — Retrieve your LinkedIn profile summary information. See the LinkedIn Profile Summary endpoint docs.

Platform Quirks

  • URN formats differ: LinkedIn URLs use urn:li:activity:xxx but the API requires urn:li:share:xxx or urn:li:ugcPost:xxx. For posts created via PostPost, use the postedId from the get-post endpoint. For external posts, you may need to look up the correct URN format.
  • WebP auto-conversion: If you provide a WebP image URL, PostPost automatically converts it to JPEG before uploading to LinkedIn. No action needed on your part.
  • 3,000-character limit: LinkedIn enforces a strict 3,000-character limit for post text. PostPost will return an error if your content exceeds this.
  • Multiple images: LinkedIn supports posting multiple images at once. They will appear as a multi-image grid layout (not a swipeable carousel — organic carousels are not supported via the API).
  • Rich text not supported via API: LinkedIn's API does not support bold, italic, or other rich text formatting. Use plain text or Unicode characters for emphasis.
  • Analytics delay: LinkedIn analytics may take up to 24 hours to fully populate. Querying immediately after posting will return partial data.
  • Hashtags: LinkedIn hashtags are supported in the content body. They are treated as plain text but become clickable on the platform.
  • Reactions on external posts: You can only react to posts visible in your LinkedIn network. Ensure your account is connected to the post author.

Character Limits

ElementLimit
Post body3,000 characters
Comment1,250 characters (validated by PostPost; returns 400 error: "message cannot exceed 1250 characters")

On this page