DEV Community

Cover image for How to Use Instagram Graph API in 2026
Wanda
Wanda

Posted on • Originally published at apidog.com

How to Use Instagram Graph API in 2026

TL;DR

The Instagram Graph API allows developers to programmatically manage Instagram Business and Creator accounts. It uses Facebook Login with OAuth 2.0 for authentication and provides endpoints for content publishing, analytics, comments, and messaging. Rate limits are 200 calls per hour per app. This guide walks through authentication, publishing, insights, comment management, webhook setup, and production integration.

Try Apidog today


Introduction

Instagram has over 2 billion monthly active users and 200M+ businesses using Instagram Business accounts. If you’re building social media management, analytics, or e-commerce tools, Instagram Graph API integration is essential for automating workflows at scale.

Manual posting, comment moderation, and reporting for multiple accounts is a huge time sink. Instagram Graph API lets you automate publishing, comment handling, sentiment analysis, and analytics so you can save 20–30 hours per week per manager.

This guide shows step-by-step how to integrate with the Instagram Graph API: setup, OAuth, publishing, insights, comment management, webhooks, and deployment, so you can build production-grade automation.

💡 Apidog simplifies API integration testing. Test Instagram endpoints, validate OAuth flows, inspect API responses, and debug publishing issues in a single workspace. Import API specs, mock responses, and share test scenarios with your team.

What Is the Instagram Graph API?

The Instagram Graph API provides programmatic access to Instagram Business and Creator accounts via the Facebook Graph API. Key features include:

  • Content publishing: photos, videos, reels, carousels
  • Media insights & analytics
  • Comment and mention management
  • Direct messaging (with Messenger Platform)
  • Hashtag/mention tracking
  • Story management
  • Shopping/product tags

Key Features

Feature Description
Graph-based API Node-based resource access
OAuth 2.0 Facebook Login authentication
Webhooks Real-time notifications (comments, mentions)
Rate Limiting 200 calls/hour/app
Content Publishing Photos, videos, reels, carousels
Insights Engagement, reach, impressions
Moderation Comments, mentions, message management

Account Requirements

Account Type API Access
Business Account Full API access
Creator Account Full API access
Personal Account No API access (must convert)
Private Account Limited insights

API Architecture Overview

All endpoints follow the Facebook Graph API structure:

https://graph.facebook.com/v18.0/
Enter fullscreen mode Exit fullscreen mode

API Versions Compared

Version Status End Date Use Case
v18.0 Current March 2026 All new integrations
v17.0 Deprecated Jan 2026 Existing integrations
v16.0 Retired Expired Do not use

Tip: Always target the latest stable version.


Getting Started: Authentication Setup

Step 1: Create Facebook Developer Account

  1. Go to the Facebook Developers Portal
  2. Sign in with your Facebook account
  3. Create a new Facebook App (type: Business)
  4. Add the Instagram Graph API product

Step 2: Link Instagram Business Account

  1. In Facebook Page Settings, go to Instagram
  2. Click Connect Account
  3. Log in to your Instagram and authorize
  4. Confirm your Instagram Business account is linked

Personal accounts can’t use the Graph API—convert to Business or Creator in Instagram Settings.

Step 3: Get Access Tokens

Build the Facebook OAuth authorization 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()}`;
};
Enter fullscreen mode Exit fullscreen mode

Required Permissions

Permission Description
instagram_basic Basic profile info, media list
instagram_content_publish Publish photos, videos, carousels
instagram_manage_comments Read/write comments
instagram_manage_insights Access analytics data
pages_read_engagement Page access for publishing
pages_manage_posts Publish to connected Page

Step 4: Exchange Token for Long-Lived Token

Short-lived tokens expire in 1 hour; exchange for a 60-day token:

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

Step 5: Get Instagram Business Account ID

Retrieve the Instagram account ID linked to your page:

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

Step 6: Make Authenticated API Calls

Create a reusable API client:

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

Content Publishing

Publishing a Photo

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

Publishing a Video

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

Publishing a Carousel (Multiple Images/Videos)

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

Media Types

Media Type Parameters Use Case
IMAGE image_url, caption Photo posts
VIDEO video_url, cover_url, caption Video posts
REELS video_url, cover_url, caption, share_to_feed Reels
CAROUSEL children (array), caption Multiple media

Retrieving Media and Insights

Getting User Media

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

Getting Media Insights

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

Available Insights Metrics

Metric Description Media Types
impressions Total views All
reach Unique accounts reached All
engagement Likes + comments + saves All
saved Times saved All
video_views Video views (3+ sec) Video, Reels
plays Total video plays Video, Reels
profile_visits Profile visits from post All
follows Follows from post All
comments Comment count All
like_count Like count All

Getting Account Insights

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

Account-Level Metrics

Metric Description
impressions Total profile/content views
reach Unique accounts reached
profile_views Profile visits
website_clicks Link in bio clicks
email_contacts Email button taps
phone_call_clicks Phone button taps
text_message_clicks SMS button taps
get_directions_clicks Address clicks
follower_count Total followers
audience_city Follower cities
audience_country Follower countries
audience_gender_age Demographic breakdown

Comment Management

Getting Comments

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

Replying to Comments

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

Hiding Comments

const hideComment = async (commentId) => {
  const response = await instagramRequest(`/${commentId}`, {
    method: 'POST',
    hide: 'true'
  });

  return response;
};

// Usage
await hideComment('17900000000000000');
console.log('Comment hidden');
Enter fullscreen mode Exit fullscreen mode

Deleting Comments

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

  console.log('Comment deleted');
};
Enter fullscreen mode Exit fullscreen mode

Webhooks

Configuring Webhooks

Subscribe to Instagram webhook events:

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

Handling Webhooks

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

Webhook Fields

Field Trigger
comments New comment or reply
mentions User mentions account
message_reactions Reaction to story
story_status Story reply/view

Rate Limiting

Understanding Rate Limits

The Instagram Graph API enforces:

  • 200 calls/hour/app (shared by all users)
  • Business Discovery: 200 calls/hour/user
  • Content Publishing: Limited by action

If you exceed the limit, you’ll get HTTP 400 with error subcode 613.

Rate Limit Best Practices

  1. Cache responses — Avoid redundant fetches
  2. Batch requests — Use field expansion to reduce calls
  3. Use webhooks — Real-time updates, avoid polling
  4. Implement backoff — Exponential backoff on 429 errors
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;
      }
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Common Issues

Issue: OAuth Token Expired

Symptoms: “Invalid OAuth access token” errors.

Solutions:

  1. Refresh token before expiry (60 days)
  2. Store expiry date and alert users
  3. Re-authenticate user if token is expired

Issue: Media Publish Fails

Symptoms: Publishing returns error.

Solutions:

  1. Image URL must be publicly accessible
  2. Image format: JPEG, PNG, <8MB
  3. Video: MP4, <1GB, <90s
  4. Wait for video processing before publishing

Issue: Insights Not Available

Symptoms: Insights API returns empty data.

Solutions:

  1. Only Business/Creator accounts are supported
  2. Wait 24–48h for insights population
  3. Ensure account has sufficient activity

Production Deployment Checklist

Before going live, make sure to:

  • [ ] Convert all test accounts to Business/Creator
  • [ ] Use OAuth 2.0 with long-lived tokens
  • [ ] Store tokens securely (encrypted)
  • [ ] Implement automatic token refresh
  • [ ] Set up webhook endpoints with HTTPS
  • [ ] Add rate limiting and request queuing
  • [ ] Implement robust error handling
  • [ ] Add logging for all API calls
  • [ ] Create content moderation workflows
  • [ ] Test with multiple account types

Real-World Use Cases

Social Media Scheduling Tool

  • Challenge: Manual posting across 50+ client accounts
  • Solution: Scheduled publishing via Instagram API
  • Result: 80% time savings, consistent posting

Key implementation:

  • Content calendar with drag-and-drop scheduling
  • Auto-publish photos, videos, carousels
  • Hashtag suggestions based on content

Customer Service Automation

  • Challenge: Slow response to customer inquiries
  • Solution: Auto-reply to common questions via webhook
  • Result: 5-min average response, 90% satisfaction

Key implementation:

  • Keyword detection (price, availability, shipping)
  • Auto-reply with product links
  • Escalate complex queries to human agents

Conclusion

The Instagram Graph API enables automation for Instagram Business and Creator accounts. Key actions:

  • Authenticate with Facebook Login OAuth 2.0 (60-day tokens)
  • Publish photos, videos, reels, and carousels
  • Access insights: engagement, reach, demographics
  • Use webhooks for real-time monitoring
  • Design around 200 calls/hour/app rate limit

Apidog streamlines API testing and team collaboration for all these workflows.


FAQ Section

How do I get access to Instagram API?

Create a Facebook Developer account, create a Business app, add Instagram Graph API product, and authenticate via Facebook Login with required permissions.

Can I post to Instagram automatically?

Yes, use the Content Publishing API to publish photos, videos, reels, and carousels to Business and Creator accounts.

What types of Instagram accounts support the API?

Only Business and Creator accounts have full API access. Personal accounts have limited or no API access.

How do I get comments from Instagram?

Use the Comments endpoint (/{media-id}/comments) to fetch comments on specific media. Webhooks provide real-time notifications.

What are Instagram rate limits?

The Instagram Graph API allows 200 calls per hour per app. Some endpoints have additional per-user limits.

Can I publish Stories via the API?

Yes, Stories can be published using the same content publishing flow as feed posts.

How do I access Instagram Insights?

Request the instagram_manage_insights permission during OAuth. Use the Insights endpoint to fetch metrics for media and account.

Can I reply to comments automatically?

Yes, use the Comments API to post replies. Many brands use this for automated customer service responses.

Top comments (0)