This guide walks you through building a Python backend that receives InstantDM webhook events and sends Instagram DMs via the API. Includes both Flask and FastAPI examples.
FastAPI Version (Recommended)
pip install fastapi uvicorn httpx python-dotenv
# main.py
import os
import hmac
import hashlib
import httpx
from datetime import datetime
from fastapi import FastAPI, Request, HTTPException
from dotenv import load_dotenv
load_dotenv()
app = FastAPI()
API_KEY = os.getenv("INSTANTDM_API_KEY")
API_URL = "https://api.instantdm.com/api-webhook"
def verify_signature(body: bytes, signature: str) -> bool:
expected = hmac.new(API_KEY.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)
async def send_dm(recipient_id: str, message: str, msg_type: str = "text", **kwargs):
async with httpx.AsyncClient() as client:
response = await client.post(
API_URL,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"action": "send_message",
"type": msg_type,
"recipient_id": recipient_id,
"message": message,
**kwargs,
},
)
return response.json()
@app.post("/webhook")
async def webhook(request: Request):
body = await request.json()
event = body.get("event")
data = body.get("data", {})
timestamp = body.get("timestamp")
print(f"[{timestamp}] Event: {event} from @{data.get('username')}")
if event == "flow_completed":
await handle_flow_completed(data)
elif event == "comment":
await handle_comment(data)
elif event == "dm_received":
await handle_dm_received(data)
return {"status": "ok"}
async def handle_flow_completed(data: dict):
rv = data.get("response_variables", {})
name = rv.get("full_name", "there")
print(f"Flow '{data.get('flow_name')}' completed by @{data.get('username')}")
# Save to database, send to CRM, etc.
await send_dm(
data["instagram_user_id"],
f"Thanks {name}! We received your info and will follow up soon.",
)
async def handle_comment(data: dict):
print(f"Comment from @{data.get('username')}: \"{data.get('comment_text')}\"")
async def handle_dm_received(data: dict):
print(f"DM from @{data.get('username')}: \"{data.get('message_text')}\"")
@app.get("/health")
async def health():
return {"status": "ok"}
Run with:
uvicorn main:app --host 0.0.0.0 --port 3000
Flask Version
pip install flask requests python-dotenv
# app.py
import os
import hmac
import hashlib
import requests
from flask import Flask, request, jsonify
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
API_KEY = os.getenv("INSTANTDM_API_KEY")
API_URL = "https://api.instantdm.com/api-webhook"
def send_dm(recipient_id, message, msg_type="text", **kwargs):
return requests.post(
API_URL,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"action": "send_message",
"type": msg_type,
"recipient_id": recipient_id,
"message": message,
**kwargs,
},
).json()
@app.route("/webhook", methods=["POST"])
def webhook():
body = request.json
event = body.get("event")
data = body.get("data", {})
if event == "flow_completed":
rv = data.get("response_variables", {})
name = rv.get("full_name", "there")
send_dm(data["instagram_user_id"], f"Thanks {name}! We'll follow up soon.")
return jsonify({"status": "ok"})
@app.route("/health")
def health():
return jsonify({"status": "ok"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=3000)
Deployment
Docker
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 3000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "3000"]
Deploy to Railway, Render, or Fly.io
Push to GitHub and connect the repo. All platforms auto-detect Python apps.
What's Next
- Read the Node.js backend guide if you prefer JavaScript.
- Deploy to AWS Lambda for serverless.
- Connect to PostgreSQL for data persistence.
- Explore the full API docs at instantdm.com/instagram-api-docs.