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
- Instagram Business or Creator Account: Personal accounts cannot access the Insights API
- Facebook Page Connection: Your Instagram account must be linked to a Facebook Page
- 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, andpages_show_list
Setting Up API Access
Step 1: Create a Meta App
- Navigate to Meta for Developers
- Create a new app and select “Business” type
- 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:
| Metric | Description | Period |
|---|---|---|
impressions | Total times content was displayed | day, week, days_28 |
reach | Unique accounts that saw content | day, week, days_28 |
follower_count | Total followers | day |
email_contacts | Email button taps | day |
phone_call_clicks | Call button taps | day |
text_message_clicks | Text button taps | day |
get_directions_clicks | Directions button taps | day |
website_clicks | Website link taps | day |
profile_views | Profile visits | day |
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:
| Metric | Description |
|---|---|
engagement | Likes + comments + saves |
impressions | Times the media was seen |
reach | Unique accounts reached |
saved | Times saved to collections |
video_views | Video views (videos only) |
Carousel Metrics:
| Metric | Description |
|---|---|
carousel_album_engagement | Total engagement on carousel |
carousel_album_impressions | Carousel impressions |
carousel_album_reach | Unique accounts reached |
carousel_album_saved | Times 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:
| Metric | Description |
|---|---|
exits | Times users left the story |
impressions | Total story views |
reach | Unique viewers |
replies | Direct message replies |
taps_forward | Taps to next story |
taps_back | Taps to previous story |
Reels Insights
Reels provide video-specific engagement data:
| Metric | Description |
|---|---|
comments | Comment count |
likes | Like count |
plays | Total plays |
reach | Unique accounts reached |
saved | Save count |
shares | Share 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 followersaudience_country: Follower countriesaudience_gender_age: Gender and age distributionaudience_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.
| Feature | Instagram Graph API | Instagram 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:
- Meta Graph API Documentation
- Instagram Platform Documentation
- Graph API Explorer
- Meta Graph API Changelog
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.





