2026년 인스타그램 그래프 API 사용법

Ashley Innocent

Ashley Innocent

25 March 2026

2026년 인스타그램 그래프 API 사용법

요약

인스타그램 그래프 API는 개발자가 인스타그램 비즈니스 및 크리에이터 계정을 프로그래밍 방식으로 관리할 수 있도록 합니다. 페이스북 로그인 OAuth 2.0 인증, 콘텐츠 게시, 인사이트, 댓글, 메시징을 위한 GraphQL 기반 엔드포인트를 사용하며, 앱당 시간당 200회 호출로 속도 제한이 있습니다. 이 가이드는 인증 설정, 콘텐츠 게시, 인사이트 검색, 댓글 관리 및 프로덕션 통합 전략을 다룹니다.

소개

인스타그램은 월간 활성 사용자 수가 20억 명을 넘으며, 2억 개 이상의 비즈니스가 인스타그램 비즈니스 계정을 사용하고 있습니다. 소셜 미디어 관리 도구, 분석 플랫폼 또는 전자상거래 통합을 구축하는 개발자에게는 이러한 방대한 잠재고객에게 도달하기 위해 인스타그램 그래프 API 통합이 필수적입니다.

현실은 다음과 같습니다. 10개 이상의 계정을 관리하는 소셜 미디어 관리자들은 수동 게시, 댓글 응답, 분석 취합에 매주 20~30시간을 낭비합니다. 견고한 인스타그램 API 통합은 콘텐츠 게시, 댓글 관리, 감성 분석 및 성과 보고를 자동화합니다.

이 가이드는 인스타그램 그래프 API 통합의 전체 과정을 안내합니다. 페이스북 로그인 인증, 콘텐츠 게시, 미디어 인사이트, 댓글 관리, 웹훅 통합 및 프로덕션 배포 전략을 배우게 될 것입니다. 이 가이드를 마치면 프로덕션 환경에 바로 사용할 수 있는 인스타그램 통합을 완료할 수 있습니다.

💡
Apidog는 API 통합 테스트를 간소화합니다. 인스타그램 엔드포인트를 테스트하고, OAuth 흐름을 검증하며, API 응답을 검사하고, 게시 문제를 하나의 작업 공간에서 디버그하세요. API 사양을 가져오고, 응답을 모의(mock)하고, 테스트 시나리오를 팀과 공유할 수 있습니다.

버튼

인스타그램 그래프 API란 무엇인가요?

인스타그램 그래프 API는 페이스북 그래프 API를 통해 인스타그램 비즈니스 및 크리에이터 계정에 대한 프로그래밍 방식 접근을 제공합니다. 이 API는 다음을 처리합니다:

주요 기능

기능 설명
그래프 기반 API 노드 기반 리소스 접근
OAuth 2.0 페이스북 로그인 인증
웹훅 댓글, 언급에 대한 실시간 알림
속도 제한 앱당 시간당 200회 호출
콘텐츠 게시 사진, 동영상, 릴스, 캐러셀
인사이트 참여, 도달, 노출 메트릭
관리 댓글, 언급, 메시지 관리

계정 요구사항

계정 유형 API 접근
비즈니스 계정 전체 API 접근
크리에이터 계정 전체 API 접근
개인 계정 API 접근 불가 (전환 필요)
비공개 계정 제한된 인사이트

API 아키텍처 개요

인스타그램은 페이스북 그래프 API 구조를 사용합니다:

https://graph.facebook.com/v18.0/

API 버전 비교

버전 상태 종료일 사용 사례
v18.0 현재 2026년 3월 모든 새로운 통합
v17.0 더 이상 사용되지 않음 2026년 1월 기존 통합
v16.0 사용 중지됨 만료됨 사용하지 마세요

페이스북은 분기별로 새 버전을 출시합니다. 항상 최신 안정 버전을 대상으로 하세요.

시작하기: 인증 설정

1단계: 페이스북 개발자 계정 생성

API에 접근하기 전에:

  1. 페이스북 개발자 포털 방문
  2. 페이스북 계정으로 로그인
  3. 페이스북 앱 생성 (유형: 비즈니스)
  4. 인스타그램 그래프 API 제품 추가

2단계: 인스타그램 비즈니스 계정 연결

인스타그램을 페이스북 페이지에 연결:

  1. 페이스북 페이지 설정 > 인스타그램으로 이동
  2. 계정 연결 클릭
  3. 인스타그램에 로그인하고 승인
  4. 인스타그램 비즈니스 계정이 연결되었는지 확인

참고: 개인 인스타그램 계정은 그래프 API를 사용할 수 없습니다. 인스타그램 설정에서 비즈니스 또는 크리에이터 계정으로 전환하세요.

3단계: 액세스 토큰 받기

사용자 액세스 토큰 생성:

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;

// Build authorization URL
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;
};

// Usage
const longLivedToken = await exchangeForLongLivedToken(shortLivedToken);
console.log(`Token expires: ${new Date(longLivedToken.expires_at * 1000)}`);

5단계: 인스타그램 비즈니스 계정 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;
};

// Usage
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();
};

// Usage
const account = await instagramRequest(`/me`);
console.log(`Instagram Account: ${account.username}`);

콘텐츠 게시

사진 게시

인스타그램에 사진 게시:

const publishPhoto = async (igAccountId, photoData) => {
  // Step 1: Create media container
  const containerResponse = await instagramRequest(`/${igAccountId}/media`, {
    method: 'POST',
    image_url: photoData.imageUrl,
    caption: photoData.caption,
    location_id: photoData.locationId, // Optional
    is_carousel_item: 'false'
  });

  const creationId = containerResponse.id;

  // Step 2: Publish the media
  const publishResponse = await instagramRequest(`/${igAccountId}/media_publish`, {
    method: 'POST',
    creation_id: creationId
  });

  return publishResponse;
};

// Usage
const post = await publishPhoto({
  igAccountId: '17841400000000000',
  imageUrl: 'https://example.com/image.jpg',
  caption: 'Excited to announce our new product! 🚀 #launch #innovation',
  locationId: '123456789' // Optional
});

console.log(`Published media ID: ${post.id}`);

동영상 게시

인스타그램에 동영상 게시:

const publishVideo = async (igAccountId, videoData) => {
  // Step 1: Create media container
  const containerResponse = await instagramRequest(`/${igAccountId}/media`, {
    method: 'POST',
    video_url: videoData.videoUrl,
    cover_url: videoData.coverUrl, // Optional thumbnail
    caption: videoData.caption,
    media_type: 'REELS', // or 'VIDEO' for feed
    share_to_feed: 'true' // For reels
  });

  const creationId = containerResponse.id;

  // Wait for video processing (poll until status is EXPIRED or FINISHED)
  await waitForVideoProcessing(creationId);

  // Step 2: Publish the media
  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 = [];

  // Step 1: Create each carousel item
  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);
  }

  // Step 2: Create carousel container with children
  const carouselContainerResponse = await instagramRequest(`/${igAccountId}/media`, {
    method: 'POST',
    media_type: 'CAROUSEL',
    children: children.join(','),
    caption: carouselData.caption
  });

  const creationId = carouselContainerResponse.id;

  // Step 3: Publish the carousel
  const publishResponse = await instagramRequest(`/${igAccountId}/media_publish`, {
    method: 'POST',
    creation_id: creationId
  });

  return publishResponse;
};

// Usage
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 (array), 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;
};

// Usage
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;
};

// Usage
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;
};

// Usage - Get last 30 days of metrics
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;
};

// Usage
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;
};

// Usage
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;
};

// Usage
await hideComment('17900000000000000');
console.log('Comment hidden');

댓글 삭제

스팸 또는 부적절한 댓글 삭제:

const deleteComment = async (commentId) => {
  await instagramRequest(`/${commentId}`, {
    method: 'DELETE'
  });

  console.log('Comment deleted');
};

웹훅

웹훅 구성

실시간 알림을 위한 웹훅 설정:

const subscribeToWebhooks = async (appId, pageId, accessToken) => {
  // Subscribe to Instagram events
  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();
};

웹훅 처리

const express = require('express');
const app = express();

// Verify webhook subscription
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) {
    console.log('Webhook verified');
    res.status(200).send(challenge);
  } else {
    res.status(403).send('Verification failed');
  }
});

// Handle webhook events
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}`);

  // Auto-reply or moderate
  if (isSpam(data.text)) {
    await hideComment(data.id);
  }
}

웹훅 필드

필드 트리거
comments 새 댓글 또는 답글
mentions 사용자가 계정을 언급
message_reactions 스토리에 대한 반응
story_status 스토리 답글/조회

속도 제한

속도 제한 이해

인스타그램 그래프 API는 다음을 적용합니다:

제한을 초과하면 오류 하위 코드 613과 함께 HTTP 400이 발생합니다.

속도 제한 모범 사례

  1. 응답 캐싱 - 변경되지 않은 데이터를 다시 가져오지 마세요
  2. 요청 배치 처리 - 필드 확장을 사용하여 호출 수를 줄이세요
  3. 웹훅 사용 - 폴링 대신 실시간 업데이트를 사용하세요
  4. 백오프 구현 - 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(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};

일반적인 문제 해결

문제: OAuth 토큰 만료됨

증상: "유효하지 않은 OAuth 액세스 토큰" 오류가 발생합니다.

해결책:

  1. 60일 만료 전 토큰 새로 고침 구현
  2. 토큰 만료일 저장 및 만료 전 알림
  3. 토큰이 만료된 경우 사용자 재인증

문제: 미디어 게시 실패

증상: 게시 시 오류가 반환됩니다.

해결책:

  1. 이미지 URL이 공개적으로 접근 가능한지 확인하세요 (인증 불필요)
  2. 이미지 형식 (JPEG, PNG) 및 크기 (8MB 미만) 확인
  3. 동영상이 MP4 형식, 1GB 미만, 90초 미만인지 확인
  4. 게시하기 전에 동영상 처리가 완료될 때까지 기다리세요

문제: 인사이트를 사용할 수 없음

증상: 인사이트 API가 빈 데이터를 반환합니다.

해결책:

  1. 계정이 비즈니스 또는 크리에이터 계정인지 확인하세요 (개인 계정 아님)
  2. 인사이트가 채워질 때까지 24-48시간 기다리세요
  3. 계정에 충분한 활동이 있는지 확인하세요

프로덕션 배포 체크리스트

라이브 전환 전에:


실제 사용 사례

소셜 미디어 예약 도구

마케팅 플랫폼이 게시를 자동화합니다:

주요 구현:

고객 서비스 자동화

전자상거래 브랜드가 댓글 응답을 자동화합니다:

주요 구현:

결론

인스타그램 그래프 API는 인스타그램 비즈니스 및 크리에이터 계정 기능에 대한 포괄적인 접근을 제공합니다. 주요 요약:

버튼

자주 묻는 질문

인스타그램 API에 어떻게 접근할 수 있나요?

페이스북 개발자 계정을 생성하고, 비즈니스 앱을 만든 다음, 인스타그램 그래프 API 제품을 추가하고, 필요한 권한으로 페이스북 로그인을 통해 인증하세요.

인스타그램에 자동으로 게시할 수 있나요?

네, 콘텐츠 게시 API를 사용하여 비즈니스 및 크리에이터 계정에 사진, 동영상, 릴스, 캐러셀을 게시할 수 있습니다.

어떤 유형의 인스타그램 계정이 API를 지원하나요?

비즈니스 및 크리에이터 계정만 전체 API 접근 권한을 가집니다. 개인 계정은 제한적이거나 API 접근 권한이 없습니다.

인스타그램에서 댓글을 어떻게 가져올 수 있나요?

Comments 엔드포인트(/{media-id}/comments)를 사용하여 특정 미디어의 댓글을 가져올 수 있습니다. 웹훅은 실시간 알림을 제공합니다.

인스타그램 속도 제한은 무엇인가요?

인스타그램 그래프 API는 앱당 시간당 200회 호출을 허용합니다. 일부 엔드포인트에는 추가적인 사용자별 제한이 있습니다.

API를 통해 스토리를 게시할 수 있나요?

네, 스토리는 피드 게시물과 동일한 콘텐츠 게시 흐름을 사용하여 게시할 수 있습니다.

인스타그램 인사이트에 어떻게 접근할 수 있나요?

OAuth 중에 instagram_manage_insights 권한을 요청하세요. Insights 엔드포인트를 사용하여 미디어 및 계정에 대한 메트릭을 가져올 수 있습니다.

댓글에 자동으로 답글을 달 수 있나요?

네, Comments API를 사용하여 답글을 게시할 수 있습니다. 많은 브랜드가 자동화된 고객 서비스 응답에 이를 사용합니다.

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

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