Skip to main content
Technical Systems

Instagram Insights API: Complete Developer Guide for 2026

The metrics are available. The meaning still needs engineering.

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.

Instagram Insights API: Complete Developer Guide for 2026

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.

Dashboard-style illustration of Instagram Insights API metrics, charts, and developer integration signals.

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:

For production pipelines, those error responses should also be logged as structured events; JSON Logging Best Practices shows an Instagram Insights API example.

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

Instagram Graph API vs Instagram Basic Display API

Developers often confuse the Instagram Insights API with the Instagram Basic Display API.

Although both provide access to Instagram data, they serve very different purposes.

FeatureInstagram Graph APIInstagram Basic Display API
Business & Creator Accounts
Personal Accounts
Insights & Analytics
Media Publishing✅ (supported endpoints)
Audience Metrics

The Instagram Insights API is part of the Instagram Graph API and is intended for Business and Creator accounts.

If you need analytics, engagement metrics, or audience insights, the Graph API is the correct choice. The same interface boundary shows up in dependency-aware SLAs, and Instagram Graph API documentation gives that boundary a public reference point.


Understanding Access Tokens

Authentication is one of the most common stumbling blocks when working with the Instagram Graph API.

Meta provides two primary types of access tokens.

Short-Lived Tokens

Short-lived user access tokens are primarily intended for development and testing.

They typically expire after a relatively short period and should not be relied upon for long-running applications.

Long-Lived Tokens

Production applications should exchange short-lived tokens for long-lived access tokens.

Long-lived tokens remain valid for significantly longer but still require periodic renewal.

Applications should monitor token expiry and refresh tokens before they become invalid to avoid interrupted access to analytics data. That is the gap JSON Schema and TypeScript types is concerned with: the code can look correct while Instagram Insights reference still defines the contract that matters at runtime.


API Versioning

The Instagram Insights API is versioned as part of the Meta Graph API.

For example:

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

Each Graph API version has a defined lifecycle.

As new versions are released, older versions are eventually deprecated.

Production applications should periodically review Meta’s changelog and update API versions before support for older releases ends.

Hardcoding outdated API versions can eventually lead to failed requests or unavailable endpoints.

When API versions or response shapes change, tests need to catch compatibility drift early; Contract Testing vs Integration Testing covers that boundary in more detail.


Working with Pagination

Many API endpoints return paginated results rather than every available record.

A typical response includes paging cursors similar to:

{
  "paging": {
    "cursors": {
      "before": "...",
      "after": "..."
    }
  }
}

Applications should continue requesting pages until all required data has been retrieved. This is where mock servers becomes useful, because Meta permissions reference makes the expected behavior explicit enough to test.

Proper pagination is especially important when analysing large accounts with hundreds or thousands of posts.


Batch Requests

Fetching insights for large numbers of posts individually can quickly consume API rate limits.

The Graph API supports batch requests that allow multiple operations to be submitted within a single HTTP request.

Batching can significantly reduce network overhead and improve performance when collecting analytics across many pieces of content.


Common API Problems

When the Insights API doesn’t behave as expected, the cause is often configuration rather than code.

Common issues include:

  • The Instagram account is not a Business or Creator account.
  • The account is not linked to a Facebook Page.
  • Required permissions have not been granted.
  • The access token has expired.
  • The requested metric is unavailable for the selected media type.
  • Demographic insights are unavailable because the account has fewer than 100 followers.

Verifying these prerequisites resolves many API issues before deeper debugging is required.


Development Mode vs Live Apps

Apps created in the Meta Developer Dashboard begin in development mode. A similar dependency appears in contract testing versus integration testing; Meta Graph API rate limiting only helps if the system preserves the contract through the full request path.

While in development mode, API access is generally limited to users who have been added as administrators, developers, or testers.

To support external users, an application typically needs to:

  • Complete App Review for the required permissions.
  • Switch the application to Live mode.
  • Meet Meta’s platform policies.

Understanding this distinction helps explain why an application may work during development but fail for other users.


Webhooks and Insights Data

Webhooks are useful for receiving notifications when certain events occur, such as comments or mentions.

However, analytics data itself is generally retrieved by polling the Insights API rather than pushed automatically through webhooks.

Many applications combine both approaches:

Webhook Event

Content Updated

Request Latest Insights

Update Dashboard

This provides near real-time reporting while keeping analytics data current.


Storing Historical Analytics

Some Instagram metrics are only available for limited periods.

If your application needs long-term reporting, historical trend analysis, or year-over-year comparisons, it is best to store retrieved insights in your own database. Seen from production, distributed tracing belongs in the same conversation as Meta Webhooks documentation, because both describe promises other systems rely on.

Many analytics platforms schedule regular data collection so that historical metrics remain available even after they are no longer accessible through the API.


Estimated Metrics

Not every metric returned by the Insights API represents an exact count.

Some audience and reach metrics are estimated using Meta’s internal measurement systems.

As a result, values may occasionally differ slightly from those displayed within the Instagram application or change as Meta refines its calculations.

For most reporting applications, these small differences are expected and should not be treated as errors.


Learn More

For the latest API capabilities and implementation guidance, refer to the official Meta documentation:

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.