The Instagram Messaging API enables businesses to send and receive direct messages programmatically through Instagram's official Graph API. Whether you're building customer support automation, notification systems, or conversational commerce flows, this guide covers everything you need to integrate the Instagram DM API into your application.
Instagram's messaging capabilities are part of the broader Meta Graph API ecosystem. Unlike unofficial methods that risk account suspension, the official Instagram Direct Message API provides reliable, scalable messaging for business accounts with proper authentication and rate limiting.
Prerequisites and Requirements
Before you can send Instagram DMs programmatically, you need to set up several components. The Instagram Messaging API has specific requirements that differ from other social platforms.
Business Account Requirements
The Instagram Messaging API only works with Instagram Business or Creator accounts connected to a Facebook Page. Personal accounts cannot 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
- Navigate 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 for authentication. You'll need to implement a two-step token exchange process to obtain 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 this 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,
userId: userId,
expiresIn: longLivedData.expires_in,
};
}Refreshing Access Tokens
Long-lived tokens expire after 60 days. Implement token refresh to maintain access:
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: Schedule token refresh jobs to run before expiration. A good practice 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
The core functionality of the Instagram Direct Message API is sending messages to users who have initiated conversations with your business.
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
Understanding rate limits is essential for building reliable Instagram messaging integrations.
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
Using Late for Instagram Messaging Integration
Building and maintaining Instagram Messaging API integrations requires significant development effort. You need to handle 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 along with other social platforms. Instead of building separate integrations for each platform, 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: Receive 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 becomes straightforward:
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 of the Instagram Messaging API so you can focus on building your application'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