How to Send Instagram DMs via API

Instagram direct message API guide — the 24-hour window, private replies, ice breakers, attachments, reactions and inbound webhooks, with working requests.

Instagram DMs, Facebook Messenger, Telegram and Bluesky each have their own message model, window rules, attachment limits and webhook payloads. Postproxy exposes one chat-and-message API across all four: the same endpoints, the same inbound event stream, and the OAuth tokens, Meta app review and webhook subscriptions handled on our side rather than yours.

That means the Instagram-specific rules below still govern what Instagram will accept — but the code you write against them is the same code that talks to Facebook Messenger. If you’re wiring several inboxes into one interface, building a unified social inbox covers the whole shape.

What can the Instagram direct message API do?

CapabilitySupported
Send text messagesYes — within the 24-hour window
Send mediaYes — one attachment per send, not combined with text
ReactionsYes — Instagram accepts love only
Private replies to commentsYes — bypasses the 24-hour window, once per comment
Story mentions and repliesYes — arrive inbound as story_mention / story_reply
Ice breakersYes — up to four suggested questions on a new thread
ig.me link referralsYes — opens the window before the user writes
Cold outreach to any userNo — not permitted by Meta’s API
Messaging outside 24 hoursHuman replies only — up to 7 days with tag: "HUMAN_AGENT"
Automated or promotional sends outside 24 hoursNo — a policy violation, not a technical limit

Account requirement: an Instagram professional account (Business or Creator). Personal accounts have no messaging API.

What are the Instagram messaging window rules?

This is the constraint most integrations get wrong, so it’s worth stating precisely:

  • 24-hour window. A free-form message is only allowed within 24 hours of the participant’s most recent message. Outside it, an ordinary send fails.
  • The HUMAN_AGENT tag extends that to 7 days, for a human replying to the participant’s own inquiry. It is not an escape hatch for automation.
  • Private replies are the documented way around it: DM someone who commented on your post, up to 7 days after the comment, once per comment.
  • ig.me referrals also open the window. When someone enters via https://ig.me/m/<username>?ref=..., a referral.received webhook fires and you may reply before they say anything. Meta only delivers these for profiles with at least one ice breaker configured.

Everything else — broadcast campaigns, cold DMs to a scraped list, re-engagement after the window — is outside what the API permits. Tools offering it are automating a logged-in session, not calling an API.

How Instagram DMs map to the API

Postproxy models Instagram messaging with two resources:

  • A Chat is a conversation between your Instagram professional account and one participant.
  • A Message belongs to a chat and is either inbound (from the user) or outbound (from you).

Sends are asynchronous: the API accepts the message with status: "pending", delivers it to Instagram, then flips it to "published" and fills in external_id.

Prerequisites

  • A Postproxy API key
  • A connected Instagram professional (Business or Creator) profile — Postproxy holds the OAuth token and the Meta webhook subscription, so there is no Meta app review on your side

Reply to an incoming DM

List chats for the profile (newest activity first):

Terminal window
curl "https://api.postproxy.dev/api/profiles/PROFILE_ID/chats" \
-H "Authorization: Bearer YOUR_API_KEY"

Then send into a chat:

Terminal window
curl -X POST "https://api.postproxy.dev/api/chats/CHAT_ID/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Yes, we ship worldwide!" }'

The response is 202 Accepted with the pending message. CHAT_ID accepts either the Postproxy hashid or Instagram’s native conversation ID.

The 24-hour messaging window

Meta only allows free-form messages within 24 hours of the participant’s last inbound message. Inside the window you can send as many messages as you like; outside it, an untagged send fails. Check the chat’s last_inbound_at before sending if your automation can lag.

Two ways past the window, both narrow:

A private reply to a comment opens a DM with a commenter up to 7 days after their comment — see comment-to-DM private replies.

The HUMAN_AGENT tag lets a human reply up to 7 days after the participant’s last inbound message:

Terminal window
curl -X POST "https://api.postproxy.dev/api/chats/CHAT_ID/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Following up on your question about sizing.", "tag": "HUMAN_AGENT" }'

Meta approved this tag for Postproxy on Instagram and Facebook, and it means what it says: a human, answering the person’s own inquiry. Promotional content, offers or automated re-engagement sent under it are policy violations, and the penalty is suspension of that account’s messaging capability. Full rules: the HUMAN_AGENT tag.

Receive DMs with webhooks

Inbound Instagram messages arrive on your webhook endpoint as message.received events — no polling needed. Subscribe via the Webhooks API and you get the full message object, including any attachments already mirrored to durable storage.

Useful related events: message.sent, message.delivered, message.read (Instagram exposes delivery and read receipts), and message.deleted when a user unsends.

Two Instagram-specific inbound shapes worth handling:

  • Story replies — the message carries a story_reply object pointing at your story.
  • Story mentions — arrive as a story_mention attachment; Postproxy mirrors the asset to permanent storage before the story expires.

Send an image or video

media accepts a public URL, multipart upload, or base64 — one attachment per send (a Meta Send API limit), and a send is either text or media, never both:

Terminal window
curl -X POST "https://api.postproxy.dev/api/chats/CHAT_ID/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "media": ["https://yourcdn.com/size-chart.jpg"] }'

React to a message

Reactions from your business account land in the user’s thread:

Terminal window
curl -X POST "https://api.postproxy.dev/api/messages/MESSAGE_ID/react" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reaction": "love" }'

Instagram’s outbound reaction API accepts only love. DELETE /api/messages/MESSAGE_ID/unreact removes it.

Can you DM any Instagram user via the API?

No. Instagram messaging is reply-based: a chat exists because the user messaged your account (or commented, via private replies). You can pre-create a chat with POST /api/profiles/PROFILE_ID/chats and the user’s Instagram-scoped ID, but the 24-hour window still governs whether a send succeeds. Cold outreach to arbitrary usernames is not something Meta’s API permits — any tool claiming otherwise is automating a logged-in session against Instagram’s terms.

Who is messaging you

Each chat’s metadata carries participant signals fetched from Meta: is_verified_user, follower_count, is_user_follow_business, is_business_follow_user. Useful for routing — e.g. escalate verified accounts or large followings to a human.

To route replies by a tap instead of parsing free text, send quick replies and buttons — Instagram delivers chips on plain-text messages only. Full field reference, statuses, and error shapes: Direct Messages API. For Messenger, the same chat/message model applies — see How to Send Facebook Messenger Messages via API. The DM launch post covers what shipped across platforms: chats, private replies, and ig.me referrals.

Ready to get started?

Start with our free plan and scale as your needs grow. No credit card required.