Instagram banned your scraper again. You switched IPs. It banned you again. You tried rotating proxies. Still banned. Here's the real reason — and the fix that actually works in 2026.
Instagram's anti-bot system has four layers, and most scrapers fail at the first one. Understanding each layer is the key to building something that doesn't get banned every 10 minutes.
The moment your request hits Instagram's servers, they check your IP against known data center ranges. AWS, Google Cloud, Hetzner, DigitalOcean, Contabo — all flagged. If your IP belongs to any major hosting provider, Instagram sees a bot before you've even sent a request.
Fix: Residential IP from a real ISP (home internet provider, not a data center).
Instagram runs JavaScript on page load that checks: your user agent, screen resolution, timezone, language, canvas fingerprint, WebGL renderer, installed fonts, and dozens of other signals. A headless Chromium with default settings fails all of these.
Fix: Spoof all fingerprint signals to match a real mobile device (iPhone 15 Pro works well).
Even with a clean IP and good fingerprint, automated behavior gets caught. Instant page loads (humans take 200-800ms to start scrolling). Straight-line mouse movement. Instant form fills. Zero time reading content.
Fix: Human-like delays, Bezier mouse curves, variable typing speed.
Real users view ~20-40 profiles per hour. An automated scraper hitting 1,000 profiles per hour is obvious. Even with a perfect fingerprint, volume triggers a flag.
Fix: Rate limit yourself to under 100 requests per hour per IP. Add random delays.
Romania works particularly well for Instagram because the IP reputation is clean, the ISPs (DIGI Romania, RCS&RDS) are residential, and Instagram doesn't geo-block Romanian IPs. You can also use US, UK, or German residential IPs depending on what content you need.
npm install playwright human-browser
npx playwright install chromium --with-deps
You need a residential proxy — a real home IP address, not a data center IP. You can get credentials at humanbrowser.dev starting at $13.99/mo, or set up your own with Bright Data.
const { launchHuman } = require('human-browser');
async function scrapeProfile(username) {
// Launch with Romanian residential IP
const { browser, page, humanScroll, sleep } = await launchHuman({
country: 'ro', // Romania — clean for Instagram
mobile: true // iPhone 15 Pro fingerprint
});
try {
await page.goto(`https://www.instagram.com/${username}/`, {
waitUntil: 'domcontentloaded',
timeout: 30000
});
// Wait like a human would (200-800ms)
await sleep(Math.random() * 600 + 200);
// Scroll naturally — Instagram checks this
await humanScroll(page, 'down');
await sleep(Math.random() * 1000 + 500);
// Extract profile data
const data = await page.evaluate(() => {
const metaDesc = document.querySelector('meta[name="description"]');
return {
description: metaDesc ? metaDesc.getAttribute('content') : null,
title: document.title,
url: window.location.href
};
});
console.log('Profile data:', data);
return data;
} finally {
await browser.close();
}
}
// Rate limit: 1 request every 36–60 seconds = ~80/hour max
async function scrapeMultipleProfiles(usernames) {
const results = [];
for (const username of usernames) {
const data = await scrapeProfile(username);
results.push(data);
// Random delay: 36-60 seconds between profiles
const delay = Math.floor(Math.random() * 24000) + 36000;
console.log(`Waiting ${(delay/1000).toFixed(1)}s before next profile...`);
await new Promise(r => setTimeout(r, delay));
}
return results;
}
| Data Type | Scrape-able | Notes |
|---|---|---|
| Public profile bio | ✓ Yes | Available in meta tags |
| Post captions (public) | ✓ Yes | Needs scrolling + parsing |
| Follower count | ✓ Yes | Visible on public profiles |
| Post images | ✓ Yes | Extract src URLs |
| Stories | ✗ Requires login | Need authenticated session |
| Private profiles | ✗ No | Follow request required |
| DMs | ✗ No | Private, not scrape-able |
// Safe rate limiting for Instagram scraping (2026)
const RATE_LIMITS = {
profilesPerHour: 80, // Max profile views per hour per IP
delayBetweenRequests: { // ms
min: 36000, // 36 seconds minimum
max: 60000 // 60 seconds maximum
},
scrollsPerPage: { min: 2, max: 5 },
timeOnPage: { min: 8000, max: 25000 } // 8-25 seconds
};
async function safeDelay() {
const { min, max } = RATE_LIMITS.delayBetweenRequests;
const delay = Math.floor(Math.random() * (max - min)) + min;
await new Promise(r => setTimeout(r, delay));
}
// If you hit a CAPTCHA or challenge page — stop and rotate IP
async function checkForBlock(page) {
const title = await page.title();
if (title.includes('challenge') || title.includes('login')) {
throw new Error('Instagram block detected — rotate IP and wait 30 min');
}
}
If you need to scrape data that requires a logged-in session, you have two options:
Log in manually once, export your session cookies, then inject them into Playwright. The residential IP + iPhone fingerprint keeps the session alive much longer than a data center IP would.
Use humanType() and humanClick() to fill the login form. Always use a residential IP — Instagram flags logins from data centers immediately.
Yes, by default. Standard Playwright on a data center server is detected immediately via IP reputation and browser fingerprinting. With a residential IP and proper fingerprint spoofing, it's indistinguishable from a real user.
A residential IP from a real home ISP. Romanian residential IPs work well for Instagram in 2026. US residential IPs work too but are slightly more expensive.
Under 100 profile views per hour per IP. Add 36-60 second random delays between requests. Never make requests at a fixed interval — the randomness matters.
Get residential proxy credentials and start in minutes. Script is open source — you only pay for the residential IP.
Get Started — from $13.99/mo →