要点
LinkedIn APIは、開発者がLinkedInのプロフェッショナルネットワークとプログラムで統合することを可能にします。OAuth 2.0認証、プロファイル、投稿、コメント、企業ページ、広告のためのRESTfulおよびGraphQLエンドポイントを使用し、アプリごとに1日あたり100〜500リクエストのレート制限があります。このガイドでは、認証設定、プロファイルアクセス、コンテンツ投稿、企業ページ管理、広告API、および本番環境への統合戦略について説明します。
はじめに
LinkedInは200以上の国と地域に9億人以上のプロフェッショナルユーザーを抱えています。採用ツール、マーケティングプラットフォーム、またはB2Bアプリケーションを開発する開発者にとって、LinkedIn APIの統合は、このプロフェッショナルなオーディエンスにリーチするために不可欠です。
現実として、LinkedInでのプレゼンスを手動で管理しているB2Bマーケターは、投稿、エンゲージメント追跡、リード生成に毎週15~20時間を費やしています。堅実なLinkedIn API統合は、コンテンツ配信、リード獲得、エンゲージメント分析、採用ワークフローを自動化します。
このガイドでは、LinkedIn APIの統合プロセス全体を説明します。OAuth 2.0認証、プロファイルアクセス、コンテンツ投稿、企業ページ管理、広告統合、Webhook、および本番環境へのデプロイ戦略を学びます。このガイドを読み終える頃には、本番環境に対応したLinkedIn統合が完了しているでしょう。
LinkedIn APIとは?
LinkedInは、プロフェッショナルネットワークデータにアクセスするためのRESTfulおよびGraphQL APIを提供します。APIは以下を処理します。
- ユーザープロファイル情報(同意の上で)
- 企業ページと更新情報
- 投稿、コメント、リアクション
- つながり(制限あり)
- 求人情報と応募
- LinkedIn広告管理
- リード生成フォーム
- メッセージング(一部のパートナーのみ)
主な機能
| 機能 | 説明 |
|---|---|
| RESTful + GraphQL | 複数のAPIスタイル |
| OAuth 2.0 | ユーザー認証が必要 |
| レート制限 | アプリごとに1日あたり100〜500リクエスト |
| 企業ページ | 完全なCRUD操作 |
| 広告API | キャンペーン管理 |
| Webhook | リアルタイム通知 |
| メディアアップロード | 画像と動画 |
API製品
| API | アクセスレベル | ユースケース |
|---|---|---|
| LinkedInでサインイン | オープン | ユーザー認証 |
| プロファイルAPI | パートナー | ユーザープロファイルの読み取り |
| 企業管理者API | パートナー | 企業ページの管理 |
| 広告API | パートナー | 広告キャンペーン管理 |
| 求人投稿API | パートナー | 求人の投稿と管理 |
| マーケティング開発者プラットフォーム | パートナー | 完全なAPIアクセス |
APIバージョン
| バージョン | ステータス | 終了日 |
|---|---|---|
| v2 | 現行 | アクティブ |
| v1 | 廃止 | 2023年12月 |
開始方法:認証設定
ステップ1:LinkedIn開発者アカウントの作成
APIにアクセスする前に:
- LinkedIn開発者ポータルにアクセスします
- LinkedInアカウントでサインインします
- 「マイアプリ」ダッシュボードで「Create App」をクリックします
- アプリの詳細(名前、ロゴ、説明)を入力します
ステップ2:アプリ設定の構成
アプリ認証を設定します:
const LINKEDIN_CLIENT_ID = process.env.LINKEDIN_CLIENT_ID;
const LINKEDIN_CLIENT_SECRET = process.env.LINKEDIN_CLIENT_SECRET;
const LINKEDIN_REDIRECT_URI = process.env.LINKEDIN_REDIRECT_URI;
// これらはアプリのダッシュボードから取得します
// https://www.linkedin.com/developers/apps/{appId}/auth
ステップ3:必要な権限の要求
LinkedInは権限の承認を要求します:
| 権限 | 説明 | 承認が必要 |
|---|---|---|
r_liteprofile |
基本プロファイル(名前、写真) | 自動承認 |
r_emailaddress |
メールアドレス | 自動承認 |
w_member_social |
ユーザーの代わりに投稿 | パートナー認証 |
r_basicprofile |
完全なプロファイル | パートナー認証 |
r_organization_social |
企業ページアクセス | パートナー認証 |
w_organization_social |
企業ページへの投稿 | パートナー認証 |
rw_ads |
広告管理 | パートナー認証 |
r_ads_reporting |
広告分析 | パートナー認証 |
ステップ4:認証URLの構築
OAuth 2.0フローを実装します:
const getAuthUrl = (state, scopes = ['r_liteprofile', 'r_emailaddress']) => {
const params = new URLSearchParams({
response_type: 'code',
client_id: LINKEDIN_CLIENT_ID,
redirect_uri: LINKEDIN_REDIRECT_URI,
scope: scopes.join(' '),
state: state
});
return `https://www.linkedin.com/oauth/v2/authorization?${params.toString()}`;
};
// 使用法
const state = require('crypto').randomBytes(16).toString('hex');
const authUrl = getAuthUrl(state, ['r_liteprofile', 'r_emailaddress', 'w_member_social']);
console.log(`ユーザーを以下にリダイレクトします: ${authUrl}`);
ステップ5:コードをアクセストークンと交換する
OAuthコールバックを処理します:
const exchangeCodeForToken = async (code) => {
const response = await fetch('https://www.linkedin.com/oauth/v2/accessToken', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: LINKEDIN_CLIENT_ID,
client_secret: LINKEDIN_CLIENT_SECRET,
redirect_uri: LINKEDIN_REDIRECT_URI
})
});
const data = await response.json();
return {
accessToken: data.access_token,
expiresIn: data.expires_in // 60日
};
};
// コールバックを処理します
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// トークンを安全に保存します
await db.users.update(req.session.userId, {
linkedin_access_token: tokens.accessToken,
linkedin_token_expires: Date.now() + (tokens.expiresIn * 1000)
});
res.redirect('/success');
} catch (error) {
console.error('OAuthエラー:', error);
res.status(500).send('認証に失敗しました');
}
});
ステップ6:アクセストークンの更新
アクセストークンは60日後に期限切れになります:
const refreshAccessToken = async (refreshToken) => {
// 注:LinkedInはリフレッシュトークンを提供していません
// ユーザーは60日後に再認証する必要があります
// 有効期限通知を実装します
};
// API呼び出しの前にトークンの有効期限を確認します
const ensureValidToken = async (userId) => {
const user = await db.users.findById(userId);
if (user.linkedin_token_expires < Date.now() + 86400000) { // 24時間
// ユーザーに再認証を通知します
await notifyUserToReauth(user.id);
throw new Error('トークンの有効期限が切れました。再認証してください');
}
return user.linkedin_access_token;
};
ステップ7:認証済みAPI呼び出しの実行
再利用可能なAPIクライアントを作成します:
const LINKEDIN_BASE_URL = 'https://api.linkedin.com/v2';
const linkedinRequest = async (endpoint, options = {}) => {
const accessToken = await ensureValidToken(options.userId);
const response = await fetch(`${LINKEDIN_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'X-Restli-Protocol-Version': '2.0.0',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`LinkedIn APIエラー: ${error.message}`);
}
return response.json();
};
// 使用法
const profile = await linkedinRequest('/me');
console.log(`こんにちは、${profile.localizedFirstName} ${profile.localizedLastName}さん`);
プロファイルアクセス
ユーザープロファイルの取得
認証済みユーザーのプロファイルを取得します:
const getUserProfile = async () => {
const response = await linkedinRequest('/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))');
return response;
};
// 使用法
const profile = await getUserProfile();
console.log(`名前: ${profile.localizedFirstName} ${profile.localizedLastName}`);
console.log(`ID: ${profile.id}`);
console.log(`写真: ${profile.profilePicture?.['displayImage~']?.elements?.[0]?.identifiers?.[0]?.identifier}`);
メールアドレスの取得
ユーザーのメールアドレスを取得します:
const getUserEmail = async () => {
const response = await linkedinRequest('/emailAddress?q=members&projection=(emailAddress*)');
return response;
};
// 使用法
const email = await getUserEmail();
console.log(`メール: ${email.elements?.[0]?.emailAddress}`);
利用可能なプロファイルフィールド
| フィールド | 権限 | 説明 |
|---|---|---|
id |
r_liteprofile | LinkedInメンバーID |
firstName |
r_liteprofile | 名 |
lastName |
r_liteprofile | 姓 |
profilePicture |
r_liteprofile | プロファイル写真URL |
headline |
r_basicprofile | プロフェッショナルヘッドライン |
summary |
r_basicprofile | 「概要」セクション |
positions |
r_basicprofile | 職歴 |
educations |
r_basicprofile | 学歴 |
emailAddress |
r_emailaddress | 主要メールアドレス |
コンテンツの投稿
投稿の作成
ユーザーのフィードにテキスト投稿を共有します:
const createPost = async (authorUrn, postContent) => {
const response = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postContent.text
},
shareMediaCategory: 'NONE'
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
})
});
return response;
};
// 使用法
const post = await createPost('urn:li:person:ABC123', {
text: '新製品の発売を発表できることを嬉しく思います!🚀 #イノベーション #スタートアップ'
});
console.log(`投稿が作成されました: ${post.id}`);
画像を伴う投稿の作成
メディア付きで投稿を共有します:
const createPostWithImage = async (authorUrn, postData) => {
// ステップ1:メディアアップロードを登録します
const uploadRegistration = await linkedinRequest('/assets?action=registerUpload', {
method: 'POST',
body: JSON.stringify({
registerUploadRequest: {
recipes: ['urn:li:digitalmediaRecipe:feedshare-image'],
owner: authorUrn,
serviceRelationships: [
{
relationshipType: 'OWNER',
identifier: 'urn:li:userGeneratedContent'
}
]
}
})
});
const uploadUrl = uploadRegistration.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl;
const assetUrn = uploadRegistration.value.asset;
// ステップ2:提供されたURLに画像をアップロードします
await fetch(uploadUrl, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + await getAccessToken(),
'Content-Type': 'application/octet-stream'
},
body: postData.imageBuffer
});
// ステップ3:アップロードされた画像で投稿を作成します
const post = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postData.text
},
shareMediaCategory: 'IMAGE',
media: [
{
status: 'READY',
description: {
text: postData.imageDescription || ''
},
media: assetUrn,
title: {
text: postData.title || ''
}
}
]
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
})
});
return post;
};
動画を伴う投稿の作成
動画コンテンツを共有します:
const createPostWithVideo = async (authorUrn, postData) => {
// 動画アップロードを登録します
const uploadRegistration = await linkedinRequest('/assets?action=registerUpload', {
method: 'POST',
body: JSON.stringify({
registerUploadRequest: {
recipes: ['urn:li:digitalmediaRecipe:feedshare-video'],
owner: authorUrn,
serviceRelationships: [
{
relationshipType: 'OWNER',
identifier: 'urn:li:userGeneratedContent'
}
]
}
})
});
const assetUrn = uploadRegistration.value.asset;
// 動画をアップロードします(応答からの署名付きアップロードURLを使用)
// ... アップロードロジック ...
// 投稿を作成します
const post = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: { text: postData.text },
shareMediaCategory: 'VIDEO',
media: [{ status: 'READY', media: assetUrn }]
}
},
visibility: { 'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC' }
})
});
return post;
};
メディアの仕様
| メディアタイプ | 形式 | 最大サイズ | 期間 |
|---|---|---|---|
| 画像 | JPG、PNG、GIF | 8MB | 該当なし |
| 動画 | MP4、MOV | 5GB | 最大15分 |
| ドキュメント | PDF、PPT、DOC | 100MB | 該当なし |
企業ページ管理
企業情報の取得
企業ページの詳細を取得します:
const getCompanyInfo = async (companyId) => {
const response = await linkedinRequest(
`/organizations/${companyId}?projection=(id,localizedName,vanityName,tagline,description,universalName,logoV2(original~:playableStreams),companyType,companyPageUrl,confirmedLocations,industries,followerCount,staffCountRange,website, specialties)`
);
return response;
};
// 使用法
const company = await getCompanyInfo('1234567');
console.log(`会社: ${company.localizedName}`);
console.log(`フォロワー: ${company.followerCount}`);
console.log(`ウェブサイト: ${company.website}`);
企業ページへの投稿
企業ページに更新情報を共有します:
const createCompanyPost = async (organizationUrn, postContent) => {
const response = await linkedinRequest('/ugcPosts', {
method: 'POST',
body: JSON.stringify({
author: organizationUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postContent.text
},
shareMediaCategory: postContent.media ? 'IMAGE' : 'NONE',
media: postContent.media ? [
{
status: 'READY',
media: postContent.media.assetUrn
}
] : []
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
})
});
return response;
};
// 使用法
const post = await createCompanyPost('urn:li:organization:1234567', {
text: '採用募集中!成長中のチームに参加しませんか。#キャリア #採用'
});
企業のフォロワーの取得
フォロワー数を取得します:
const getFollowerCount = async (organizationId) => {
const response = await linkedinRequest(
`/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:${organizationId}`
);
return response;
};
レート制限
レート制限の理解
LinkedInはアプリごとにレート制限を適用します:
| API | 制限 | 期間 |
|---|---|---|
| プロファイルAPI | 100リクエスト | 1日あたり |
| UGC投稿 | 50投稿 | 1日あたり |
| 企業管理者 | 500リクエスト | 1日あたり |
| 広告API | 100リクエスト | 1分あたり |
レート制限ヘッダー
| ヘッダー | 説明 |
|---|---|
X-Restli-Quota-Remaining |
残りのリクエスト数 |
X-Restli-Quota-Reset |
リセットまでの秒数 |
一般的な問題のトラブルシューティング
問題:401 Unauthorized(未承認)
解決策:
- アクセストークンが期限切れになっていないか確認します(60日)
- トークンのスコープに要求されたリソースが含まれているか確認します
Authorization: Bearer {token}ヘッダーが存在することを確認します
問題:403 Forbidden(禁止)
解決策:
- アプリが必要な権限を持っているか確認します
- ユーザーが要求されたスコープを承認したか確認します
- パートナー認証が必要な場合があります
問題:429 Rate Limited(レート制限)
解決策:
- リクエストキューイングを実装します
- 呼び出しを減らすために応答をキャッシュします
- ポーリングの代わりにWebhookを使用します
本番環境デプロイチェックリスト
本番稼働前に:
- [ ] LinkedInパートナー認証を完了します
- [ ] 安全なトークンストレージを備えたOAuth 2.0を実装します
- [ ] トークン有効期限通知(60日)を追加します
- [ ] レート制限とキューイングを設定します
- [ ] Webhookエンドポイントを設定します
- [ ] 包括的なエラー処理を実装します
- [ ] すべてのAPI呼び出しのログを追加します
- [ ] ブランドガイドラインの遵守レビューを作成します
実際のユースケース
採用プラットフォーム
採用ツールが求人投稿を自動化します:
- 課題:複数のチャネルへの手動投稿
- 解決策:LinkedIn Jobs API統合
- 結果:80%の時間削減、応募数3倍
B2Bマーケティング自動化
マーケティングプラットフォームがLinkedInコンテンツをスケジュールします:
- 課題:一貫性のない投稿スケジュール
- 解決策:UGC APIによる自動投稿
- 結果:エンゲージメント5倍、一貫したブランドプレゼンス
まとめ
LinkedIn APIは、プロフェッショナルネットワーク機能への包括的なアクセスを提供します。主なポイント:
- 60日間有効なトークンによるOAuth 2.0認証
- プロファイル、投稿、企業ページAPIが利用可能
- レート制限には注意深い管理が必要です(1日あたり100〜500)
- ほとんどのAPIでパートナー認証が必要
- ApidogはAPIテストとチームコラボレーションを効率化します
よくある質問
LinkedIn APIにアクセスするにはどうすればよいですか?
LinkedIn開発者アカウントを作成し、アプリを作成し、高度なAPIアクセスにはパートナー認証を完了してください。
LinkedInに自動的に投稿できますか?
はい、個人投稿にはw_member_social権限を持つUGC(User Generated Content)APIを、企業投稿にはw_organization_socialを使用できます。
LinkedInのレート制限とは何ですか?
レート制限はAPIによって1日あたり100〜500リクエストの範囲です。広告APIは1分あたり100リクエストを許可しています。
LinkedInトークンはどのくらい持続しますか?
アクセストークンは60日後に期限切れになります。APIアクセスを継続するには、ユーザーが再認証する必要があります。
ユーザーのつながりにアクセスできますか?
いいえ、プライバシー変更により、LinkedInはほとんどのアプリからのつながりAPIアクセスを削除しました。
