linkedin-api
npx machina-cli add skill rockyco/claude-linkedin-plugin/linkedin-api --openclawLinkedIn API Knowledge
Post Types
LinkedIn supports these post content types via the Posts API (POST /rest/posts):
- Text only: Just the
commentaryfield, nocontentobject - Single image:
content.media.id= image URN - Multi-image:
content.multiImage.images[]array (2-20 images) - Article:
content.articlewithsourceURL, optionaltitle,description,thumbnail - Document:
content.media.id= document URN (PDF, PPT, etc.) - Poll: Not available via API (LinkedIn web only)
Required Headers
All LinkedIn REST API calls require:
Authorization: Bearer {access_token}
X-Restli-Protocol-Version: 2.0.0
Linkedin-Version: 202601
Content-Type: application/json
Image Upload Flow
- Initialize:
POST /rest/images?action=initializeUploadwithownerURN - Upload binary:
PUT {uploadUrl}withContent-Type: application/octet-stream - Use the returned
urn:li:image:{id}in the post's content field
Post Text Best Practices
- First 3 lines are visible before "see more" - make them count
- Use line breaks for readability (LinkedIn preserves whitespace)
- Hashtags at the end, 3-5 max, relevant to topic
- Tag people with @mentions where appropriate
- Character limit: ~3000 characters. The REST API silently truncates longer text with no error. Always validate before posting.
- Aim for 1500-2000 characters for optimal engagement
- Use unicode characters for bullet points if needed
Character Limit and Truncation
Critical: LinkedIn's Posts REST API silently truncates commentary text at approximately 3000 characters. There is no error or warning from the API - the post is created with shortened text.
Recommended workflow for long posts:
- Always validate text length before posting using
validate_text_length()(built into linkedin-api.py) - Under 2500 chars: Safe to post via API directly
- 2500-3000 chars: Use
--draftfor safety, or publish directly at user's discretion - Over 3000 chars: Always use
--draftto avoid public truncation
Draft mode (RECOMMENDED for long posts):
# Create as draft with images - never publicly truncated:
python3 linkedin-api.py post-multi-image --draft --text-file /tmp/post.txt --images img1.png img2.png
# Text-only draft:
python3 linkedin-api.py post-text --draft --text-file /tmp/post.txt
After --draft:
- The post is saved as a draft on LinkedIn (not published)
- Images are correctly attached via the API
- Open
https://www.linkedin.com/post/new/draftsto review - Edit text if truncated, then publish from the web UI
- LinkedIn allows only ONE draft at a time - publish or delete existing drafts before creating a new one
Preview mode (upload-only, no draft created):
# Upload images without posting or creating a draft:
python3 linkedin-api.py post-multi-image --preview --text-file /tmp/post.txt --images img1.png img2.png
# Text-only preview (validates length):
python3 linkedin-api.py post-text --preview --text-file /tmp/post.txt
When to use --draft vs --preview:
- --draft: Creates a draft with images attached. Best when images are part of the post and text needs editing. The draft exists on LinkedIn's servers.
- --preview: Uploads images but creates nothing. Best when the user wants full control via the compose page. No draft is stored.
Author URN
For personal posts: urn:li:person:{id}
For company pages: urn:li:organization:{id}
The person ID comes from /v2/userinfo (the sub field) after OAuth with openid scope.
Visibility Options
PUBLIC- visible to anyone on LinkedInCONNECTIONS- visible only to connections
Known API Limitations
- Silent text truncation: The Posts API silently truncates
commentarytext at ~3000 characters. No error is returned - the post is created successfully but with shortened text. Always validate text length before creation. Use--draftmode for posts near or over the limit - the truncated text is never public and can be fixed before publishing. - PARTIAL_UPDATE unreliable for commentary:
X-RestLi-Method: PARTIAL_UPDATEwithpatch: {"$set": {"commentary": "..."}}returns 204 but may not actually update the text. Do not rely on it to fix truncated posts. - Browser editing works: If text is truncated, edit via LinkedIn's web UI. The internal web APIs correctly handle text updates.
- Always use --text-file: Pass post text via a temp file (
--text-file /tmp/linkedin_post.txt) rather than--texton the command line to avoid shell quoting issues with multi-line or special-character text. - Draft mode available: All
post-*commands support--draftto create the post as a draft (images attached, text editable before publish). LinkedIn limits you to one draft at a time. - Preview mode available: All
post-*commands support--previewto upload media and validate text without creating the post or a draft. - Draft URL:
https://www.linkedin.com/post/new/drafts- navigate here to find, edit, and publish drafts.
Common Errors
- 401 Unauthorized: Token expired (60-day lifetime). Re-run
/linkedin:setup. - 403 Forbidden: Missing required scope. Check that "Share on LinkedIn" product is added to the app.
- 422 Unprocessable: Invalid post payload. Check URN format and required fields.
- 429 Too Many Requests: Rate limited. Wait and retry.
Comments API
Note: Comments use the socialActions endpoint which requires the Community Management API product (partner-level access). The basic "Share on LinkedIn" product (w_member_social) does NOT cover this endpoint. Apply at https://developer.linkedin.com/product-catalog/marketing/community-management-api.
Comments use the socialActions endpoint, separate from the posts endpoint.
List comments
GET /rest/socialActions/{postUrn}/comments?start=0&count=20
Response elements[] array contains: actor (URN), message.text, commentUrn, id, created.time (ms epoch), likesSummary.totalLikes.
Create a comment
POST /rest/socialActions/{postUrn}/comments
Body: {"actor": personUrn, "object": postUrn, "message": {"text": "..."}}
Returns comment ID in x-restli-id header and full comment object in body.
Reply to a comment (nested)
POST /rest/socialActions/{commentUrn}/comments
Body: {"actor": personUrn, "object": postUrn, "message": {"text": "..."}, "parentComment": commentUrn}
The commentUrn is composite: urn:li:comment:(urn:li:activity:xxx,commentId).
Scopes
- Writing comments:
w_member_social(standard, included with "Share on LinkedIn" product) - Reading comments: may require
r_member_social(restricted) for non-own posts
Scripts Location
The LinkedIn API scripts are at ${CLAUDE_PLUGIN_ROOT}/scripts/:
oauth-server.py- OAuth 2.0 flow with local callback serverlinkedin-api.py- Post creation, image upload, auth check, post verification, draft mode, preview mode
Source
git clone https://github.com/rockyco/claude-linkedin-plugin/blob/main/skills/linkedin-api/SKILL.mdView on GitHub Overview
This skill covers composing LinkedIn posts through the REST API, including available post content types, image/document upload flows, and content guidelines. It also covers required headers, draft/preview workflows, and handling of truncation so posts publish reliably.
How This Skill Works
LinkedIn posts via the API support text, images, multi-image, articles, and documents (polls are web-only). To publish, send requests to POST /rest/posts with the proper content object and headers such as Authorization and X-Restli-Protocol-Version. For images, you initialize an upload, upload the binary, and attach the resulting urn:li:image:{id} to the post; long content can use --draft or --preview to avoid public truncation, since the API truncates around 3000 characters without an error.
When to Use It
- Creating a text-only post via the REST API
- Posting a single image or a multi-image gallery
- Publishing an article or a document attachment
- Drafting long posts to avoid truncation using --draft or previewing with --preview
- Troubleshooting API formatting, headers, or content issues
Quick Start
- Step 1: Prepare your text file (and any images you want to include)
- Step 2: Choose a mode (text, single image, or multi-image) and decide on --draft or --preview
- Step 3: Run the corresponding CLI command (e.g., post-text or post-multi-image) and review the result in Drafts or on LinkedIn
Best Practices
- Validate text length before posting; the API truncates around 3000 characters
- Place hashtags at the end and keep them to 3-5 relevant tags
- Use line breaks for readability; LinkedIn preserves whitespace
- Tag people with @mentions where appropriate
- Test drafts (--draft) or previews (--preview) before publishing publicly
Example Use Cases
- Posting a text-only update by sending the commentary field via post-text
- Publishing a single image by setting content.media.id to the image URN
- Publishing a gallery with content.multiImage.images[] containing 2-20 images
- Publishing an article by including content.article with a source URL and optional title/description
- Drafting a long post with --draft to attach images and edit text before public publish