How Instagram's September 2026 Change Broke Follow Trackers (and How Snapshot Diffing Works)
For years, every "recent follows" tracker on the internet was built on one quiet assumption: Instagram returns a Following list newest first. If you wanted to know who someone followed last, you fetched their list and re
For years, every "recent follows" tracker on the internet was built on one quiet assumption: Instagram returns a Following list newest first. If you wanted to know who someone followed last, you fetched their list and read the top.
On 9 September 2026, that assumption broke. There was no announcement and no changelog. A whole category of tools just started showing wrong results on the same morning.
This post explains what changed, why the "read the top of the list" model failed, and how the fallback most tools moved to, snapshot diffing, actually works. There's code, and a list of the bugs you will hit if you build it yourself.
Disclosure: I work on Klupt, one of the trackers that broke that day. I'll say where it fits at the end. This post was written with AI tools.
The old model: order was the only clock
Instagram never shared when someone followed an account. There is no followed_at field on another person's Following list. The only signal was position: the newest follows sat at the top.
So most trackers were very thin. Simplified, they did this:
// The pre-September model: trust the server's order.
const following = await fetchFollowingList(username); // newest first (assumed)
const recentFollows = following.slice(0, 20);
No storage, no history. The tool didn't sort anything. It just passed Instagram's order along to the user. That made these tools cheap to run and instant on the first search, which is exactly what people wanted.
What changed on 9 September 2026
The list started coming back ranked instead of chronological. Gramlens, a follower export tool, describes the new order as ranked for the viewer, so two people can open the same profile and see two different orders. IG Detective paused follow tracking because some "new" follows it showed were not new at all.
For the thin model above, this is the worst possible failure. Nothing errors. The code still returns 20 accounts. They are just the wrong 20, and the UI still labels them "recent".
That's a good lesson in general: if your feature depends on an undocumented ordering, you have no contract, only a habit. It can change on any day without a single failed request.
The fallback: snapshot diffing
If the server won't tell you what is new, you can work it out yourself: save the full list now, fetch it again later, and compare. Anything in the new list that wasn't in the old one is a new follow.
The core is a set difference:
/**
* Compare two snapshots of a Following list.
* Each item: { id: string, username: string }
*/
function diffSnapshots(previous, current) {
const prevIds = new Set(previous.map((a) => a.id));
const currIds = new Set(current.map((a) => a.id));
return {
added: current.filter((a) => !prevIds.has(a.id)),
removed: previous.filter((a) => !currIds.has(a.id)),
};
}
And a polling loop around it:
async function checkProfile(profile, store) {
const current = await fetchFullFollowingList(profile.username);
const previous = await store.getLatestSnapshot(profile.id);
await store.saveSnapshot(profile.id, current, Date.now());
if (!previous) return { added: [], baseline: true }; // first run
const { added } = diffSnapshots(previous.items, current);
return { added, since: previous.takenAt };
}
That's maybe 30 lines. The real work is in the edge cases.
Six bugs you will hit
1. Keying on usernames
Usernames change. If you diff on username, every rename looks like one unfollow plus one new follow. Always diff on the stable account ID and treat the username as display data.
2. The baseline problem
The first run has nothing to compare against, so it returns zero new follows. Your user searched a profile because they wanted an answer now, and you show them an empty list. This is the biggest product problem with snapshots, and code can't fix it. You can only be honest in the UI: "We started watching today. New follows will appear from here."
3. Partial fetches create fake unfollows
Big lists are paginated. If page 7 of 12 fails or gets rate limited, your "current" snapshot is missing hundreds of accounts. Diff that against a full snapshot and you report hundreds of unfollows that never happened.
Rule: never diff an incomplete snapshot. Compare the number of items you fetched with the profile's following count, and throw the snapshot away if they are too far apart:
function isComplete(items, expectedCount, tolerance = 0.02) {
if (expectedCount === 0) return items.length === 0;
return Math.abs(items.length - expectedCount) / expectedCount <= tolerance;
}
The small tolerance is there because counts and lists are not always updated at exactly the same moment.
4. Flapping from caches
Sometimes an account disappears from one fetch and comes back in the next, because of caching or eventual consistency on the server side. If you alert on the first sighting, users get "new follow" pings for accounts that were always there.
A simple fix is to confirm a change across two checks before you tell anyone:
// Only report an account once it has appeared in two snapshots in a row.
function confirmedAdds(addedNow, addedLastRun) {
const last = new Set(addedLastRun.map((a) => a.id));
return addedNow.filter((a) => last.has(a.id));
}
It costs you one poll interval of delay. In return, you send far fewer false alerts.
5. Lists that go private or hidden
A profile can switch to private, or the list can stop being available. A fetch that returns nothing is not the same as "they unfollowed everyone". Handle "can't read" as its own state, and don't run a diff on it.
6. Your timestamps are only as good as your poll interval
With snapshots, the only date you can attach to a follow is "somewhere between my last check and this one". Poll every hour and your resolution is one hour. Poll once a day and it's one day. Any tool that shows an exact time for an old follow is guessing, because Instagram doesn't share that data for other people's lists.
Snapshot diffing vs real order
| Read the order | Snapshot diffing | |
|---|---|---|
| Works on first search | Yes | No, needs a baseline |
| Finds follows from before you started | Yes | No |
| Survives an ordering change | No | Yes |
| Storage needed | None | Full list per check |
| Exact dates | No | Only "between check A and B" |
Snapshots are honest and robust. They are great for watching your own followers over months. They just can't answer the question most people actually type into Google: "who did this person follow recently?" A tool that starts counting today can't answer that.
Where Klupt fits
Klupt broke on 9 September like everyone else, and for a few weeks it also ran on snapshots. Since late October 2026 it shows a public profile's recent follows in real order again, newest first, from the first search. I won't go into how we read the list, but the hourly snapshot diff is still there on top of it: it powers the NEW badges and the Telegram alerts when a tracked profile follows someone.
If you want to see the result, the web tracker needs no login, and the iPhone app can also check private accounts you already follow, with the login kept on your device. A quick way to test any tracker, ours included: follow a new account from a second account, search that second account, and see if the follow shows up at the top.
Two rules we keep, and that I'd suggest to anyone building in this space:
- Public data only. If a tool asks for someone's Instagram password to "see" their list, walk away.
- Don't fake precision. Show order or "seen between A and B". Never invent dates.
Have you shipped something that depended on an undocumented API ordering and then broke? I'd love to hear how you found out, and what you replaced it with.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.