Automating Instagram DMs with Playwright (My First Browser Automation Project)
Sharing how I built a Playwright + Express automation to send Instagram DMs for testing, with sessions and n8n integration.
August 22, 2025 (1y ago)
3 min read
β‘ Recently, I started exploring browser automation as part of my learning journey. I was curious: "Can I automate something like Instagram DMs just for testing?"
So I gave it a try with Playwright + Express.js, and in this blog Iβll share how I built it, the problems I faced, and what I learned. (And yes β this is strictly for educational/testing purposes, not spam π«)
InfoBefore we dive in: - β Donβt use this for bulk DM or spamming. - β Safe for testing, demos, and learning automation. - π« Misuse may get your account flagged.
π¦ Setting up the Project
First I created a fresh Node.js project and added the dependencies:
{ "name": "browser-automation", "version": "1.0.0", "main": "index.js", "license": "ISC", "dependencies": { "express": "^5.1.0", "playwright": "^1.54.2" } }
Then ran:
pnpm install npx playwright install # takes 5β8 min initially
π§© Writing the Instagram DM Automation
Hereβs the heart of my experiment β a simple Express server with a Playwright script.
When I send a request with a username + message, it automatically opens Instagram and sends the DM.
import express from "express"; import { chromium } from "playwright"; const app = express(); app.use(express.json()); const userDataDir = "./instagram_session"; // keeps session // Function to send Instagram DM async function sendInstagramMessage(page, username, message) { try { console.log(`[INFO] Sending message to @${username}...`); await page.goto(`https://www.instagram.com/${username}/`); await page.waitForSelector("text=Message", { timeout: 10000 }); await page.click("text=Message"); const box = page.locator( 'div[aria-label="Message"][contenteditable="true"]', ); await box.click(); for (const char of message) { await page.keyboard.type(char, { delay: 80 + Math.random() * 120 }); } await page.keyboard.press("Enter"); console.log(`[SUCCESS] Message sent to @${username}`); return { success: true }; } catch (err) { console.error(`[ERROR]`, err.message); return { success: false, error: err.message }; } }
π Testing the API
I exposed the function through an Express route, so I could test it like this:
curl -X POST http://localhost:3001/instagram \ -H "Content-Type: application/json" \ -d '{"username": "target_username", "message": "Hello from Playwright π"}'
And boom π it worked! Seeing the DM get typed in real-time was very satisfying.
βοΈ Sessions & Templates
One thing I learned early: persistent sessions are super useful.
By saving the login in ./instagram_session, I didnβt need to log in every time.
I also added support for message templates to avoid hardcoding the text.
π Bonus: n8n Workflow
Since I also explore automation with n8n, I made a workflow that connects to this server. π Check the n8n Template JSON
Now I can trigger Instagram messages via n8n nodes β kind of like connecting Lego blocks.
β Key Takeaways
- Built my first Playwright + Express automation for Instagram DMs.
- Learned about persistent sessions and API-driven automation.
- Integrated it with n8n for more flexibility.
- And most importantly: this project is a reminder that automation is powerful β but needs to be used responsibly.
π₯ This was my first browser automation project with Playwright β and honestly, it made me more confident about exploring automation further.
Here are some other articles you might find interesting.

How Realtors Can Use AI to Qualify & Close More Clients
Learn how top realtors are using AI automation to qualify leads, nurture conversations, and close more deals β without spending hours texting or calling.

Building a Barcode Scanner & Generator in Next.js
How I researched, tested libraries, and built a simple yet powerful barcode scanner and generator app using Next.js.

