Integration Guide

How to Build a Custom Python Backend for InstantDM

Learn how to build a custom Python backend that receives InstantDM webhooks and sends Instagram DMs via the API. Complete Flask/FastAPI example.

Meta Business Partner
30,000+ creators
$9.99/mo flat

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

Ready to Automate Your Instagram DMs?

Join 30,000+ creators and brands using InstantDM today.

Start Your Free Trial

No credit card required. Setup in under 15 minutes.