@bernierllc/social-media-linkedin
v1.0.4
Published
LinkedIn API integration service with OAuth 2.0, content posting (posts, articles, documents), and rate limiting
Readme
@bernierllc/social-media-linkedin
LinkedIn API integration service with OAuth 2.0, content posting (posts, articles, documents), and rate limiting.
Installation
npm install @bernierllc/social-media-linkedinUsage
import { LinkedInService } from '@bernierllc/social-media-linkedin';
// Create a new LinkedIn service instance
const linkedin = new LinkedInService({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
callbackUrl: 'https://your-app.com/callback',
scopes: ['openid', 'profile', 'w_member_social']
});
// Generate authorization URL for OAuth flow
const authUrl = linkedin.getAuthorizationUrl();
// Redirect user to authUrl...
// After user authorizes, handle the callback
const authResult = await linkedin.handleCallback(code, state);
if (authResult.success) {
console.log('Authenticated as:', authResult.profile?.name);
}
// Post an update
const postResult = await linkedin.postUpdate({
text: 'Hello LinkedIn! 👋',
visibility: 'PUBLIC'
});
if (postResult.success) {
console.log('Posted:', postResult.postUrn);
}API Reference
LinkedInService
Main service class for LinkedIn API integration.
Constructor
new LinkedInService(config: LinkedInConfig)Config Options:
clientId(required): LinkedIn application client IDclientSecret(required): LinkedIn application client secretcallbackUrl(required): OAuth callback URLscopes: OAuth scopes to request (default:['openid', 'profile', 'w_member_social'])rateLimitConfig: Rate limiting configurationtokenStorage: Token storage interface for persistence
Authentication Methods
getAuthorizationUrl(callbackUrl?: string): string
Generate OAuth 2.0 authorization URL for user consent.
const authUrl = linkedin.getAuthorizationUrl();
// Redirect user to authUrlhandleCallback(code: string, state: string): Promise<LinkedInAuthResult>
Handle OAuth callback and exchange authorization code for tokens.
const result = await linkedin.handleCallback(code, state);
if (result.success) {
console.log('User:', result.profile);
console.log('Tokens:', result.tokens);
}authenticate(credentials: LinkedInCredentials): Promise<LinkedInAuthResult>
Authenticate with existing access token.
const result = await linkedin.authenticate({
accessToken: 'existing-token',
expiresAt: 1234567890
});refreshAccessToken(): Promise<LinkedInAuthResult>
Refresh the access token using refresh token.
const result = await linkedin.refreshAccessToken();revokeAccess(): Promise<boolean>
Revoke access and clear tokens.
const revoked = await linkedin.revokeAccess();isAuthenticated(): boolean
Check if currently authenticated with valid token.
if (linkedin.isAuthenticated()) {
// Can make API calls
}Content Posting Methods
postUpdate(content: LinkedInContent): Promise<LinkedInPostResult>
Post an update to LinkedIn.
const result = await linkedin.postUpdate({
text: 'Check out this post!',
visibility: 'PUBLIC',
media: [{
type: 'image',
data: imageBuffer,
mimeType: 'image/jpeg',
altText: 'Description of the image'
}]
});postArticle(article: LinkedInArticle): Promise<LinkedInArticleResult>
Post an article to LinkedIn.
const result = await linkedin.postArticle({
title: 'My Article Title',
body: '<p>Article content in HTML</p>',
description: 'Brief description',
visibility: 'PUBLIC'
});shareDocument(document: LinkedInDocument): Promise<LinkedInPostResult>
Share a document (PDF, etc.) to LinkedIn.
const result = await linkedin.shareDocument({
title: 'My Document',
text: 'Check out this document!',
file: {
type: 'document',
data: pdfBuffer,
mimeType: 'application/pdf',
fileName: 'document.pdf'
}
});deletePost(postUrn: string): Promise<boolean>
Delete a post by URN.
const deleted = await linkedin.deletePost('urn:li:share:123456');Scheduling Methods
schedulePost(content: LinkedInContent, scheduledTime: Date): LinkedInScheduleResult
Schedule a post for future publishing.
const result = linkedin.schedulePost(
{ text: 'Scheduled post!' },
new Date('2025-01-15T10:00:00Z')
);cancelScheduledPost(scheduleId: string): boolean
Cancel a scheduled post.
const cancelled = linkedin.cancelScheduledPost('sched_123');listScheduledPosts(): LinkedInScheduledPost[]
List all scheduled posts.
const scheduled = linkedin.listScheduledPosts();Media Methods
uploadMedia(media: LinkedInMedia): Promise<LinkedInMediaUploadResult>
Upload media to LinkedIn.
const result = await linkedin.uploadMedia({
type: 'image',
data: imageBuffer,
mimeType: 'image/png'
});
if (result.success) {
console.log('Asset URN:', result.assetUrn);
}Rate Limiting
getRateLimitStatus(endpoint?: string): LinkedInRateLimitStatus | undefined
Get current rate limit status.
const status = linkedin.getRateLimitStatus('POST /ugcPosts');
console.log('Remaining:', status?.remaining);
console.log('Resets at:', new Date(status?.resetAt * 1000));Types
LinkedInContent
interface LinkedInContent {
text: string; // Up to 3000 characters
media?: LinkedInMedia[]; // Optional media attachments
link?: LinkedInLink; // Optional link preview
visibility?: LinkedInVisibility; // PUBLIC, CONNECTIONS, or LOGGED_IN
organizationUrn?: string; // For company page posts
}LinkedInMedia
interface LinkedInMedia {
type: 'image' | 'video' | 'document';
data?: Buffer; // Media data
url?: string; // External URL
mimeType?: string; // MIME type
fileName?: string; // File name
altText?: string; // Accessibility text
size?: number; // File size in bytes
}LinkedInArticle
interface LinkedInArticle {
title: string; // Up to 200 characters
body: string; // HTML content
description?: string; // Summary/excerpt
coverImage?: LinkedInMedia; // Cover image
visibility?: LinkedInVisibility;
organizationUrn?: string;
}OAuth Scopes
| Scope | Description |
|-------|-------------|
| openid | OpenID Connect (required) |
| profile | Basic profile information |
| email | Email address |
| w_member_social | Post on behalf of user |
| r_liteprofile | Read lite profile |
| r_organization_social | Read company page content |
| w_organization_social | Post to company pages |
Rate Limiting
The service automatically handles rate limits:
const linkedin = new LinkedInService({
// ... other config
rateLimitConfig: {
enabled: true,
strategy: 'wait', // or 'fail'
maxWaitMs: 60000 // Max wait time before failing
}
});Token Storage
Implement the LinkedInTokenStorage interface for token persistence:
const tokenStorage: LinkedInTokenStorage = {
save: async (tokens) => {
await db.saveTokens(tokens);
},
load: async () => {
return await db.loadTokens();
},
clear: async () => {
await db.clearTokens();
}
};
const linkedin = new LinkedInService({
// ... other config
tokenStorage
});Media Limits
| Type | Max Size | |------|----------| | Image | 8 MB | | Video | 200 MB | | Document | 100 MB |
Integration Status
- Logger: planned - Will integrate with @bernierllc/logger
- Docs-Suite: ready - Markdown documentation
- NeverHub: planned - Event publishing for linkedin.post.published
See Also
- @bernierllc/social-media-twitter - Twitter/X API integration
- @bernierllc/social-media-manager - Multi-platform social media manager
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
