DEV Community

Cover image for LinkedIn API の使い方:完全プロフェッショナルネットワーク連携ガイド(2026年)
Akira
Akira

Posted on • Originally published at apidog.com

LinkedIn API の使い方:完全プロフェッショナルネットワーク連携ガイド(2026年)

要点

LinkedIn APIは、開発者がLinkedInのプロフェッショナルネットワークとプログラムで統合するための標準的な方法を提供します。OAuth 2.0認証、プロファイル、投稿、コメント、企業ページ、広告のためのRESTfulおよびGraphQLエンドポイントが用意されており、アプリごとに1日100〜500リクエストのレート制限があります。本記事では、認証設定から本番環境への統合までの実装手順を解説します。

今すぐApidogを試してみましょう

はじめに

LinkedInは200以上の国と地域に9億人以上のユーザーが存在します。B2Bアプリやマーケティングツール、採用サービスを開発する際、LinkedIn APIの活用は欠かせません。

実務では、B2Bマーケターが手動でプレゼンスを管理すると、週15~20時間もかかることがあります。API統合を活用することで、コンテンツ配信、リード獲得、エンゲージメント分析、採用ワークフローの自動化が実現できます。

このガイドで、OAuth 2.0による認証、プロファイル取得、投稿、企業ページ管理、広告API、Webhook、デプロイ戦略まで、実装に必要な具体的手順を解説します。

💡 ApidogはAPI統合テストを効率化します。LinkedInエンドポイントのテスト、OAuthフローの検証、API応答の確認、権限エラーのデバッグまで1ワークスペースで完結。API仕様のインポートやモックレスポンス生成もサポート、チームでテストシナリオを共有可能です。

LinkedIn APIとは?

LinkedInはプロフェッショナルネットワークデータへのRESTful・GraphQL 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開発者アカウントの作成

  1. LinkedIn開発者ポータルにアクセス
  2. LinkedInアカウントでサインイン
  3. 「マイアプリ」→「Create App」をクリック
  4. アプリの詳細(名前・ロゴ・説明)を入力

ステップ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
Enter fullscreen mode Exit fullscreen mode

ステップ3:必要な権限の要求

権限ごとに、どのデータへアクセスできるかが決まります。

権限 説明 承認が必要
r_liteprofile 基本プロファイル(名前、写真) 自動承認
r_emailaddress メールアドレス 自動承認
w_member_social ユーザーの代わりに投稿 パートナー認証
r_basicprofile 完全なプロファイル パートナー認証
r_organization_social 企業ページアクセス パートナー認証
w_organization_social 企業ページへの投稿 パートナー認証
rw_ads 広告管理 パートナー認証
r_ads_reporting 広告分析 パートナー認証

ステップ4:認証URLの構築

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}`);
Enter fullscreen mode Exit fullscreen mode

ステップ5:コードをアクセストークンと交換する

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('認証に失敗しました');
  }
});
Enter fullscreen mode Exit fullscreen mode

ステップ6:アクセストークンの更新

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;
};
Enter fullscreen mode Exit fullscreen mode

ステップ7:認証済み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}さん`);
Enter fullscreen mode Exit fullscreen mode

プロファイルアクセス

ユーザープロファイルの取得

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}`);
Enter fullscreen mode Exit fullscreen mode

メールアドレスの取得

const getUserEmail = async () => {
  const response = await linkedinRequest('/emailAddress?q=members&projection=(emailAddress*)');
  return response;
};

// 使用例
const email = await getUserEmail();
console.log(`メール: ${email.elements?.[0]?.emailAddress}`);
Enter fullscreen mode Exit fullscreen mode

利用可能なプロファイルフィールド

フィールド 権限 説明
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}`);
Enter fullscreen mode Exit fullscreen mode

画像を伴う投稿の作成

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;
};
Enter fullscreen mode Exit fullscreen mode

動画を伴う投稿の作成

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;
};
Enter fullscreen mode Exit fullscreen mode

メディアの仕様

メディアタイプ 形式 最大サイズ 期間
画像 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}`);
Enter fullscreen mode Exit fullscreen mode

企業ページへの投稿

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: '採用募集中!成長中のチームに参加しませんか。#キャリア #採用'
});
Enter fullscreen mode Exit fullscreen mode

企業のフォロワーの取得

const getFollowerCount = async (organizationId) => {
  const response = await linkedinRequest(
    `/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:${organizationId}`
  );
  return response;
};
Enter fullscreen mode Exit fullscreen mode

レート制限

レート制限の理解

API 制限 期間
プロファイルAPI 100リクエスト 1日あたり
UGC投稿 50投稿 1日あたり
企業管理者 500リクエスト 1日あたり
広告API 100リクエスト 1分あたり

レート制限ヘッダー

ヘッダー 説明
X-Restli-Quota-Remaining 残りのリクエスト数
X-Restli-Quota-Reset リセットまでの秒数

一般的な問題のトラブルシューティング

問題:401 Unauthorized(未承認)

対策:

  1. アクセストークンの有効期限(60日)を確認
  2. トークンスコープに必要なリソースがあるか確認
  3. Authorization: Bearer {token} ヘッダーの有無を確認

問題:403 Forbidden(禁止)

対策:

  1. アプリが必要な権限を持つかチェック
  2. ユーザーが必要なスコープを承認済みか確認
  3. パートナー認証の有無を確認

問題:429 Rate Limited(レート制限)

対策:

  1. リクエストキューイングを実装
  2. レスポンスキャッシュで呼び出し頻度を抑える
  3. ポーリングの代わりにWebhookを利用

本番環境デプロイチェックリスト

本番リリース前に以下を確認しましょう。

  • [ ] LinkedInパートナー認証を完了
  • [ ] OAuth 2.0の安全なトークンストレージ実装
  • [ ] トークン有効期限通知(60日)を設定
  • [ ] レート制限とキューイングの導入
  • [ ] Webhookエンドポイントを設定
  • [ ] エラー処理を網羅的に実装
  • [ ] すべてのAPI呼び出しのログ記録
  • [ ] ブランドガイドラインの遵守レビュー

実際のユースケース

採用プラットフォーム

  • 課題:複数チャネルへの手動投稿
  • 解決策:LinkedIn Jobs API統合
  • 結果:80%の時間削減・応募数3倍

B2Bマーケティング自動化

  • 課題:一貫性のない投稿スケジュール
  • 解決策:UGC APIによる自動投稿
  • 結果:エンゲージメント5倍・ブランド統一

まとめ

LinkedIn APIはプロフェッショナルネットワーク機能への包括的アクセスを実現します。実装時のポイントは:

  • OAuth 2.0認証(トークンは60日有効)
  • プロファイル・投稿・企業ページAPIの活用
  • レート制限(1日100〜500リクエスト)の管理
  • ほとんどのAPIでパートナー認証が必要
  • ApidogによるAPIテスト・チームコラボの効率化

よくある質問

LinkedIn APIにアクセスするにはどうすればよいですか?

LinkedIn開発者アカウントを作成し、アプリを登録。高度なアクセスにはパートナー認証を申請。

LinkedInに自動的に投稿できますか?

はい。個人投稿はw_member_social、企業投稿はw_organization_social権限でUGC APIを利用可能です。

LinkedInのレート制限とは何ですか?

APIごとに1日100〜500リクエスト(広告APIは1分100リクエスト)に制限されます。

LinkedInトークンはどのくらい持続しますか?

アクセストークンの有効期限は60日。継続利用には再認証が必要です。

ユーザーのつながりにアクセスできますか?

いいえ。プライバシーポリシー強化により、ほとんどのアプリはつながりAPIにアクセスできません。

Top comments (0)