How to Post to a LinkedIn Company Page via API
Posting to a LinkedIn company page requires different OAuth scopes and a different author URN than personal posts. Here's the full pattern with Postproxy.
Personal vs company: what’s actually different
The publish endpoint path is the same — POST /rest/posts. What changes:
| Personal post | Company page post | |
|---|---|---|
| OAuth scope required | w_member_social | w_organization_social |
| Author URN | urn:li:person:{member-id} | urn:li:organization:{org-id} |
| Permission check | The signed-in member | Member must have ADMINISTRATOR or CONTENT_ADMIN on the page |
The wrong-scope failure mode is the most common: you have a working personal token, hit the publish endpoint with an organization URN, and LinkedIn returns 403 Forbidden — Insufficient permissions. Re-running OAuth with the right scope is the only fix.
With Postproxy: post to your personal profile
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "Ten lessons from a year shipping infra." }, "profiles": ["linkedin"] }'Post to a company page (organization)
You connect the LinkedIn profile once. Then on each post, pass organization_id in the linkedin platform params to route to the company page instead of your personal profile:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "We are hiring a senior backend engineer. Apply at acme.com/careers." }, "profiles": ["linkedin"], "platforms": { "linkedin": { "organization_id": "12345678" } } }'To list which organizations your connected LinkedIn account can post to, call:
curl -X GET "https://api.postproxy.dev/api/profiles/PROFILE_ID/placements" \ -H "Authorization: Bearer YOUR_API_KEY"Response (LinkedIn example):
{ "placements": [ { "id": null, "name": "Personal Profile" }, { "id": "108520199", "name": "Acme Marketing" }, { "id": "203488011", "name": "Acme Labs" } ]}id: null means your personal profile. Any other id is an organization you can post on behalf of — pass that as organization_id.
Posting media to a company page
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "Q1 retrospective: what shipped, what slipped, what we learned." }, "profiles": ["linkedin"], "media": ["https://yourcdn.com/q1-retro.png"], "platforms": { "linkedin": { "organization_id": "108520199" } } }'LinkedIn supports image, video, and document attachments on company page posts.
Posting a PDF (document post)
PDFs render as inline carousels in the LinkedIn feed — useful for whitepapers and reports:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "Our Q1 report — 18 pages on what we built." }, "profiles": ["linkedin"], "media": ["https://yourcdn.com/q1-report.pdf"], "platforms": { "linkedin": { "organization_id": "108520199" } } }'Documents must be alone — don’t mix with images or videos. PDFs cap at 100 MB / 300 pages.
Multiple company pages from one account
If your account administers several pages (agency, holding company, multi-brand), make one request per page — each carries its own organization_id. Using the Python SDK:
import asynciofrom postproxy import PostProxy, PlatformParams, LinkedInParams
async def fan_out(org_ids): async with PostProxy("your-api-key", profile_group_id="pg-abc") as client: for org_id in org_ids: await client.posts.create( "New whitepaper out: acme.com/whitepaper", profiles=["linkedin"], platforms=PlatformParams( linkedin=LinkedInParams(organization_id=org_id), ), )
asyncio.run(fan_out(["108520199", "203488011", "311945522"]))Scheduling company page posts
LinkedIn’s API has no scheduled_publish_time. Use Postproxy’s scheduled_at:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "Live at 9 AM ET: our CEO on the state of the industry.", "scheduled_at": "2026-05-15T13:00:00Z" }, "profiles": ["linkedin"], "platforms": { "linkedin": { "organization_id": "108520199" } } }'Personal voice + company headline
A common pattern: post the headline from the company page and the founder’s reflection from their personal profile. Two requests, one media set:
async with PostProxy("your-api-key", profile_group_id="pg-abc") as client: media = ["https://yourcdn.com/v3-launch.png"]
await client.posts.create( "v3 is live. Engineering deep-dive on the blog: acme.com/blog/v3", profiles=["linkedin"], media=media, platforms=PlatformParams( linkedin=LinkedInParams(organization_id="108520199"), ), )
await client.posts.create( "Two years of engineering went into this. Here is what I am proud of:", profiles=["linkedin"], media=media, )Common 403 / 422 errors
| Error | What it actually means |
|---|---|
403 Insufficient permissions | OAuth scope is missing w_organization_social. Re-auth. |
403 Not enough permissions to access | The member is not an admin/content-admin of the page. Add them in LinkedIn page settings. |
422 Invalid author URN | Personal token used with an organization URN, or vice versa. Check organization_id. |
429 Throttled | Daily post limit hit. |
For a longer walkthrough of the company page automation pattern, see LinkedIn API: automate company page publishing.