The Instagram Messaging API lets businesses send and receive direct messages programmatically through Instagram's official Graph API. If you're building customer support automation, notification systems, or conversational commerce flows, this guide covers what you need to integrate the Instagram DM API into your app.
Instagram's messaging capabilities are part of the Meta Graph API ecosystem. Unlike unofficial methods that risk account suspension, the official Instagram Direct Message API gives you reliable, scalable messaging for business accounts with proper authentication and rate limiting.
Prerequisites and Requirements
Business Account Requirements
The Instagram Messaging API only works with Instagram Business or Creator accounts connected to a Facebook Page. Personal accounts can't access the messaging endpoints.
| Requirement | Details |
|---|---|
| Account Type | Instagram Business or Creator account |
| Facebook Page | Must be linked to a Facebook Page |
| Meta App | Requires a registered Meta Developer App |
| Permissions | instagram_business_manage_messages scope |
| App Review | Required for production access beyond test users |
Creating a Meta Developer App
- Go to developers.facebook.com and create a new app
- Select "Business" as the app type
- Add the "Instagram Graph API" product to your app
- Configure your OAuth redirect URIs
- Note your App ID and App Secret
// Environment variables needed for Instagram API integration
interface InstagramConfig {
clientId: string; // Meta App ID
clientSecret: string; // Meta App Secret
redirectUri: string; // Your OAuth callback URL
webhookVerifyToken: string; // For webhook verification
}
const config: InstagramConfig = {
clientId: process.env.INSTAGRAM_CLIENT_ID || '',
clientSecret: process.env.INSTAGRAM_CLIENT_SECRET || '',
redirectUri: process.env.INSTAGRAM_REDIRECT_URI || '',
webhookVerifyToken: process.env.INSTAGRAM_WEBHOOK_VERIFY_TOKEN || '',
};
Note: Store all credentials in environment variables. Never commit API secrets to version control.
OAuth 2.0 Authentication Flow
The Instagram Messaging API uses OAuth 2.0. You'll need a two-step token exchange to get long-lived access tokens.
Step 1: Generate Authorization URL
Direct users to Instagram's authorization page with the required scopes:
function getInstagramAuthUrl(state: string): string {
const scopes = [
'instagram_business_basic',
'instagram_business_content_publish',
'instagram_business_manage_messages', // Required for DMs
];
const params = new URLSearchParams({
client_id: config.clientId,
redirect_uri: config.redirectUri,
scope: scopes.join(','),
response_type: 'code',
auth_type: 'rerequest', // Force re-permission prompt
state: state, // CSRF protection
});
return `https://www.instagram.com/oauth/authorize?${params.toString()}`;
}
Step 2: Exchange Code for Tokens
After the user authorizes your app, Instagram redirects to your callback URL with an authorization code. Exchange it for access tokens:
interface TokenResponse {
accessToken: string;
refreshToken: string;
userId: string;
expiresIn: number;
}
async function exchangeCodeForToken(
code: string
): Promise<TokenResponse> {
// Step 1: Exchange code for short-lived token
const tokenResponse = await fetch(
'https://api.instagram.com/oauth/access_token',
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: config.clientId,
client_secret: config.clientSecret,
grant_type: 'authorization_code',
redirect_uri: config.redirectUri,
code: code,
}),
}
);
if (!tokenResponse.ok) {
const error = await tokenResponse.text();
throw new Error(`Token exchange failed: ${error}`);
}
const shortLivedData = await tokenResponse.json();
const shortLivedToken = shortLivedData.access_token;
const userId = shortLivedData.user_id;
// Step 2: Exchange for long-lived token (60 days)
const longLivedResponse = await fetch(
`https://graph.instagram.com/access_token?` +
`grant_type=ig_exchange_token&` +
`client_secret=${config.clientSecret}&` +
`access_token=${shortLivedToken}`,
{ method: 'GET' }
);
if (!longLivedResponse.ok) {
const error = await longLivedResponse.text();
throw new Error(`Long-lived token exchange failed: ${error}`);
}
const longLivedData = await longLivedResponse.json();
return {
accessToken: longLivedData.access_token,
refreshToken: longLivedData.access_token, // Instagram uses same token
userId: userId,
expiresIn: longLivedData.expires_in, // ~60 days in seconds
};
}
Refreshing Access Tokens
Long-lived tokens expire after 60 days. Refresh them before they lapse:
async function refreshAccessToken(
currentToken: string
): Promise<{ accessToken: string; expiresIn: number }> {
const response = await fetch(
`https://graph.instagram.com/refresh_access_token?` +
`grant_type=ig_refresh_token&` +
`access_token=${currentToken}`,
{ method: 'GET' }
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Token refresh failed: ${response.status} ${errorText}`
);
}
const data = await response.json();
if (data.error) {
throw new Error(
`Token refresh error: ${data.error.message}`
);
}
if (!data.access_token) {
throw new Error('Token refresh did not return an access_token');
}
return {
accessToken: data.access_token,
expiresIn: data.expires_in,
};
}
Note: A good rule of thumb is to refresh tokens when they have less than 7 days remaining.
Setting Up Webhooks for Real-time Messages
To receive incoming messages in real-time, you need to configure webhooks. Instagram sends POST requests to your endpoint whenever a user messages your business account.
Webhook Verification Endpoint
Instagram verifies your webhook URL with a GET request before activating it:
import { Request, Response } from 'express';
function handleWebhookVerification(
req: Request,
res: Response
): void {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === config.webhookVerifyToken) {
console.log('Webhook verified successfully');
res.status(200).send(challenge);
return;
}
console.error('Webhook verification failed');
res.status(403).send('Verification failed');
}
Sending Messages via the Instagram DM API
Sending Text Messages
To send Instagram DMs programmatically, use the messages endpoint:
interface SendMessageOptions {
accessToken: string;
igUserId: string; // Your Instagram Business Account ID
recipientId: string; // The user's Instagram-scoped ID
text: string;
}
interface SendMessageResponse {
messageId: string;
}
async function sendTextMessage(
options: SendMessageOptions
): Promise<SendMessageResponse> {
const { accessToken, igUserId, recipientId, text } = options;
const response = await fetch(
`https://graph.instagram.com/${igUserId}/messages`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
body: JSON.stringify({
recipient: {
id: recipientId,
},
message: {
text: text,
},
}),
}
);
if (!response.ok) {
const error = await response.text();
throw new Error(`Failed to send message: ${error}`);
}
const data = await response.json();
return { messageId: data.message_id };
}
Rate Limits and Best Practices
Rate Limit Overview
| Limit Type | Threshold | Window |
|---|---|---|
| API Calls | 200 calls | Per hour per user |
| Messages Sent | Varies | Based on conversation history |
| Webhook Responses | Must respond within 20 seconds | Per event |
Best Practices
- Implement exponential backoff for rate limit errors
- Cache user tokens to avoid repeated auth calls
- Use webhook acknowledgment immediately, process asynchronously
- Monitor token expiration and refresh proactively
- Log all API interactions for debugging
Frequently Asked Questions
Is the Instagram Messaging API free?
Yes. You need a Meta Developer account, an approved Meta App, and must pass App Review to access messaging permissions in production. There are no per-message fees from Meta, but you're responsible for your own infrastructure costs.
Can I send automated DMs to anyone on Instagram?
No. The API only lets you message users who have initiated a conversation with your business account first. Once a user messages you, a 24-hour standard messaging window opens during which you can send replies. After the 24-hour window closes, you can still send Human Agent messages for up to 7 days, but only for customer support purposes (not promotional content). You cannot send unsolicited messages or cold outreach.
What's the difference between the Instagram Messaging API and Graph API?
The Instagram Graph API covers all Instagram functionality: posting content, reading insights, managing comments, and messaging. The Instagram Messaging API is the messaging subset, using the /messages endpoint. Both use the same OAuth 2.0 authentication and access tokens.
How long do Instagram API access tokens last?
Short-lived tokens expire after 1 hour. Long-lived tokens last 60 days. Exchange short-lived tokens for long-lived ones immediately after OAuth, then refresh before they expire. Best practice is to refresh when tokens have less than 7 days remaining.
Do I need App Review to use the Instagram Messaging API?
Yes, for production use. During development you can test with up to 25 test users without App Review. To message real users at scale, you'll need to submit for Meta App Review and get the instagram_business_manage_messages permission approved. This typically takes 2-5 business days.
Using Late for Instagram Messaging Integration
Building and maintaining Instagram Messaging API integrations takes real effort. You're on the hook for OAuth flows, token refresh, webhook management, rate limiting, and error handling, across potentially multiple social platforms.
Late provides a unified API that simplifies Instagram messaging integration alongside other social platforms. Instead of building separate integrations for each one, Late offers:
- Unified Authentication: Single OAuth flow for Instagram, Facebook, Twitter, LinkedIn, and more
- Automatic Token Management: Late handles token refresh and expiration automatically
- Webhook Abstraction: Normalized webhook events across all platforms
- Built-in Rate Limiting: Intelligent request queuing and retry logic
- Type-Safe SDK: Full TypeScript support with comprehensive types
With Late, the Instagram messaging implementation looks like this:
import { Late } from '@getlate/sdk';
const late = new Late({ apiKey: process.env.LATE_API_KEY });
// Send a message through Late's unified API
await late.messages.send({
accountId: 'instagram-account-id',
recipientId: 'user-id',
content: {
text: 'Hello from Late!',
},
});
// Handle incoming messages with normalized events
late.webhooks.on('message.received', (event) => {
console.log('Platform:', event.platform); // 'instagram'
console.log('Message:', event.content.text);
});
Late handles the complexity so you can focus on your app's core functionality. Check out the Late documentation to get started with Instagram messaging and other social platform integrations.

Miquel is the founder of Late, building the most reliable social media API for developers. Previously built multiple startups and scaled APIs to millions of requests.
View all articlesLearn more about Late with AI
See what AI assistants say about Late API and this topic