<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mike</title>
    <description>The latest articles on DEV Community by mike (@saqlvation).</description>
    <link>https://dev.to/saqlvation</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4040721%2F40ff4251-82f3-44a2-8af8-2891f0eda355.webp</url>
      <title>DEV Community: mike</title>
      <link>https://dev.to/saqlvation</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saqlvation"/>
    <language>en</language>
    <item>
      <title>I Built an Instagram Bot Cleaner in Python Here's the Architecture</title>
      <dc:creator>mike</dc:creator>
      <pubDate>Tue, 21 Jul 2026 20:38:56 +0000</pubDate>
      <link>https://dev.to/saqlvation/i-built-an-instagram-bot-cleaner-in-python-heres-the-architecture-4pm9</link>
      <guid>https://dev.to/saqlvation/i-built-an-instagram-bot-cleaner-in-python-heres-the-architecture-4pm9</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Saqlvation/instapurge" rel="noopener noreferrer"&gt;https://github.com/Saqlvation/instapurge&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=5vW7f2HbNiA" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=5vW7f2HbNiA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TL;DR&lt;br&gt;
I built InstaPurge, a Python-based Instagram bot cleaner that identifies and removes fake followers, inactive accounts, and bot-driven interactions from your Instagram profile. This post breaks down the full architecture, design decisions, and lessons learned.&lt;br&gt;
The Problem&lt;br&gt;
Instagram is flooded with bots. Over time, your follower count becomes a vanity metric — inflated by fake accounts, engagement pods, and inactive users. Worse, these bots can drag down your engagement rate, which the algorithm uses to decide how widely to distribute your content.&lt;br&gt;
I wanted a tool that could:&lt;br&gt;
Detect bot accounts and inactive followers&lt;br&gt;
Analyze engagement patterns to find low-quality connections&lt;br&gt;
Clean them out safely, without triggering Instagram's rate limits or anti-automation systems&lt;br&gt;
Log everything so you know exactly what was removed and why&lt;br&gt;
High-Level Architecture&lt;br&gt;
plain&lt;br&gt;
┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐&lt;br&gt;
│   CLI / Web UI  │────▶│  Core Controller   │────▶│  Instagram API  │&lt;br&gt;
│  (User Input)   │     │  (Orchestration)   │     │  (Data Source)  │&lt;br&gt;
└─────────────────┘     └──────────────────┘     └─────────────────┘&lt;br&gt;
                               │&lt;br&gt;
                               ▼&lt;br&gt;
                        ┌──────────────────┐&lt;br&gt;
                        │  Analysis Engine │&lt;br&gt;
                        │  (Bot Detection) │&lt;br&gt;
                        └──────────────────┘&lt;br&gt;
                               │&lt;br&gt;
                               ▼&lt;br&gt;
                        ┌──────────────────┐&lt;br&gt;
                        │  Action Queue    │&lt;br&gt;
                        │  (Rate-Limited   │&lt;br&gt;
                        │   Execution)     │&lt;br&gt;
                        └──────────────────┘&lt;br&gt;
                               │&lt;br&gt;
                               ▼&lt;br&gt;
                        ┌──────────────────┐&lt;br&gt;
                        │  SQLite / JSON   │&lt;br&gt;
                        │  (Audit Logging) │&lt;br&gt;
                        └──────────────────┘&lt;br&gt;
Layer 1: The Instagram Bridge&lt;br&gt;
The biggest challenge with Instagram automation is not getting banned. Instagram aggressively detects and blocks automation tools. Here's how I approached it:&lt;br&gt;
Authentication Strategy&lt;br&gt;
Session-based login using instaloader or selenium to mimic real browser behavior&lt;br&gt;
Cookie persistence so you don't re-authenticate every run (a major red flag for IG)&lt;br&gt;
2FA handling with manual fallback — because breaking into your own account shouldn't be automatic&lt;br&gt;
Data Collection&lt;br&gt;
Instead of scraping (which violates ToS and gets you blocked), I use Instagram's private API endpoints through a library like instaloader or instagrapi. This gives structured access to:&lt;br&gt;
Follower/following lists&lt;br&gt;
Post metadata (likes, comments, timestamps)&lt;br&gt;
User profiles (bio, follower count, post count, profile picture)&lt;br&gt;
Python&lt;/p&gt;

&lt;h1&gt;
  
  
  Simplified data fetcher
&lt;/h1&gt;

&lt;p&gt;class InstagramBridge:&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self, session_file: str):&lt;br&gt;
        self.loader = instaloader.Instaloader()&lt;br&gt;
        self.loader.load_session_from_file(session_file)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_followers(self, username: str) -&amp;gt; Iterator[Profile]:
    profile = instaloader.Profile.from_username(self.loader.context, username)
    return profile.get_followers()

def get_following(self, username: str) -&amp;gt; Iterator[Profile]:
    profile = instaloader.Profile.from_username(self.loader.context, username)
    return profile.get_followers()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Layer 2: The Bot Detection Engine&lt;br&gt;
This is where the magic happens. A "bot" isn't just one thing — it's a spectrum. I built a scoring system that weighs multiple signals:&lt;br&gt;
Signal 1: Profile Heuristics&lt;br&gt;
Fogli di calcolo&lt;br&gt;
Signal  Weight  Why It Matters&lt;br&gt;
No profile picture  High    Bots rarely upload custom photos&lt;br&gt;
Default bio / no bio    Medium  Minimal effort = minimal human&lt;br&gt;
Follower-to-following ratio &amp;lt; 0.1   High    Following thousands, followed by few = spam pattern&lt;br&gt;
Account age &amp;lt; 30 days   Medium  Fresh accounts are often throwaways&lt;br&gt;
Username has random digits  Low john_doe_2847 screams automation&lt;br&gt;
Signal 2: Engagement Patterns&lt;br&gt;
No likes on your posts — if they never interact, why are they following you?&lt;br&gt;
Generic comments — "Nice pic!" or emojis-only on every post&lt;br&gt;
Comment timing — posted at inhumanly consistent intervals (every 47 minutes)&lt;br&gt;
Signal 3: Network Analysis&lt;br&gt;
Mutual connections — bots often cluster; if 10 of your followers all follow the same 500 spam accounts, they're likely part of a botnet&lt;br&gt;
Graph clustering using simple connected-component analysis&lt;br&gt;
The Scoring Algorithm&lt;br&gt;
Python&lt;br&gt;
class BotScorer:&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self, weights: dict):&lt;br&gt;
        self.weights = weights&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def score(self, user: UserProfile, interactions: List[Interaction]) -&amp;gt; float:
    score = 0.0

    # Profile heuristics
    if not user.has_profile_pic:
        score += self.weights['no_pic']
    if user.followers / max(user.following, 1) &amp;lt; 0.1:
        score += self.weights['low_ratio']
    if user.account_age_days &amp;lt; 30:
        score += self.weights['new_account']

    # Engagement heuristics
    if not interactions:
        score += self.weights['no_interaction']
    elif self._is_generic_comment_pattern(interactions):
        score += self.weights['generic_comments']

    return min(score, 1.0)  # Cap at 1.0

def classify(self, score: float) -&amp;gt; str:
    if score &amp;gt;= 0.8: return 'bot'
    if score &amp;gt;= 0.5: return 'suspicious'
    return 'human'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Layer 3: The Action Queue &amp;amp; Rate Limiter&lt;br&gt;
Instagram's rate limits are aggressive and undocumented. Through trial and error, I found these safe thresholds:&lt;br&gt;
Fogli di calcolo&lt;br&gt;
Action  Safe Limit  Cooldown&lt;br&gt;
Unfollow    ~150/day    20-60s between actions&lt;br&gt;
Profile fetch   ~200/hour   2-5s between requests&lt;br&gt;
Like/Comment    ~100/day    Randomized delays&lt;br&gt;
I implemented a token-bucket rate limiter with jitter to make traffic look organic:&lt;br&gt;
Python&lt;br&gt;
import random&lt;br&gt;
import time&lt;br&gt;
from dataclasses import dataclass&lt;br&gt;
from collections import deque&lt;/p&gt;

&lt;p&gt;@dataclass&lt;br&gt;
class RateLimiter:&lt;br&gt;
    max_actions: int      # per window&lt;br&gt;
    window_seconds: int&lt;br&gt;
    min_delay: float&lt;br&gt;
    max_delay: float&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def __post_init__(self):
    self.actions = deque()

def wait_and_execute(self, action: callable):
    self._enforce_rate()
    delay = random.uniform(self.min_delay, self.max_delay)
    time.sleep(delay)
    action()
    self.actions.append(time.time())

def _enforce_rate(self):
    now = time.time()
    # Remove actions outside the window
    while self.actions and now - self.actions[0] &amp;gt; self.window_seconds:
        self.actions.popleft()

    if len(self.actions) &amp;gt;= self.max_actions:
        sleep_time = self.window_seconds - (now - self.actions[0])
        if sleep_time &amp;gt; 0:
            time.sleep(sleep_time + random.uniform(5, 15))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Layer 4: Audit &amp;amp; Safety&lt;br&gt;
Every action is logged to a local SQLite database with a full snapshot of the decision:&lt;br&gt;
Python&lt;br&gt;
@dataclass&lt;br&gt;
class RemovalRecord:&lt;br&gt;
    timestamp: datetime&lt;br&gt;
    username: str&lt;br&gt;
    bot_score: float&lt;br&gt;
    signals_triggered: List[str]&lt;br&gt;
    action_taken: str  # 'unfollowed', 'flagged', 'skipped'&lt;br&gt;
    session_id: str&lt;br&gt;
This serves two purposes:&lt;br&gt;
Undo capability — if you accidentally purge a real friend, you have the data to reverse it&lt;br&gt;
Pattern analysis — over time, you can tune your scoring weights based on false positives&lt;br&gt;
The CLI Experience&lt;br&gt;
I kept the interface minimal but informative using rich for terminal UI:&lt;br&gt;
plain&lt;br&gt;
┌─────────────────────────────────────────┐&lt;br&gt;
│  InstaPurge v1.0.0                      │&lt;br&gt;
│  ─────────────────                      │&lt;br&gt;
│                                         │&lt;br&gt;
│  [1] Scan followers                     │&lt;br&gt;
│  [2] Review flagged accounts            │&lt;br&gt;
│  [3] Execute cleanup (dry-run)          │&lt;br&gt;
│  [4] Execute cleanup (live)             │&lt;br&gt;
│  [5] View audit log                     │&lt;br&gt;
│  [q] Quit                               │&lt;br&gt;
│                                         │&lt;br&gt;
└─────────────────────────────────────────┘&lt;br&gt;
The dry-run mode is critical — it shows you exactly what would happen without making any API calls.&lt;br&gt;
Key Design Decisions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why not use the official Instagram API?
The Basic Display API doesn't expose follower lists or allow unfollow actions. The Graph API is for business accounts only. Private API libraries are the only viable path for personal account management.&lt;/li&gt;
&lt;li&gt;Why SQLite over a cloud database?
Privacy. Your follower list and bot scores shouldn't leave your machine. Local-first architecture keeps everything under your control.&lt;/li&gt;
&lt;li&gt;Why Python?
instaloader and instagrapi are mature Python libraries
rich makes beautiful CLI interfaces trivial
pandas + scikit-learn make signal analysis and clustering easy if you want to level up to ML-based detection later
What I Learned
Rate limits are the real enemy. You can build the smartest bot detector in the world, but if you hit Instagram's rate limit, you're locked out for 24-48 hours. Patience wins.
False positives hurt. I initially set thresholds too aggressively and nearly unfollowed a few real friends. The scoring system needs a "review queue" for edge cases.
Instagram changes fast. The private API shifts frequently. I abstracted the bridge layer so I can swap libraries without touching the core logic.
Future Improvements
ML-based detection: Train a classifier on labeled bot/human accounts using profile features
Web dashboard: A React frontend for non-technical users
Scheduled runs: Cron-based daily scans with email reports
Cross-platform: Package as a standalone executable with PyInstaller
Try It Out
bash
git clone &lt;a href="https://github.com/Saqlvation/instapurge.git" rel="noopener noreferrer"&gt;https://github.com/Saqlvation/instapurge.git&lt;/a&gt;
cd instapurge
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -r requirements.txt
python -m instapurge --help
⚠️ Disclaimer: This tool is for educational purposes and personal account management. Use responsibly and at your own risk. Instagram's Terms of Service prohibit certain automated actions.
Questions? Thoughts?
Drop a comment below — I'd love to hear how you'd approach bot detection differently, or if you've built something similar!&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>automation</category>
      <category>instagram</category>
    </item>
  </channel>
</rss>
