Tutorials

Instagram Insights API: Complete Developer Guide for 2026

Learn how to use the Instagram Insights API to access analytics data, track engagement metrics, and build reporting tools. Includes code examples and best practices.

The Instagram Insights API provides programmatic access to valuable analytics data for business and creator accounts. Whether you’re building a social media dashboard, automating reports, or analyzing content performance, this guide covers everything you need to integrate Instagram analytics into your applications.

What is the Instagram Insights API?

The Instagram Insights API is part of Meta’s Instagram Graph API. It enables developers to retrieve performance metrics for Instagram Business and Creator accounts, including data on posts, stories, reels, and audience demographics.

Unlike manual analytics checking through the Instagram app, the API allows you to:

  • Automate data collection across multiple accounts
  • Build custom dashboards and reporting tools
  • Integrate Instagram metrics with other data sources
  • Track historical performance trends programmatically

Prerequisites for Using Instagram Insights API

Before accessing insights data, you need:

Account Requirements

  1. Instagram Business or Creator Account: Personal accounts cannot access the Insights API
  2. Facebook Page Connection: Your Instagram account must be linked to a Facebook Page
  3. Meta Developer Account: Register at developers.facebook.com

Technical Requirements

  • Facebook App: Created in the Meta Developer Dashboard
  • Access Tokens: User or Page access tokens with appropriate permissions
  • Permissions Granted: instagram_basic, instagram_manage_insights, and pages_show_list

Setting Up API Access

Step 1: Create a Meta App

  1. Navigate to Meta for Developers
  2. Create a new app and select “Business” type
  3. Add the “Instagram Graph API” product to your app

Step 2: Configure Permissions

Request these permissions for insights access:

instagram_basic
instagram_manage_insights
pages_show_list
pages_read_engagement

Step 3: Generate Access Tokens

For development, use the Graph API Explorer to generate tokens. For production, implement the OAuth flow:

const authUrl = `https://www.facebook.com/v18.0/dialog/oauth?
  client_id=${APP_ID}
  &redirect_uri=${REDIRECT_URI}
  &scope=instagram_basic,instagram_manage_insights,pages_show_list`;

Step 4: Get Instagram Business Account ID

Retrieve your Instagram Business Account ID through the linked Facebook Page:

// First, get the Facebook Page ID
GET /me/accounts?access_token={access-token}

// Then, get the Instagram Business Account
GET /{page-id}?fields=instagram_business_account&access_token={access-token}

Available Metrics and Endpoints

The Instagram Insights API provides metrics at three levels: account, media, and story/reel.

Account-Level Insights

Retrieve overall account performance:

GET /{instagram-account-id}/insights

Available Metrics:

MetricDescriptionPeriod
impressionsTotal times content was displayedday, week, days_28
reachUnique accounts that saw contentday, week, days_28
follower_countTotal followersday
email_contactsEmail button tapsday
phone_call_clicksCall button tapsday
text_message_clicksText button tapsday
get_directions_clicksDirections button tapsday
website_clicksWebsite link tapsday
profile_viewsProfile visitsday

Example Request:

const response = await fetch(
  `https://graph.facebook.com/v18.0/${igAccountId}/insights?` +
  `metric=impressions,reach,profile_views&period=day&` +
  `access_token=${accessToken}`
);

const data = await response.json();

Media-Level Insights

Get performance data for individual posts:

GET /{media-id}/insights

Photo and Video Metrics:

MetricDescription
engagementLikes + comments + saves
impressionsTimes the media was seen
reachUnique accounts reached
savedTimes saved to collections
video_viewsVideo views (videos only)

Carousel Metrics:

MetricDescription
carousel_album_engagementTotal engagement on carousel
carousel_album_impressionsCarousel impressions
carousel_album_reachUnique accounts reached
carousel_album_savedTimes carousel was saved

Example Request:

async function getMediaInsights(mediaId, accessToken) {
  const metrics = 'engagement,impressions,reach,saved';

  const response = await fetch(
    `https://graph.facebook.com/v18.0/${mediaId}/insights?` +
    `metric=${metrics}&access_token=${accessToken}`
  );

  return response.json();
}

Story Insights

Stories have unique metrics available for 24 hours after posting:

MetricDescription
exitsTimes users left the story
impressionsTotal story views
reachUnique viewers
repliesDirect message replies
taps_forwardTaps to next story
taps_backTaps to previous story

Reels Insights

Reels provide video-specific engagement data:

MetricDescription
commentsComment count
likesLike count
playsTotal plays
reachUnique accounts reached
savedSave count
sharesShare count

Audience Demographics

Access follower demographic breakdowns:

GET /{instagram-account-id}/insights?metric=audience_city,audience_country,audience_gender_age,audience_locale&period=lifetime

Demographic Metrics:

  • audience_city: Top cities of followers
  • audience_country: Follower countries
  • audience_gender_age: Gender and age distribution
  • audience_locale: Language preferences

Important: Demographic data requires a minimum of 100 followers.

async function getAudienceDemographics(igAccountId, accessToken) {
  const metrics = [
    'audience_city',
    'audience_country',
    'audience_gender_age',
    'audience_locale'
  ].join(',');

  const response = await fetch(
    `https://graph.facebook.com/v18.0/${igAccountId}/insights?` +
    `metric=${metrics}&period=lifetime&access_token=${accessToken}`
  );

  const data = await response.json();
  return data.data;
}

Building a Complete Analytics Dashboard

Here’s a practical implementation for fetching comprehensive insights:

class InstagramInsights {
  constructor(accessToken, igAccountId) {
    this.accessToken = accessToken;
    this.igAccountId = igAccountId;
    this.baseUrl = 'https://graph.facebook.com/v18.0';
  }

  async fetchApi(endpoint, params = {}) {
    const url = new URL(`${this.baseUrl}${endpoint}`);
    url.searchParams.append('access_token', this.accessToken);

    Object.entries(params).forEach(([key, value]) => {
      url.searchParams.append(key, value);
    });

    const response = await fetch(url);

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error?.message || 'API request failed');
    }

    return response.json();
  }

  async getAccountInsights(period = 'day') {
    const metrics = [
      'impressions',
      'reach',
      'profile_views',
      'website_clicks',
      'follower_count'
    ].join(',');

    return this.fetchApi(`/${this.igAccountId}/insights`, {
      metric: metrics,
      period: period
    });
  }

  async getRecentMedia(limit = 25) {
    return this.fetchApi(`/${this.igAccountId}/media`, {
      fields: 'id,caption,media_type,timestamp,permalink,like_count,comments_count',
      limit: limit
    });
  }

  async getMediaInsights(mediaId) {
    const metrics = 'engagement,impressions,reach,saved';

    return this.fetchApi(`/${mediaId}/insights`, {
      metric: metrics
    });
  }

  async getAllMediaWithInsights(limit = 25) {
    const mediaResponse = await this.getRecentMedia(limit);
    const media = mediaResponse.data;

    const mediaWithInsights = await Promise.all(
      media.map(async (item) => {
        try {
          const insights = await this.getMediaInsights(item.id);
          return { ...item, insights: insights.data };
        } catch (error) {
          return { ...item, insights: null, error: error.message };
        }
      })
    );

    return mediaWithInsights;
  }
}

// Usage
const insights = new InstagramInsights(ACCESS_TOKEN, IG_ACCOUNT_ID);

const accountData = await insights.getAccountInsights('days_28');
const mediaPerformance = await insights.getAllMediaWithInsights(10);

Rate Limits and Best Practices

API Rate Limits

The Instagram Graph API enforces rate limits:

  • Standard limit: 200 calls per user per hour
  • Business Use Case: Higher limits available with approved use cases

Handling Rate Limits

async function fetchWithRateLimit(fetchFn, retries = 3) {
  for (let attempt = 0; attempt < retries; attempt++) {
    try {
      return await fetchFn();
    } catch (error) {
      if (error.message.includes('rate limit') && attempt < retries - 1) {
        const waitTime = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, waitTime));
        continue;
      }
      throw error;
    }
  }
}

Caching Strategies

Reduce API calls with intelligent caching:

class CachedInsights {
  constructor(insights, cacheDuration = 3600000) {
    this.insights = insights;
    this.cache = new Map();
    this.cacheDuration = cacheDuration;
  }

  async getCached(key, fetchFn) {
    const cached = this.cache.get(key);

    if (cached && Date.now() - cached.timestamp < this.cacheDuration) {
      return cached.data;
    }

    const data = await fetchFn();
    this.cache.set(key, { data, timestamp: Date.now() });
    return data;
  }

  async getAccountInsights(period) {
    return this.getCached(
      `account_${period}`,
      () => this.insights.getAccountInsights(period)
    );
  }
}

Common Use Cases

Automated Weekly Reports

async function generateWeeklyReport(insights) {
  const accountMetrics = await insights.getAccountInsights('week');
  const topMedia = await insights.getAllMediaWithInsights(10);

  const sortedByEngagement = topMedia
    .filter(m => m.insights)
    .sort((a, b) => {
      const engA = a.insights.find(i => i.name === 'engagement')?.values[0]?.value || 0;
      const engB = b.insights.find(i => i.name === 'engagement')?.values[0]?.value || 0;
      return engB - engA;
    });

  return {
    period: 'week',
    generatedAt: new Date().toISOString(),
    accountMetrics: accountMetrics.data,
    topPerformingPosts: sortedByEngagement.slice(0, 5)
  };
}

Engagement Rate Calculation

function calculateEngagementRate(mediaInsights, followerCount) {
  const engagement = mediaInsights.find(m => m.name === 'engagement')?.values[0]?.value || 0;
  return ((engagement / followerCount) * 100).toFixed(2);
}

Best Posting Time Analysis

function analyzeBestPostingTimes(mediaWithInsights) {
  const hourlyEngagement = {};

  mediaWithInsights.forEach(media => {
    if (!media.insights) return;

    const hour = new Date(media.timestamp).getHours();
    const engagement = media.insights.find(i => i.name === 'engagement')?.values[0]?.value || 0;

    if (!hourlyEngagement[hour]) {
      hourlyEngagement[hour] = { total: 0, count: 0 };
    }

    hourlyEngagement[hour].total += engagement;
    hourlyEngagement[hour].count += 1;
  });

  return Object.entries(hourlyEngagement)
    .map(([hour, data]) => ({
      hour: parseInt(hour),
      avgEngagement: data.total / data.count
    }))
    .sort((a, b) => b.avgEngagement - a.avgEngagement);
}

Error Handling

Handle common API errors gracefully:

function handleInsightsError(error) {
  const errorCode = error.code;

  const errorMessages = {
    100: 'Invalid parameter - check your metric names',
    190: 'Access token expired - refresh authentication',
    200: 'Permission denied - verify app permissions',
    4: 'Rate limit exceeded - implement backoff strategy',
    10: 'Permission error - user may have revoked access'
  };

  return errorMessages[errorCode] || `Unknown error: ${error.message}`;
}

Webhook Integration

Set up webhooks to receive real-time updates:

// Webhook endpoint
app.post('/instagram/webhook', (req, res) => {
  const { object, entry } = req.body;

  if (object === 'instagram') {
    entry.forEach(event => {
      const { changes } = event;

      changes.forEach(change => {
        if (change.field === 'mentions') {
          handleMention(change.value);
        } else if (change.field === 'comments') {
          handleComment(change.value);
        }
      });
    });
  }

  res.sendStatus(200);
});

Security Considerations

Token Storage

Never expose access tokens in client-side code:

// Bad - token exposed
const response = await fetch(`https://graph.facebook.com/...?access_token=${token}`);

// Good - proxy through your server
const response = await fetch('/api/instagram/insights');

Token Refresh

Implement automatic token refresh before expiration:

async function refreshLongLivedToken(currentToken) {
  const response = await fetch(
    `https://graph.facebook.com/v18.0/oauth/access_token?` +
    `grant_type=fb_exchange_token&` +
    `client_id=${APP_ID}&` +
    `client_secret=${APP_SECRET}&` +
    `fb_exchange_token=${currentToken}`
  );

  return response.json();
}

Limitations and Considerations

Data Availability

  • Stories: Insights available for 24 hours only
  • Historical data: Limited to 30 days for most metrics
  • Demographic data: Requires 100+ followers
  • Promoted content: Some metrics differ for ads

API Constraints

  • No access to competitor accounts
  • Personal accounts excluded
  • Some metrics are estimates
  • Data may be delayed up to 48 hours

Conclusion

The Instagram Insights API provides powerful analytics capabilities for businesses and developers building social media tools. With proper implementation of authentication, rate limiting, and caching, you can create robust analytics applications that deliver actionable insights.

Start with basic account metrics, expand to media-level analysis, and consider webhook integration for real-time monitoring. Always handle errors gracefully and respect API rate limits to maintain reliable access to Instagram analytics data.