要するに
InstagramグラフAPIは、Instagramビジネスアカウントおよびクリエイターアカウントをプログラムで管理できるようにします。FacebookログインOAuth 2.0認証、コンテンツ公開、インサイト、コメント、メッセージングのためのGraphQLベースのエンドポイントを使用し、アプリごとに1時間あたり200件のAPI呼び出しというレート制限があります。このガイドでは、認証設定、コンテンツ公開、インサイト取得、コメント管理、および本番環境での統合戦略について、実装手順を中心に解説します。
はじめに
Instagramは20億人を超える月間アクティブユーザーを持ち、2億以上の企業がInstagramビジネスアカウントを利用しています。ソーシャルメディア管理ツールや分析プラットフォーム、Eコマース統合を構築する開発者にとって、InstagramグラフAPIとの連携は必須です。
手動で複数アカウントを管理する場合、毎週20~30時間を消費します。APIを活用することで、コンテンツ公開/コメントモデレーション/インサイト取得/レポーティングなどを自動化できます。
本ガイドは、公式ドキュメントを補完し、Facebookログイン認証からAPI連携、Webhook、デプロイまで、現場で使えるコード例と手順を詳述します。
💡 ApidogはAPI統合テストを効率化します。Instagramのエンドポイントテスト、OAuth認証フローの検証、レスポンスの検査・モック、テストシナリオの共有までワークスペースで一括管理できます。
InstagramグラフAPIとは?
InstagramグラフAPIは、FacebookグラフAPI経由でInstagramビジネスアカウントおよびクリエイターアカウントにプログラムからアクセス可能にするAPIです。主要な用途は以下です。
- コンテンツ公開(写真・動画・リール・カルーセル)
- メディアインサイト取得
- コメント・メンション管理
- ダイレクトメッセージ(Messenger Platformと組み合わせ)
- ハッシュタグ・メンションのトラッキング
- ストーリー管理
- ショッピング・商品タグ
主な機能
| 機能 | 説明 |
|---|---|
| グラフベースAPI | ノードベースのリソースアクセス |
| OAuth 2.0 | Facebookログイン認証 |
| Webhooks | コメント・メンションのリアルタイム通知 |
| レート制限 | アプリごとに1時間200回 |
| コンテンツ公開 | 写真・動画・リール・カルーセル |
| インサイト | エンゲージメント・リーチ・インプレッション |
| モデレーション | コメント・メンション・メッセージ管理 |
アカウント要件
| アカウントタイプ | APIアクセス |
|---|---|
| ビジネスアカウント | 全APIアクセス |
| クリエイターアカウント | 全APIアクセス |
| 個人アカウント | APIアクセス不可(変換必須) |
| 非公開アカウント | 一部インサイトのみ |
APIアーキテクチャの概要
APIベースURL例:
https://graph.facebook.com/v18.0/
APIバージョンの比較
| バージョン | ステータス | 終了日 | ユースケース |
|---|---|---|---|
| v18.0 | 現在 | 2026年3月 | すべての新しい統合 |
| v17.0 | 非推奨 | 2026年1月 | 既存の統合 |
| v16.0 | 廃止済み | 期限切れ | 使用不可 |
四半期ごとにバージョンアップあり。常に最新バージョンで開発しましょう。
はじめに: 認証設定
ステップ1: Facebook開発者アカウントの作成
- Facebook開発者ポータルにアクセス
- Facebookアカウントでサインイン
- 新規アプリ(タイプ: ビジネス)作成
- InstagramグラフAPI製品を追加
ステップ2: Instagramビジネスアカウントのリンク
- Facebookページの「設定」→「Instagram」へ移動
- 「アカウントを接続」をクリック
- Instagramへログインし認証
- ビジネスアカウントとしてリンクされていることを確認
※ 個人アカウントはGraph API不可。設定からビジネス/クリエイターへ変換必須。
ステップ3: アクセストークンの取得
OAuth認証URLを生成し、ユーザーに認可を促します。
const FB_APP_ID = process.env.FB_APP_ID;
const FB_APP_SECRET = process.env.FB_APP_SECRET;
const FB_REDIRECT_URI = process.env.FB_REDIRECT_URI;
const getAuthUrl = (state) => {
const params = new URLSearchParams({
client_id: FB_APP_ID,
redirect_uri: FB_REDIRECT_URI,
scope: 'instagram_basic,instagram_content_publish,instagram_manage_comments,instagram_manage_insights,pages_read_engagement',
state: state
});
return `https://www.facebook.com/v18.0/dialog/oauth?${params.toString()}`;
};
必要な権限
| 権限 | 説明 |
|---|---|
instagram_basic |
プロフィール・メディアリスト |
instagram_content_publish |
写真・動画・カルーセル公開 |
instagram_manage_comments |
コメント操作 |
instagram_manage_insights |
インサイト取得 |
pages_read_engagement |
ページアクセス |
pages_manage_posts |
ページ経由での公開 |
ステップ4: 長期間有効なトークンへ交換
短期(1時間)トークンを長期(60日)有効トークンに交換します。
const exchangeForLongLivedToken = async (shortLivedToken) => {
const response = await fetch(
`https://graph.facebook.com/v18.0/oauth/access_token?` +
`grant_type=fb_exchange_token&` +
`client_id=${FB_APP_ID}&` +
`client_secret=${FB_APP_SECRET}&` +
`fb_exchange_token=${shortLivedToken}`
);
const data = await response.json();
return data;
};
// 例
const longLivedToken = await exchangeForLongLivedToken(shortLivedToken);
console.log(`Token expires: ${new Date(longLivedToken.expires_at * 1000)}`);
ステップ5: InstagramビジネスアカウントIDの取得
const getInstagramAccountId = async (pageId, accessToken) => {
const response = await fetch(
`https://graph.facebook.com/v18.0/${pageId}?fields=instagram_business_account&access_token=${accessToken}`
);
const data = await response.json();
return data.instagram_business_account.id;
};
// 例
const igAccountId = await getInstagramAccountId('12345678', accessToken);
console.log(`Instagram Account ID: ${igAccountId}`);
ステップ6: 認証済みAPI呼び出し
再利用可能なAPIクライアント例:
const IG_BASE_URL = 'https://graph.facebook.com/v18.0';
const instagramRequest = async (endpoint, params = {}) => {
const url = new URL(`${IG_BASE_URL}${endpoint}`);
url.searchParams.append('access_token', process.env.INSTAGRAM_ACCESS_TOKEN);
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
const response = await fetch(url.toString());
if (!response.ok) {
const error = await response.json();
throw new Error(`Instagram API Error: ${error.error.message}`);
}
return response.json();
};
// 例
const account = await instagramRequest(`/me`);
console.log(`Instagram Account: ${account.username}`);
コンテンツの公開
写真の公開
const publishPhoto = async (igAccountId, photoData) => {
// メディアコンテナ作成
const containerResponse = await instagramRequest(`/${igAccountId}/media`, {
method: 'POST',
image_url: photoData.imageUrl,
caption: photoData.caption,
location_id: photoData.locationId, // 任意
is_carousel_item: 'false'
});
const creationId = containerResponse.id;
// メディア公開
const publishResponse = await instagramRequest(`/${igAccountId}/media_publish`, {
method: 'POST',
creation_id: creationId
});
return publishResponse;
};
// 使用例
const post = await publishPhoto({
igAccountId: '17841400000000000',
imageUrl: 'https://example.com/image.jpg',
caption: 'Excited to announce our new product! 🚀 #launch #innovation',
locationId: '123456789' // 任意
});
console.log(`Published media ID: ${post.id}`);
動画の公開
const publishVideo = async (igAccountId, videoData) => {
// メディアコンテナ作成
const containerResponse = await instagramRequest(`/${igAccountId}/media`, {
method: 'POST',
video_url: videoData.videoUrl,
cover_url: videoData.coverUrl, // サムネイル任意
caption: videoData.caption,
media_type: 'REELS', // フィードなら'VIDEO'
share_to_feed: 'true'
});
const creationId = containerResponse.id;
// 動画処理完了までポーリング
await waitForVideoProcessing(creationId);
// メディア公開
const publishResponse = await instagramRequest(`/${igAccountId}/media_publish`, {
method: 'POST',
creation_id: creationId
});
return publishResponse;
};
const waitForVideoProcessing = async (creationId, maxAttempts = 30) => {
for (let i = 0; i < maxAttempts; i++) {
const status = await instagramRequest(`/${creationId}`);
if (status.status_code === 'FINISHED') {
return true;
} else if (status.status_code === 'EXPIRED') {
throw new Error('Video processing expired');
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error('Video processing timeout');
};
カルーセル(複数メディア)の公開
const publishCarousel = async (igAccountId, carouselData) => {
const children = [];
// 各メディアコンテナ作成
for (const item of carouselData.items) {
const containerResponse = await instagramRequest(`/${igAccountId}/media`, {
method: 'POST',
[item.type === 'video' ? 'video_url' : 'image_url']: item.url,
caption: item.caption || '',
is_carousel_item: 'true'
});
children.push(containerResponse.id);
}
// カルーセルコンテナ作成
const carouselContainerResponse = await instagramRequest(`/${igAccountId}/media`, {
method: 'POST',
media_type: 'CAROUSEL',
children: children.join(','),
caption: carouselData.caption
});
const creationId = carouselContainerResponse.id;
// カルーセル公開
const publishResponse = await instagramRequest(`/${igAccountId}/media_publish`, {
method: 'POST',
creation_id: creationId
});
return publishResponse;
};
// 使用例
const carousel = await publishCarousel('17841400000000000', {
caption: 'Product showcase 2026',
items: [
{ type: 'image', url: 'https://example.com/img1.jpg', caption: 'Product 1' },
{ type: 'image', url: 'https://example.com/img2.jpg', caption: 'Product 2' },
{ type: 'video', url: 'https://example.com/vid1.mp4', caption: 'Demo' }
]
});
メディアタイプ
| メディアタイプ | パラメータ | ユースケース |
|---|---|---|
IMAGE |
image_url, caption | 写真 |
VIDEO |
video_url, cover_url, caption | 動画 |
REELS |
video_url, cover_url, caption, share_to_feed | リール |
CAROUSEL |
children(配列), caption | 複数メディア |
メディアとインサイトの取得
ユーザーメディアの取得
const getUserMedia = async (igAccountId, limit = 25) => {
const response = await instagramRequest(`/${igAccountId}/media`, {
fields: 'id,caption,media_type,media_url,permalink,timestamp,like_count,comments_count',
limit: limit.toString()
});
return response;
};
// 使用例
const media = await getUserMedia('17841400000000000');
media.data.forEach(item => {
console.log(`${item.media_type}: ${item.caption}`);
console.log(`Likes: ${item.like_count}, Comments: ${item.comments_count}`);
console.log(`URL: ${item.permalink}`);
});
メディアインサイトの取得
const getMediaInsights = async (mediaId) => {
const response = await instagramRequest(`/${mediaId}/insights`, {
fields: 'impressions,reach,engagement,saved,video_views,profile_visits,follows'
});
return response;
};
// 使用例
const insights = await getMediaInsights('17890000000000000');
insights.data.forEach(metric => {
console.log(`${metric.name}: ${metric.values[0].value}`);
});
利用可能なインサイト指標
| 指標 | 説明 | メディアタイプ |
|---|---|---|
impressions |
合計ビュー数 | 全て |
reach |
到達ユニークアカウント数 | 全て |
engagement |
いいね+コメント+保存 | 全て |
saved |
保存回数 | 全て |
video_views |
3秒以上の動画視聴回数 | 動画・リール |
plays |
合計動画再生数 | 動画・リール |
profile_visits |
投稿からのプロフィール訪問数 | 全て |
follows |
投稿からのフォロー | 全て |
comments |
コメント数 | 全て |
like_count |
いいね数 | 全て |
アカウントインサイトの取得
const getAccountInsights = async (igAccountId, metricNames, since = null, until = null) => {
const params = {
metric: metricNames.join(','),
period: 'day'
};
if (since) params.since = since;
if (until) params.until = until;
const response = await instagramRequest(`/${igAccountId}/insights`, params);
return response;
};
// 例:直近30日
const accountInsights = await getAccountInsights(
'17841400000000000',
['impressions', 'reach', 'profile_views', 'email_contacts', 'website_clicks'],
'2026-02-23',
'2026-03-25'
);
accountInsights.data.forEach(metric => {
console.log(`${metric.name}:`);
metric.values.forEach(value => {
console.log(` ${value.end_time}: ${value.value}`);
});
});
アカウントレベルの指標
| 指標 | 説明 |
|---|---|
impressions |
プロフィール+コンテンツ表示数 |
reach |
到達ユニークアカウント数 |
profile_views |
プロフィール訪問数 |
website_clicks |
プロフィールリンククリック数 |
email_contacts |
メールボタンタップ数 |
phone_call_clicks |
電話ボタンタップ数 |
text_message_clicks |
SMSボタンタップ数 |
get_directions_clicks |
住所クリック数 |
follower_count |
合計フォロワー数 |
audience_city |
フォロワー都市 |
audience_country |
フォロワー国 |
audience_gender_age |
人口統計 |
コメント管理
コメントの取得
const getMediaComments = async (mediaId, limit = 50) => {
const response = await instagramRequest(`/${mediaId}/comments`, {
fields: 'id,text,timestamp,username,hidden',
limit: limit.toString()
});
return response;
};
// 使用例
const comments = await getMediaComments('17890000000000000');
comments.data.forEach(comment => {
console.log(`@${comment.username}: ${comment.text}`);
console.log(`Hidden: ${comment.hidden}`);
});
コメントへの返信
const replyToComment = async (mediaId, commentId, replyText) => {
const response = await instagramRequest(`/${mediaId}/comments`, {
method: 'POST',
response_to: commentId,
message: replyText
});
return response;
};
// 使用例
const reply = await replyToComment(
'17890000000000000',
'17900000000000000',
'Thank you for your interest! Check your DM for details.'
);
console.log(`Reply posted: ${reply.id}`);
コメントの非表示
const hideComment = async (commentId) => {
const response = await instagramRequest(`/${commentId}`, {
method: 'POST',
hide: 'true'
});
return response;
};
// 使用例
await hideComment('17900000000000000');
console.log('Comment hidden');
コメントの削除
const deleteComment = async (commentId) => {
await instagramRequest(`/${commentId}`, {
method: 'DELETE'
});
console.log('Comment deleted');
};
Webhook
Webhookの構成
const subscribeToWebhooks = async (appId, pageId, accessToken) => {
const response = await fetch(
`https://graph.facebook.com/v18.0/${appId}/subscriptions`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
object: 'instagram',
callback_url: 'https://myapp.com/webhooks/instagram',
verify_token: process.env.WEBHOOK_VERIFY_TOKEN,
access_token: accessToken,
fields: ['comments', 'mentions', 'message_reactions']
})
}
);
return response.json();
};
Webhookの処理
const express = require('express');
const app = express();
// 検証
app.get('/webhooks/instagram', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.WEBHOOK_VERIFY_TOKEN) {
res.status(200).send(challenge);
} else {
res.status(403).send('Verification failed');
}
});
// イベント受信
app.post('/webhooks/instagram', express.json(), async (req, res) => {
const body = req.body;
if (body.object !== 'instagram') {
return res.status(404).send('Not found');
}
for (const entry of body.entry) {
const igId = entry.id;
const changes = entry.changes;
for (const change of changes) {
switch (change.field) {
case 'comments':
await handleNewComment(change.value);
break;
case 'mentions':
await handleMention(change.value);
break;
case 'message_reactions':
await handleReaction(change.value);
break;
}
}
}
res.status(200).send('OK');
});
async function handleNewComment(data) {
console.log(`New comment on media ${data.media_id}`);
console.log(`From: ${data.from_id}`);
console.log(`Text: ${data.text}`);
// スパムなら自動非表示
if (isSpam(data.text)) {
await hideComment(data.id);
}
}
Webhookフィールド
| フィールド | トリガー |
|---|---|
comments |
新規コメント/返信 |
mentions |
アカウントのメンション |
message_reactions |
ストーリーへのリアクション |
story_status |
ストーリーの返信/表示 |
レート制限
レート制限の理解
- アプリごとに1時間200回(全ユーザー合算)
- ビジネスディスカバリー:ユーザーごとに1時間200回
- コンテンツ公開:アクション別に制限あり
超過するとHTTP 400(サブコード613)エラー。
レート制限のベストプラクティス
- 応答キャッシュ - 変更がないデータは再取得しない
- バッチリクエスト - フィールド展開で呼び出し数削減
- Webhook活用 - ポーリングではなくリアルタイム取得
- バックオフ実装 - 429エラー時は指数バックオフ
const makeRateLimitedRequest = async (endpoint, params = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await instagramRequest(endpoint, params);
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`レート制限に達しました。${delay}ms後に再試行します...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
一般的な問題のトラブルシューティング
問題: OAuthトークンの期限切れ
症状: 「無効なOAuthアクセストークン」エラー
解決策:
- 60日以内にトークン更新を自動化
- 有効期限を保存し、期限前にアラート
- 期限切れなら再認証リダイレクト
問題: メディア公開の失敗
症状: 公開時エラー
解決策:
- 画像URLが公開アクセス可能か確認
- 画像形式(JPEG/PNG)、サイズ(8MB未満)を確認
- 動画はMP4、1GB未満、90秒未満
- 動画処理完了まで待機
問題: インサイトが利用できない
症状: インサイトAPIが空データを返す
解決策:
- ビジネス/クリエイターアカウントであることの確認
- インサイト生成まで24~48時間待つ
- アカウントが十分アクティブであるか確認
本番デプロイチェックリスト
- [ ] テストアカウントをビジネス/クリエイターに変換
- [ ] 長期トークンでOAuth 2.0実装
- [ ] トークンを暗号化・安全に保存
- [ ] 自動トークン更新の実装
- [ ] WebhookエンドポイントをHTTPSで公開
- [ ] レート制限対策・リクエストキュー導入
- [ ] エラー処理の実装
- [ ] API呼び出しのロギング
- [ ] コンテンツモデレーションワークフロー作成
- [ ] 複数アカウントタイプでテスト
実世界のユースケース
ソーシャルメディアスケジュールツール
- 課題: 50超のクライアントアカウントを手動で投稿管理
- 解決策: Instagram APIでスケジュール自動投稿
- 結果: 作業時間80%削減・一貫した投稿
実装例
- コンテンツカレンダーでのドラッグ&ドロップスケジューリング
- 写真・動画・カルーセルの自動公開
- 投稿内容からハッシュタグ自動提案
カスタマーサービス自動化
- 課題: 顧客対応の遅延
- 解決策: Webhook+キーワード検出で自動返信
- 結果: 平均応答5分、満足度90%
実装例
- 「価格」「在庫」「発送」などの自動応答
- 商品リンク付き自動返信
- 複雑な問合せは人間エージェントへ転送
結論
InstagramグラフAPIはビジネス/クリエイターアカウントの自動化に最適なAPIです。
- 60日間有効なOAuth 2.0トークンで認証
- 写真・動画・リール・カルーセルのAPI投稿
- 各種インサイト取得・分析
- Webhookでコメント・メンションを即時取得
- レート制限対策必須(1時間200回)
- ApidogでAPIテスト・コラボレーションを効率化
FAQセクション
Instagram APIにアクセスするにはどうすればよいですか?
Facebook開発者アカウント+ビジネスアプリ作成+InstagramグラフAPI製品追加+OAuth認証権限付与で利用できます。
Instagramに自動的に投稿できますか?
はい。API経由で写真・動画・リール・カルーセルの自動投稿が可能です。
Instagram APIをサポートするアカウントタイプは何ですか?
ビジネスアカウント・クリエイターアカウントのみ。個人アカウントは不可または制限あり。
Instagramからコメントを取得するにはどうすればよいですか?
/{media-id}/commentsエンドポイントで取得。Webhookを使えばリアルタイム通知も受信可能。
Instagramのレート制限は何ですか?
アプリごとに1時間あたり200回。エンドポイントごとに追加制限あり。
API経由でストーリーを公開できますか?
はい。通常投稿と同じフローでストーリー公開も可能です。
Instagramインサイトにアクセスするにはどうすればよいですか?
instagram_manage_insights権限が必要。インサイトエンドポイントで取得できます。
コメントに自動返信できますか?
はい。コメントAPIで自動返信が可能。カスタマーサービス自動化にも応用できます。
Top comments (0)