TL;DR: Instagram does not ban accounts because a bot touched them. It flags behaviour that looks automated — even intervals, bursts, identical texts, the absence of browsing. This is a build log of a small automation stack that tries to behave like a person, the research behind every guardrail, and the honest answer to what any of that buys you.
Before the technical part, the boring part: automated interaction violates Instagram's terms of service. This was built for a single, clearly marked burner account, for research into how platforms detect automation — not for scale, not for spam, and not for anyone's main account. Everything below is documented because the detection side is the interesting engineering problem, not because there is a safe way to run a bot.
The premise
The goal was narrow: one account, follow, like, comment and unfollow, from a local machine, with the ability to stop everything instantly and see exactly what happened afterwards. No cloud, no farm, no proxies resold as a service. A worker on a laptop, a small web console, and a ledger.
The interesting part is not the API calls. It is that an automation system optimises for exactly the signals a platform uses to detect it.
What the platform actually measures
Instagram does not publish its limits, and the numbers that circulate online are folklore. What Meta does say is that thresholds are dynamic and depend on account age, history, velocity, repetition and report history. Read carefully, that is a description of behaviour shape, not of a counter.
Four properties matter more than any daily total:
- Interval regularity. Machine schedules produce even spacing. People do not click "like" every 30.0 seconds for an hour.
- Velocity and clustering. Twenty actions in ninety seconds from a single source reads as scripted even when the day's total is small. Actions per hour matter more than actions per day.
- Repetition. The same comment text across posts is the clearest fingerprint there is.
- No browsing. A session that only ever writes — follow, like, comment, close — never scrolls, never opens a profile it does not act on, never pauses to read.
What happens when those signals accumulate is a ladder, not a switch:
| Rung | Symptom | Typical duration |
|---|---|---|
| 1 | "Try again later" soft warning | minutes to hours |
| 2 | Action block on one action | hours to 48 h |
| 3 | Extended block, sometimes several actions | 3 to 7 days |
| 4 | Reach suppression, no error shown | 14 to 30 days |
| 5 | Feature limits or account disabled | until reviewed |
The trap is rung two. Retrying a blocked action extends the block — which is how a soft warning becomes a week. Any sane client treats a rate-limit exception as a stop signal, not as a retry.
The stack
The system is three pieces and a file of state.
| Piece | What it does |
|---|---|
| Worker | Python process on the loopback interface with a bearer token; wraps the client library and owns all state |
| Console | Password-gated page that talks only to the local API proxy — the worker token never reaches the browser |
| State | Plain files: the session, the account profile, the limits, the queue, the activity ledger, the used-comment log |
| Ledger | One append-only line per action, which is what makes a run auditable afterwards |
Two design rules keep it boring. Jobs default to dry run — every runner logs what it would do and touches nothing until execution is explicitly requested. And at most two jobs run at once, never two of the same kind, so a comment job and an unfollow job can overlap, but the action rate stays inside the pacing envelope.
The pacing engine
Every action is separated from the next by a delay drawn from a weighted distribution, not a uniform one. Uniform randomness still looks machine-made, because the variance is wrong: humans mostly act quickly, occasionally dawdle, and rarely produce the long tail.
Each action type has a range, and each range is sampled in three bands:
DEFAULT DELAYS:
| Action | Range | Drawn from |
|---|---|---|
| Follow | 45 to 120 s | mostly the lower third, 5 percent long tail |
| Like | 20 to 60 s | same shape |
| Comment | 60 to 180 s | same shape |
| Unfollow | 40 to 90 s | same shape |
| Session pass | 30 to 120 s | same shape |
Then a global jitter multiplies the result by a random factor of up to 40 percent, and the value is explicitly forced to be non-round — a delay of 102.4 seconds, never 100. Round numbers are a tell.
On top of that:
- Micro-breaks. After every four to eight actions the pacer inserts a two-to-eight minute pause. A long session with no breaks is a burst, whatever the individual delays look like.
- An active window. Jobs only start between 08:00 and 23:00 local time unless explicitly forced. Automating at 04:00 is free evidence.
- Settle-in. Every job waits five to forty-five seconds after starting before the first action, the way a person does not click the instant a page opens.
- Scene pauses. During feed sessions, every pass is followed by a random two-to-eight minute pause — the "watching something" gap.
In code the weighted draw is four lines:
span = max_s - min_s
roll = random.random()
if roll < 0.7: return random.uniform(min_s, min_s + span * 0.4)
if roll < 0.95: return random.uniform(min_s + span * 0.4, max_s)
return random.uniform(max_s, max(max_s * 1.8, max_s + 30))
The caps
Delays shape a session; caps shape a day. Both are enforced server-side, so the console cannot talk the worker into exceeding them.
| Action | Per hour | Per day |
|---|---|---|
| Follow | 10 | 30 |
| Like | 30 | 100 |
| Comment | 5 | 20 |
| Unfollow | 25 | 150 |
Two of these numbers were added because of the research, not despite it. The daily caps existed from the start; the hourly caps did not, and the research is unambiguous that velocity is judged per hour, not per day.
The rest of the guardrails sit at the same layer:
- Warm-up. A fresh account gets a multiplier on every cap — 10 percent, then 25, then 50 — for its first fourteen days, and the console refuses to execute until the account is at least a day old with a complete profile.
- Hard pause. Rate-limit exceptions do not just stop the current job; they write a
paused_untiltimestamp that blocks every job for 24 to 48 hours (48 to 72 for the stronger signals). The soft warning is treated as a verdict, not a hiccup. - Kill switch. A single file on disk aborts everything: running jobs check it on every sleep tick, and the API refuses new work while it exists.
- One account, one egress. The signup and the automation traffic leave from the same network. A residential IP changing mid-life is a signal, not a disguise.
Not saying the same thing twice
The comment system is the part the research changed most, because repetition is the clearest automation fingerprint there is.
The pool is 152 comments across 17 categories — from typography and art to datascience and city — but the pool is not the interesting bit. The matching is:
- A post's caption is classified into a category using keyword rules over the caption and hashtags.
- A comment is drawn from that category, avoiding anything used within the session.
- Used comments are written to a log with a thirty-day window, so the same text does not reappear weeks apart.
- The operator can also edit the draft — the system suggests, it does not publish blind.
A comment job that posts four of five drafts with one rejected by the classifier is normal. What it must never do is post the same sentence twice.
Reading the feed like a person
The most interesting job is the one that does the least: a "sofa session" that reads the real timeline feed.
It pulls the authenticated home feed, filters ads, skips posts already liked, shuffles each pass, and then behaves like someone half-watching: it likes a configurable share of what it sees (65 percent by default), comments on a small classified subset, and "watches a scene" for a random two to eight minutes between passes. Nothing is targeted. The only product is history — an account with a timeline and idle time looks like an account someone uses, which is the exact property a bot lacks.
What the research changed
Translating the research into code produced a short, uncomfortable list. Four items shipped:
- Hourly caps were added because per-hour velocity is the actual trigger.
- Warm-up became a profile with multipliers rather than a note in a readme.
- The hard pause replaced per-job cooldowns, so the account stops, not just the job.
- Comment rotation got a persistent log with a thirty-day window.
Three items are deliberately not implemented, because they would make the system look worse, not better: ban evasion (a replacement account from the same device is linked and dies within hours), fake engagement, and anything that touches a main account. Cross-account contagion is real — a linked page banned for an unrelated reason can take the account down with it.
What none of this guarantees
The honest summary is this: every number in this article is a mitigation, not a control. Instagram's thresholds are unpublished and dynamic, and the only reliable statement is behaviour-shaped — "this looks automated" is a judgement, and judgements can be wrong in both directions. A perfectly paced bot can still be flagged by a report wave from users; a sloppy manual session can be flagged by nothing.
That asymmetry is the takeaway. All this engineering buys is a lower probability of being machine-obvious, and a system that stops cleanly when the platform says stop. If the account is blocked, the only protocol that works is the boring one: stop the action immediately, pause everything for a day or two, keep using the app like a human, come back at a quarter of the volume. If a third-party tool ever had access, revoke it and change the password.
And the part that matters most: the console for this system is switched off. It runs only against a dedicated burner account, and it stays off until there is one.
Takeaways
- Platforms detect behaviour shape — interval regularity, velocity, repetition, the absence of browsing — long before they count actions.
- Retrying a blocked action extends the block; a rate-limit exception is a stop signal, not a retry.
- Weighted delays, jitter, micro-breaks and an active window attack the shape; caps attack the volume. Both are necessary and neither is sufficient.
- Repetition is the clearest fingerprint, which is why the comment system is about classification and rotation, not volume.
- Some things cannot be mitigated from the client at all: ban evasion, fake engagement and cross-account contagion simply make it worse.