How Instagram comment-to-DM automation works under the hood (Meta Graph API, webhooks, private replies)
You have seen the pattern: a Reel says "comment GUIDE and I'll send it to you", and seconds after you comment, a DM arrives with the link. It isn't someone glued to their phone. It is a small event-driven system built on
You have seen the pattern: a Reel says "comment GUIDE and I'll send it to you", and seconds after you comment, a DM arrives with the link. It isn't someone glued to their phone. It is a small event-driven system built on Meta's official Instagram APIs.
This post covers how the comment reaches your server, how the DM gets sent, which limits you have to design around, and why "give us your password" bots are a bad idea. It stays at the architecture level on purpose. Meta changes endpoint versions, permission names and limits, so treat the official Instagram Platform docs as the source of truth for exact values.
The building blocks
A comment-to-DM flow needs four things:
- An Instagram professional account (Business or Creator). The messaging and comment APIs are not available for personal accounts.
- A Meta app with the right permissions, granted by the account owner through OAuth. At a high level you need permission to read and manage comments and permission to manage messages. The exact permission names depend on whether you use "Instagram API with Instagram Login" or the Facebook Login flow, and production access to other people's accounts goes through Meta's App Review.
- A webhook endpoint that Meta can call when something happens on the account.
- A worker that decides what to do with each event and calls the Graph API to reply.
None of this involves the user's Instagram password. The owner signs in on Meta's own screen, approves the permissions, and your app gets an access token scoped to what they approved, which they can revoke at any time.
Step 1: receiving comments via webhooks
Polling every post for comments is slow and wastes rate limits. Instead, you subscribe your app to webhook fields for the account: the comments field for this flow, plus the messaging fields for DM features.
Webhook setup has two parts you should get right on day one:
- Verification handshake. When you register the callback URL, Meta sends a GET request with a challenge value and the verify token you configured. Your endpoint checks the token and echoes the challenge back.
- Payload signatures. Event notifications are POST requests signed with your app secret (an HMAC SHA-256 signature in a request header). Verify that signature before you trust the body. Otherwise anyone who finds your URL can make your bot send DMs.
A comment notification carries roughly this information: which account it is for, the comment ID, the comment text, who wrote it, and which media item it is on. The shape below is illustrative only, simplified from the real payload, so check the docs for exact field names:
// ILLUSTRATIVE ONLY: simplified, not the exact Meta payload
{
"object": "instagram",
"entry": [{
"id": "<ig-account-id>",
"time": 1760000000,
"changes": [{
"field": "comments",
"value": {
"id": "<comment-id>",
"text": "GUIDE please!",
"from": { "id": "<commenter-id>", "username": "someone" },
"media": { "id": "<media-id>" }
}
}]
}]
}
Your handler should verify the signature, put the event on a queue, and return 200 quickly. If your endpoint is slow or keeps failing, Meta retries deliveries and may eventually disable the subscription. Do the real work in a background worker.
Step 2: matching the rule
The worker looks up which automation, if any, applies:
- Is this media item one the account owner chose? Some flows apply to one Reel, others to every post.
- Does the comment text match the keyword? Normalise case and whitespace, strip emoji if you need to, and decide whether "guide pls" counts as a match for "GUIDE". Exact-word matching is more predictable than substring matching (you don't want "misguided" to trigger it).
- Is the commenter the account itself? If your bot posts a public reply, that reply can show up as a new comment event. Skip comments authored by the connected account, or you can end up in a loop.
Step 3: the private reply
This is the key concept. Normally a business account can only DM someone after that person has messaged it first. The private replies feature is the exception Meta made for exactly this case: it lets the account send a DM to someone in response to their comment, by addressing the message to the comment rather than to a user.
Conceptually, you call the messaging endpoint with a recipient that points to the comment ID instead of a user ID. Again, illustrative pseudo-code, not a copy-paste request:
# ILLUSTRATIVE ONLY: check Meta's docs for the current endpoint and version
def send_private_reply(account, comment_id, text, link):
body = {
"recipient": {"comment_id": comment_id}, # address the comment, not the user
"message": {"text": f"{text}\n{link}"},
}
return graph_post(f"/{account.ig_id}/messages", body, token=account.token)
The important constraints, at a high level:
- One private reply per comment. You get one message in response to a comment. Further messages need the person to reply in the DM thread, which opens a normal conversation.
- It only works for a limited time after the comment. Meta's docs define a window of days, not months. A backlog job that tries to DM people who commented weeks ago will fail. Check the current documentation for the exact window.
- It may land in message requests, depending on the person's settings. Write it so it makes sense even if they open it hours later.
Many flows also post a public reply under the comment ("Sent! Check your DMs") through the comment replies endpoint. It shows other viewers the flow works. Rotate a few variations so the thread doesn't fill with identical text.
Step 4: the messaging window
Once the person replies to your DM, you are in a standard conversation, and the standard messaging rules apply. The main one: a business can respond freely within 24 hours of the user's last message. Outside that window, sending is restricted to specific cases Meta allows (for example, a human agent reply within a longer window, which needs its own permission).
This affects your design:
- Put the important content (the link, the answer) in the first message. Don't hold it back for a follow-up you might not be allowed to send.
- If you build multi-step flows (ask for an email, then send the resource), track the timestamp of the user's last message per conversation and check it before every send.
- When a conversation needs a human, flag it and pause the automation for that thread.
Step 5: rate limits, retries and idempotency
A Reel that takes off can produce thousands of comments in an hour. That is where hobby scripts fall over.
Rate limits. The Graph API applies per-app and per-account limits, and messaging has its own throughput limits. Put sends behind a per-account queue with a token bucket, and back off on throttling errors. Read the headers and error codes Meta returns rather than hard-coding a number from a blog post.
Duplicate deliveries. Webhooks are delivered at least once, so you will sometimes receive the same comment event twice. Use the comment ID as an idempotency key:
# ILLUSTRATIVE ONLY
def handle_comment(event):
key = f"private-reply:{event.comment_id}"
if not store.set_if_absent(key, ttl_days=30):
return # already handled (or in progress)
rule = match_rule(event)
if rule is None or event.author_id == event.account_id:
return
enqueue_send(event.account_id, rule, event.comment_id)
Since only one private reply is allowed per comment, a duplicate send fails or looks spammy. Record each outcome (sent, failed with error code, skipped) so retries only re-run safe cases.
Token health. Tokens expire or get revoked. Treat auth errors as a signal to pause that account's automations and notify the owner, not something to retry forever.
Why password-based bots are risky
Before the official APIs covered DMs, many tools logged in with the user's username and password and drove the app like a person. Some still do. The problems:
- Credential exposure. A third party holding a plain password, often with 2FA codes too, is a large attack surface. A breach at the vendor is a breach of every customer account.
- Terms and enforcement. Automating Instagram through unofficial access goes against Meta's terms. Platforms actively detect it, and the consequences range from action blocks to disabled accounts. That risk falls on the account owner, not the vendor.
- Fragility. Private endpoints change without notice, so automations break silently.
- No consent model. OAuth scopes show what an app can do and can be revoked. A password grants everything.
The official route is more work, but it is the only one you can build a business on.
A worked example
If you would rather not run this pipeline yourself, hosted tools package the same pieces. AutoPilotDM is one example: it connects an Instagram professional account through Meta's official permissions (no password), and lets the owner pick a post or Reel, set a keyword, and write the public reply and the DM. The same event pipeline handles keyword DM replies, welcome messages, Story mention and reply responses, FAQ answers, in-chat lead forms and human handover. You can see the setup flow in their tutorials on YouTube or at autopilotdm.com.
Whether you build or buy, the checklist is the same:
- Verify webhook signatures and respond fast.
- Deduplicate on comment ID.
- Put the value in the first DM.
- Respect the messaging window and rate limits.
- Give humans an easy way to take over.
- Never ask for a password.
Written by the AutoPilotDM Team. AutoPilotDM is an Instagram-only DM automation tool built on Meta's official API.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.