README
¶
twitter-follower-rank
A simple Go program to print a Twitter User's list of followers, sorted by number of followers.
[!CAUTION] This does not use or require API access. Please read disclaimer(s) section first.

[!IMPORTANT]
Pros
- No $$$ API key
- It just works
Cons
- Come up with some way to scroll down. I did it by hand.
- After ~2k followers I got rate-limited with a 429 response. Go make some tea and come back.
Usage
# Get repo && cd
git clone https://github.com/5HT2/twitter-follower-rank && cd twitter-follower-rank
# Build
make
# Once you have a data.json, run:
./twitter-follower-rank
# That's it!
Usage of ./twitter-follower-rank:
-f string
Data file to read (default "data.json" or matching "data-[0-9]{4}-[0-9]{2}-[0-9]{2}(-following)?.json")
-following
Invert mutuals detection mode to the following tab instead of the followers tab
-maxFollowers int
Filter to <= this many followers
-maxFollowing int
Filter to <= this many following
-minFollowers int
Filter to >= this many followers
-minFollowing int
Filter to >= this many following
-ratio
Only display followers with a following:follower ratio of >= -ratioBuf
-ratioBuf float
Buffer for ranked. e.g. If set to 0.9, it will display if (followers / following >= 0.9) (default 0.9)
Auto select data.json file
An explanation of how automatically selecting a data file works:
The dates given here are examples, to show how the sort order works. It will use the first valid file that it finds.
- The flag
-fis always prioritized as first, and is only ignored if the file cannot be read / does not exist. - The
data.jsonvalue is always last as a final fallback. - The rest of the files are found from the existing files in the current working directory.
By default, the data file will by selected in this order:
my-custom-file.json (optionally set by -f)
data-2024-04-20.json
data-2024-03-01.json
data-2023-11-30.json
data.json
If you have the -following flag set it would instead look like this:
my-custom-file.json (optionally set by -f)
data-2024-04-20-following.json
data-2024-03-01-following.json
data.json
What is a data.json
The way that this works is by leveraging Twitter's own Followers tab, and simply grabbing an object of all the requests.
In the future, I'll probably add a way to also do this via Twitter's GDPR data request, assuming it includes enough info to not have to scrape.
[!TIP] TLDR: The
data.jsonis created from the requests that your browser sends when loading a/followersor/followingtab for a user profile.
How to get your very own data.json
- To do so, open the Chrome Dev Tools with Ctrl Shift I, go to the network tab.
- In the network tab, search the text
Followers?. This will filter it to only the requests we want. - Now, open the followers menu on Twitter, or visit https://[domain]/username/followers.
- Scroll all the way to the bottom, just keep in mind that going too fast will rate-limit you. If you hit the limit, just wait 30min - 1h.
- Once at the bottom, open a new Dev Tools window for your existing Dev Tools window with Ctrl Shift I (yes, debug inception).
- In the new Dev Tools window, open the console tab (taken from StackOverflow^1), works on Chrome 118 or newer) and run the following:
[!IMPORTANT] If you encounter an error complaining about
r.contentData(), use the newer ≥ 118 version.[^3]
Chrome / Edge ≥ 118
let followers = await (async () => {
const getContent = r => r.url() && !r.url().startsWith('data:') && r.contentData();
const nodes = UI.panels.network.networkLogView.dataGrid.rootNode().flatChildren();
const requests = nodes.map(n => n.request());
const contents = await Promise.all(requests.map(getContent));
return contents.map((data, i) => {
const r = requests[i];
const url = r.url();
const body = data?.content;
const content = !data ? url :
r.contentType().isTextType() ? data :
typeof body !== 'string' ? body :
`data:${r.mimeType}${data.encoded ? ';base64' : ''},${body}`;
return { url, content };
});
})();
Chrome / Edge ≥ 111 ≤ 117
let followers = await (async () => {
const getContent = r => r.url() && !r.url().startsWith('data:') && r.contentData();
const nodes = UI.panels.network.networkLogView.dataGrid.rootNode().flatChildren();
const requests = nodes.map(n => n.request());
const contents = await Promise.all(requests.map(getContent));
return contents.map((data, i) => {
const r = requests[i];
const url = r.url();
const body = data?.content;
const content = !data ? url :
r.contentType().isTextType() ? data :
typeof body !== 'string' ? body :
`data:${r.mimeType}${data.encoded ? ';base64' : ''},${body}`;
return { url, content };
});
})();
8. Congrats! Now you can create a file called `data.json` inside this project, and paste the JSON object into it.
Using the data.json
[!TIP] The program itself handles all the parsing and such from that point. Please see the usage section for a list of program flags, the simplest arguments are as follows:
# You can even omit the -f flag, as it will auto-select the file for you. ./twitter-follower-rank -f data.json
Manual patch workaround
[!TIP] The patch is currently unnecessary!!
As of fbf4d30 it is automagically supported.
[!NOTE] Twitter attempted to patch this (multiple times).
- As of 2024/03, Twitter switched
followers[n].content.content→followers[n].content.#g- As of 2024/04, Twitter switched
followers[n].content.#g→followers[n].content.#e- As of 2024/12, Twitter switched
followers[n].content.#e→followers[n].content
Patch workaround (currently unnecessary)
This is annoying, as it means the Chrome Dev tools don't include the final .content when doing "Copy Object", but there is a workaround.
There's a fix-chrome-private-field.applescript script which you can run with osascript fix-chrome-private-field.applescript [number of items].
The script essentially just types out followers[n].content.content = followers[n].content.#e incrementally and increases n. You can't do this in a for loop in Chrome for some reason, as it will give you a private field access error.
If you'd like a Linux version of the script, or know how to get around Chrome not allowing accessing private fields in a for loop, feel free to open an issue or message me (my contact info is listed on my profile).
Disclaimer(s)
- I have no idea if Twitter will care if you scroll down to the bottom of the followers tab, I'd advise you to tread with caution though.
- This code was incredibly hastily written, it's kind of awful, easy to improve but it works, I don't care enough to bother. It does what I need it to.
- This might break if the response format changes. Unlikely, but feel free to make a Github issue about it.
- This data appears to be kind of cached on the server, if someone changes their username the old one is still sent in a response (multiple weeks after the username change). There's no error checking for empty profiles either. For the most part, it's functional, with the rare 1 or 2 duplicate accounts, the follower counts might also be a lil outdated, this is just Twitter's caching / optimization stuff on their end.
- Idk this is really janky, if it breaks or there's something you want me to fix, feel free to ask lol
[^3]: Chromium third_party/devtools-frontend API change for requestData()
Documentation
¶
There is no documentation for this package.