LinkedIn's API requires partner-level access for most publishing features — but Aether is already approved. You get LinkedIn posting without the 6-month review process. Here's how to go from zero to publishing in under an hour.
Prerequisites
- An Aether account — free tier supports 3 connected accounts
- A LinkedIn Company Page (for Page posts) or a personal LinkedIn account
- Node.js 18+ in your project
1. Install the SDK
npm install aether2. Connect a LinkedIn account
Aether supports two LinkedIn profile types: personal profiles ( type: "person") and Company Pages (type: "page"). Use platform: "linkedin" for both — the OAuth flow lets the user choose which pages to grant access to.
import Aether from "aether";
const aether = new Aether({ apiKey: process.env.AETHER_API_KEY });
// Generate an OAuth link — choose "linkedin" for personal profiles
// or "linkedin_page" for Company Pages
const link = await aether.connectLinks.create({
platform: "linkedin",
redirectUrl: "https://yourapp.com/oauth/linkedin/callback",
});
// Redirect your user to link.url
console.log(link.url);LinkedIn tokens expire after 60 days. Aether's token-refresh worker proactively renews them 24 hours before expiry — your users won't need to reconnect.
3. List connected profiles
// List all connected LinkedIn profiles
const profiles = await aether.profiles.list({ platform: "linkedin" });
// profiles.data looks like:
// [
// { id: "li_abc123", displayName: "Acme Corp", type: "page" },
// { id: "li_xyz789", displayName: "Jane Smith", type: "person" }
// ]
const pageId = profiles.data.find(p => p.type === "page")?.id;
const personId = profiles.data.find(p => p.type === "person")?.id;4. Publish a text post
LinkedIn text posts support up to 3,000 characters. Line breaks render correctly — use them to structure your update. Aether handles the UGC post API call and returns the live post URL.
// Post a text update to a LinkedIn Company Page
const post = await aether.posts.create({
profileIds: [pageId],
text: "We just shipped v2.0 of our API platform. Here's what's new 👇\n\n" +
"• Unified inbox across 7 platforms\n" +
"• Real-time webhooks with retry logic\n" +
"• MCP server for AI agent integration\n\n" +
"Full changelog: https://aetherhq.dev/changelog",
});
console.log(post.status); // "published"
console.log(post.platformResults?.linkedin?.url); // LinkedIn post URLPosting images and cross-posting
LinkedIn image posts require a two-step upload: register the asset, then attach it to the post. Aether handles both steps when you pass a mediaUrls array. You can also post to a Company Page and personal profile simultaneously with a single API call.
// Post with an image — provide a publicly accessible URL
const imagePost = await aether.posts.create({
profileIds: [pageId],
text: "Behind the scenes of our engineering team 📸",
overrides: {
linkedin: {
mediaUrls: ["https://yourcdn.com/team-photo.jpg"],
altText: "The Aether engineering team at our offsite in Lisbon",
},
},
});
// Post to both personal profile and company page in one call
const crossPost = await aether.posts.create({
profileIds: [pageId, personId],
text: "Excited to announce our Series A 🎉",
overrides: {
linkedin: {
// LinkedIn strips links from body text — add a commentary field instead
commentary: "We raised $8M to build the social media API infrastructure layer.",
},
},
});LinkedIn-specific rules to know
- LinkedIn enforces a 150 API calls/day limit per token — avoid high-frequency polling
- URLs in post body text are automatically converted to preview cards by LinkedIn, but the URL itself is not clickable in the native app — add your link in the commentary field for best UX
- Hashtags work in body text:
#buildinpublic— up to 3 is best practice - Company Page posts require the connected user to be a Super Admin or Content Admin of the page
Next steps
- Use scheduled posts to queue your LinkedIn content calendar
- Subscribe to the
post.publishedwebhook to sync post URLs back to your CRM - Pull post-level analytics — impressions, clicks, reactions — with the analytics API