要約
Hootsuite APIは、開発者がソーシャルメディア管理ワークフローとプログラム的に統合することを可能にします。OAuth 2.0認証、プロフィール、投稿、分析、チーム管理のためのRESTfulエンドポイントを使用し、プランに応じて1分あたり50〜200リクエストのレート制限があります。このガイドでは、認証設定、投稿スケジュール、分析取得、チーム管理、本番環境への統合戦略について説明します。
注意: Hootsuiteは2024年をもってそのパブリックAPIを廃止しました。このガイドでは、Hootsuiteのパートナー統合、Webhook、同様の機能を提供するサードパーティ製ソーシャルメディア管理APIなど、代替アプローチについて説明します。
はじめに
Hootsuiteは、175カ国以上で20万社以上の企業のために3,000万以上ものソーシャルメディアアカウントを管理しています。ソーシャルメディアツール、マーケティングプラットフォーム、分析ダッシュボードを構築する開発者にとって、この膨大なビジネスオーディエンスにリーチするには、ソーシャルメディアAPIの統合が不可欠です。
現実として、20以上のアカウントを管理するソーシャルメディア担当者は、手動での投稿、エンゲージメント追跡、レポート作成に毎週25〜35時間を費やしています。堅牢なソーシャルメディアAPI統合は、コンテンツ配信、エンゲージメント監視、感情分析、パフォーマンスレポート作成を自動化します。
Hootsuite APIの現状と代替手段
現在のAPI状況
2024年現在、HootsuiteはそのパブリックAPIを廃止しています。統合のための選択肢は以下の通りです。
| アプローチ | 説明 | 最適 |
|---|---|---|
| ネイティブプラットフォームAPI | Facebook、Twitter、LinkedInなどとの直接統合 | 完全な制御、カスタムソリューション |
| Audiense | Hootsuiteが所有するオーディエンスインテリジェンス | オーディエンス分析 |
| HeyOrca | ソーシャルメディアスケジューリングAPI | コンテンツカレンダー |
| Buffer API | ソーシャルメディア管理 | 小規模チーム |
| Sprout Social API | エンタープライズソーシャル管理 | 大規模組織 |
| Agorapulse API | ソーシャルメディアCRM | コミュニティ管理 |
推奨されるアプローチ: ネイティブプラットフォームAPI
ほとんどのユースケースにおいて、ソーシャルプラットフォームと直接統合することが最も柔軟性を提供します。
| プラットフォーム | API名 | 主要機能 |
|---|---|---|
| Facebook/Instagram | Graph API | 投稿、インサイト、コメント |
| Twitter/X | API v2 | ツイート、分析、ストリーム |
| Marketing API | 投稿、企業ページ、広告 | |
| API v5 | ピン、ボード、分析 | |
| TikTok | Display API | 動画、ユーザー情報 |
| YouTube | Data API | 動画、プレイリスト、分析 |
開始する: マルチプラットフォーム認証
ステップ1: 開発者アプリの登録
各プラットフォームの開発者アカウントを作成します。
// Store credentials securely
const SOCIAL_CREDENTIALS = {
facebook: {
appId: process.env.FB_APP_ID,
appSecret: process.env.FB_APP_SECRET,
redirectUri: process.env.FB_REDIRECT_URI
},
twitter: {
apiKey: process.env.TWITTER_API_KEY,
apiSecret: process.env.TWITTER_API_SECRET,
redirectUri: process.env.TWITTER_REDIRECT_URI
},
linkedin: {
clientId: process.env.LINKEDIN_CLIENT_ID,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
redirectUri: process.env.LINKEDIN_REDIRECT_URI
},
instagram: {
appId: process.env.FB_APP_ID, // Uses Facebook Login
appSecret: process.env.FB_APP_SECRET
}
};
ステップ2: OAuth 2.0フローの実装
複数のプラットフォームに対応する統一OAuthハンドラー:
const getAuthUrl = (platform, state) => {
const configs = {
facebook: {
url: 'https://www.facebook.com/v18.0/dialog/oauth',
params: {
client_id: SOCIAL_CREDENTIALS.facebook.appId,
redirect_uri: SOCIAL_CREDENTIALS.facebook.redirectUri,
scope: 'pages_manage_posts,pages_read_engagement,instagram_basic,instagram_content_publish',
state
}
},
twitter: {
url: 'https://twitter.com/i/oauth2/authorize',
params: {
client_id: SOCIAL_CREDENTIALS.twitter.apiKey,
redirect_uri: SOCIAL_CREDENTIALS.twitter.redirectUri,
scope: 'tweet.read tweet.write users.read offline.access',
state,
response_type: 'code'
}
},
linkedin: {
url: 'https://www.linkedin.com/oauth/v2/authorization',
params: {
client_id: SOCIAL_CREDENTIALS.linkedin.clientId,
redirect_uri: SOCIAL_CREDENTIALS.linkedin.redirectUri,
scope: 'w_member_social r_basicprofile',
state,
response_type: 'code'
}
}
};
const config = configs[platform];
const params = new URLSearchParams(config.params);
return `${config.url}?${params.toString()}`;
};
// Handle OAuth callback
const handleOAuthCallback = async (platform, code) => {
const tokenEndpoints = {
facebook: 'https://graph.facebook.com/v18.0/oauth/access_token',
twitter: 'https://api.twitter.com/2/oauth2/token',
linkedin: 'https://www.linkedin.com/oauth/v2/accessToken'
};
const response = await fetch(tokenEndpoints[platform], {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: SOCIAL_CREDENTIALS[platform].apiKey || SOCIAL_CREDENTIALS[platform].appId || SOCIAL_CREDENTIALS[platform].clientId,
client_secret: SOCIAL_CREDENTIALS[platform].appSecret || SOCIAL_CREDENTIALS[platform].apiSecret,
redirect_uri: SOCIAL_CREDENTIALS[platform].redirectUri,
code,
grant_type: 'authorization_code'
})
});
return response.json();
};
ステップ3: トークンを安全に保存する
// Database schema for social tokens
const SocialToken = {
userId: 'user_123',
platform: 'facebook',
accessToken: 'encrypted_token_here',
refreshToken: 'encrypted_refresh_token',
tokenExpiry: Date.now() + 5183999, // 60 days
scopes: ['pages_manage_posts', 'pages_read_engagement'],
pageId: 'page_456', // For Facebook/Instagram
pageName: 'My Business Page'
};
投稿のスケジュールと公開
マルチプラットフォーム投稿の作成
複数のプラットフォームに同時に公開します。
const createSocialPost = async (postData) => {
const results = {};
// Facebook Page Post
if (postData.platforms.includes('facebook')) {
results.facebook = await postToFacebook({
pageId: postData.facebookPageId,
message: postData.message,
link: postData.link,
photo: postData.photo
});
}
// Twitter Post
if (postData.platforms.includes('twitter')) {
results.twitter = await postToTwitter({
text: postData.message,
media: postData.photo
});
}
// LinkedIn Post
if (postData.platforms.includes('linkedin')) {
results.linkedin = await postToLinkedIn({
authorUrn: postData.linkedinAuthorUrn,
text: postData.message,
contentUrl: postData.link
});
}
// Instagram Post
if (postData.platforms.includes('instagram')) {
results.instagram = await postToInstagram({
igAccountId: postData.igAccountId,
imageUrl: postData.photo,
caption: postData.message
});
}
return results;
};
// Facebook posting
const postToFacebook = async (postData) => {
const token = await getFacebookPageToken(postData.pageId);
const params = new URLSearchParams({
message: postData.message,
access_token: token
});
if (postData.link) {
params.append('link', postData.link);
}
if (postData.photo) {
params.append('photo', postData.photo);
}
const response = await fetch(
`https://graph.facebook.com/v18.0/${postData.pageId}/feed?${params.toString()}`,
{ method: 'POST' }
);
return response.json();
};
// Twitter posting
const postToTwitter = async (postData) => {
const token = await getTwitterToken();
let mediaIds = [];
if (postData.media) {
// Upload media first
const mediaUpload = await uploadTwitterMedia(postData.media, token);
mediaIds = [mediaUpload.media_id_string];
}
const response = await fetch('https://api.twitter.com/2/tweets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: postData.text,
media: mediaIds.length > 0 ? { media_ids: mediaIds } : undefined
})
});
return response.json();
};
// LinkedIn posting
const postToLinkedIn = async (postData) => {
const token = await getLinkedInToken();
const post = {
author: postData.authorUrn,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text: postData.text
},
shareMediaCategory: postData.contentUrl ? 'ARTICLE' : 'NONE',
media: postData.contentUrl ? [{
status: 'READY',
media: postData.contentUrn,
description: { text: postData.text }
}] : []
}
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
}
};
const response = await fetch('https://api.linkedin.com/v2/ugcPosts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'X-Restli-Protocol-Version': '2.0.0'
},
body: JSON.stringify(post)
});
return response.json();
};
// Instagram posting
const postToInstagram = async (postData) => {
const token = await getInstagramToken();
// Step 1: Create media container
const containerResponse = await fetch(
`https://graph.facebook.com/v18.0/${postData.igAccountId}/media`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: JSON.stringify({
image_url: postData.imageUrl,
caption: postData.caption
})
}
);
const container = await containerResponse.json();
// Step 2: Publish
const publishResponse = await fetch(
`https://graph.facebook.com/v18.0/${postData.igAccountId}/media_publish`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ creation_id: container.id })
}
);
return publishResponse.json();
};
投稿のスケジュール設定
投稿のスケジュール設定を実装します。
const schedulePost = async (postData, scheduledTime) => {
// Store in database for later execution
const scheduledPost = await db.scheduledPosts.create({
message: postData.message,
platforms: postData.platforms,
scheduledTime: scheduledTime,
status: 'pending',
media: postData.media,
link: postData.link
});
// Set up job queue
await jobQueue.add('publish-social-post', {
postId: scheduledPost.id
}, {
delay: scheduledTime - Date.now()
});
return scheduledPost;
};
// Job processor
jobQueue.process('publish-social-post', async (job) => {
const post = await db.scheduledPosts.findById(job.data.postId);
try {
const result = await createSocialPost(post);
await db.scheduledPosts.update(post.id, {
status: 'published',
publishedAt: new Date(),
results: result
});
return result;
} catch (error) {
await db.scheduledPosts.update(post.id, {
status: 'failed',
error: error.message
});
throw error;
}
});
分析とレポート
クロスプラットフォーム分析の取得
すべてのプラットフォームからメトリクスを集計します。
const getSocialAnalytics = async (accountId, dateRange) => {
const analytics = {
facebook: await getFacebookAnalytics(accountId.facebook, dateRange),
twitter: await getTwitterAnalytics(accountId.twitter, dateRange),
linkedin: await getLinkedInAnalytics(accountId.linkedin, dateRange),
instagram: await getInstagramAnalytics(accountId.instagram, dateRange)
};
// Aggregate metrics
const totals = {
impressions: sum(analytics, 'impressions'),
engagement: sum(analytics, 'engagement'),
clicks: sum(analytics, 'clicks'),
shares: sum(analytics, 'shares'),
comments: sum(analytics, 'comments'),
newFollowers: sum(analytics, 'newFollowers')
};
return { analytics, totals };
};
// Facebook Insights
const getFacebookAnalytics = async (pageId, dateRange) => {
const token = await getFacebookPageToken(pageId);
const metrics = [
'page_impressions_unique',
'page_engaged_users',
'page_post_engagements',
'page_clicks',
'page_fan_adds'
];
const params = new URLSearchParams({
metric: metrics.join(','),
since: dateRange.from,
until: dateRange.until,
access_token: token
});
const response = await fetch(
`https://graph.facebook.com/v18.0/${pageId}/insights?${params.toString()}`
);
return response.json();
};
// Twitter Analytics
const getTwitterAnalytics = async (userId, dateRange) => {
const token = await getTwitterToken();
const response = await fetch(
`https://api.twitter.com/2/users/${userId}/metrics/private`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.json();
};
// LinkedIn Analytics
const getLinkedInAnalytics = async (organizationId, dateRange) => {
const token = await getLinkedInToken();
const response = await fetch(
`https://api.linkedin.com/v2/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=${organizationId}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.json();
};
// Instagram Insights
const getInstagramAnalytics = async (igAccountId, dateRange) => {
const token = await getInstagramToken();
const metrics = [
'impressions',
'reach',
'engagement',
'profile_views',
'follower_count'
];
const params = new URLSearchParams({
metric: metrics.join(','),
period: 'day',
since: dateRange.from,
until: dateRange.until
});
const response = await fetch(
`https://graph.facebook.com/v18.0/${igAccountId}/insights?${params.toString()}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return response.json();
};
function sum(analytics, metric) {
return Object.values(analytics).reduce((total, platform) => {
return total + (platform.data?.[metric] || 0);
}, 0);
}
チーム管理
ロールベースのアクセス制御
const TEAM_ROLES = {
ADMIN: 'admin',
MANAGER: 'manager',
CONTRIBUTOR: 'contributor',
VIEWER: 'viewer'
};
const ROLE_PERMISSIONS = {
[TEAM_ROLES.ADMIN]: ['create', 'read', 'update', 'delete', 'manage_team', 'billing'],
[TEAM_ROLES.MANAGER]: ['create', 'read', 'update', 'approve_posts'],
[TEAM_ROLES.CONTRIBUTOR]: ['create', 'read'],
[TEAM_ROLES.VIEWER]: ['read']
};
const checkPermission = (userRole, requiredPermission) => {
const permissions = ROLE_PERMISSIONS[userRole] || [];
return permissions.includes(requiredPermission);
};
レート制限
プラットフォームのレート制限
| プラットフォーム | 制限 | 期間 |
|---|---|---|
| Facebook Graph | 200コール | ユーザーごとに1時間あたり |
| Twitter API v2 | 300ツイート | 15分あたり |
| 100-500コール | 1日あたり | |
| 200コール | 1時間あたり |
レート制限処理の実装
class SocialMediaRateLimiter {
constructor() {
this.limits = {
facebook: { limit: 200, window: 3600000 },
twitter: { limit: 300, window: 900000 },
linkedin: { limit: 500, window: 86400000 },
instagram: { limit: 200, window: 3600000 }
};
this.counters = {};
}
async request(platform, endpoint, options) {
await this.waitForCapacity(platform);
const response = await fetch(endpoint, options);
this.incrementCounter(platform);
return response;
}
async waitForCapacity(platform) {
const limit = this.limits[platform];
const counter = this.counters[platform] || { count: 0, resetTime: Date.now() };
if (Date.now() > counter.resetTime + limit.window) {
counter.count = 0;
counter.resetTime = Date.now();
}
if (counter.count >= limit.limit) {
const waitTime = counter.resetTime + limit.window - Date.now();
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.counters[platform] = counter;
}
incrementCounter(platform) {
if (!this.counters[platform]) {
this.counters[platform] = { count: 0, resetTime: Date.now() };
}
this.counters[platform].count++;
}
}
本番環境展開チェックリスト
公開前に:
- [ ] すべてのプラットフォームでOAuth 2.0を実装する
- [ ] トークンを暗号化して安全に保存する
- [ ] 自動トークン更新を設定する
- [ ] プラットフォームごとにレート制限を実装する
- [ ] 包括的なエラー処理を追加する
- [ ] すべてのAPI呼び出しのロギングを設定する
- [ ] 投稿承認ワークフローを作成する
- [ ] コンテンツモデレーションを実装する
- [ ] 分析集計を設定する
- [ ] バックアップ投稿メカニズムを作成する
実世界のユースケース
ソーシャルメディアダッシュボード
マーケティング代理店が統一ダッシュボードを構築します。
- 課題: 複数のプラットフォームで50以上のクライアントアカウントを管理する
- 解決策: マルチプラットフォーム投稿機能を備えた中央ダッシュボード
- 結果: 60%の時間削減、一貫したブランドプレゼンス
自動コンテンツ配信
パブリッシャーが記事共有を自動化します。
- 課題: 新しいコンテンツの手動共有
- 解決策: 新しい記事をすべてのプラットフォームに自動投稿する
- 結果: 即時配信、ソーシャルトラフィック3倍
結論
HootsuiteのパブリックAPIは廃止されましたが、ネイティブプラットフォームAPIは包括的なソーシャルメディア管理機能を提供します。主なポイントは以下の通りです。
- 各プラットフォームでOAuth 2.0を個別に実装する
- レート制限はプラットフォームによって大きく異なる
- 統一された投稿にはプラットフォーム固有の実装が必要
- 分析集計はクロスプラットフォームのインサイトを提供する
- ApidogはAPIテストとチームコラボレーションを効率化する
FAQ (よくある質問)
HootsuiteにはまだAPIがありますか?
Hootsuiteは2024年にパブリックAPIを廃止しました。ネイティブプラットフォームAPIまたはBuffer、Sprout Social、Agorapulseのような代替管理プラットフォームを使用してください。
複数のプラットフォームに同時に投稿するにはどうすればよいですか?
各プラットフォームのOAuthを実装し、各プラットフォームのAPIを並行して呼び出す統一された投稿機能を作成します。
ソーシャルメディアAPIのレート制限はどのくらいですか?
制限は異なります。Facebook (1時間あたり200回)、Twitter (15分あたり300ツイート)、LinkedIn (1日あたり100-500回)、Instagram (1時間あたり200回)。
投稿をスケジュールするにはどうすればよいですか?
投稿をscheduled_timeとともにデータベースに保存し、ジョブキュー (Bull, Agenda) を使用して指定された時間に公開します。
すべてのプラットフォームから分析データを取得できますか?
はい、各プラットフォームは分析APIを提供しています。クロスプラットフォームレポートのためにデータを集計してください。
