Hootsuite API 사용법

Ashley Innocent

Ashley Innocent

25 March 2026

Hootsuite API 사용법

요약 (TL;DR)

Hootsuite API는 개발자가 소셜 미디어 관리 워크플로우를 프로그래밍 방식으로 통합할 수 있게 해줍니다. OAuth 2.0 인증, 프로필, 게시물, 분석 및 팀 관리를 위한 RESTful 엔드포인트를 사용하며, 요금제에 따라 분당 50-200개의 요청 제한이 있습니다. 이 가이드는 인증 설정, 게시물 예약, 분석 검색, 팀 관리 및 프로덕션 통합 전략을 다룹니다.

참고: Hootsuite는 2024년부로 공개 API를 중단했습니다. 이 가이드는 Hootsuite의 파트너 통합, 웹훅, 그리고 유사한 기능을 제공하는 타사 소셜 미디어 관리 API를 포함한 대체 접근 방식을 다룹니다.

서론

Hootsuite는 175개 이상의 국가에서 20만 개 이상의 기업을 위해 3천만 개 이상의 소셜 미디어 계정을 관리합니다. 소셜 미디어 도구, 마케팅 플랫폼 또는 분석 대시보드를 구축하는 개발자에게는 이 방대한 비즈니스 잠재 고객에게 도달하기 위해 소셜 미디어 API 통합이 필수적입니다.

현실은 이렇습니다. 20개 이상의 계정을 처리하는 소셜 미디어 관리자들은 수동 게시, 참여 추적 및 보고서 생성에 매주 25-35시간을 낭비합니다. 견고한 소셜 미디어 API 통합은 콘텐츠 배포, 참여 모니터링, 감성 분석 및 성과 보고를 자동화합니다.

button

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 트윗, 분석, 스트림
LinkedIn Marketing API 게시물, 회사 페이지, 광고
Pinterest 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 호출 사용자당 시간당
Twitter API v2 300 트윗 15분당
LinkedIn 100-500 호출 일일
Instagram 200 호출 시간당

속도 제한 처리 구현

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++;
  }
}

운영 환경 배포 체크리스트

서비스 개시 전:

실제 사용 사례

소셜 미디어 대시보드

마케팅 에이전시가 통합 대시보드를 구축:

자동화된 콘텐츠 배포

출판사가 기사 공유를 자동화:

결론

Hootsuite의 공개 API는 중단되었지만, 네이티브 플랫폼 API는 포괄적인 소셜 미디어 관리 기능을 제공합니다. 핵심 요약:

button

FAQ 섹션

Hootsuite는 여전히 API를 제공하나요?

Hootsuite는 2024년에 공개 API를 중단했습니다. 네이티브 플랫폼 API 또는 Buffer, Sprout Social, Agorapulse와 같은 대체 관리 플랫폼을 사용하세요.

여러 플랫폼에 동시에 게시하려면 어떻게 해야 하나요?

각 플랫폼에 대한 OAuth를 구현하고, 각 플랫폼의 API를 병렬로 호출하는 통합 게시 함수를 생성하세요.

소셜 미디어 API의 속도 제한은 무엇인가요?

제한은 다양합니다: Facebook (시간당 200회), Twitter (15분당 300회), LinkedIn (일일 100-500회), Instagram (시간당 200회).

게시물을 예약하려면 어떻게 해야 하나요?

예약 시간과 함께 게시물을 데이터베이스에 저장한 다음, 작업 큐(Bull, Agenda)를 사용하여 예약된 시간에 게시하세요.

모든 플랫폼에서 분석 데이터를 얻을 수 있나요?

네, 각 플랫폼은 분석 API를 제공합니다. 교차 플랫폼 보고를 위해 데이터를 집계하세요.

Apidog에서 API 설계-첫 번째 연습

API를 더 쉽게 구축하고 사용하는 방법을 발견하세요