Exporting a Follower List: Two Modes, 18x Cost Difference
Exporting a Follower List: Two Modes, 18x Cost Difference
Most people writing a follower export discover the problem at around account number 50,000: the job has been running for an hour, it's nowhere near done, and the bill is larger than expected.
The cause is almost always the same — pulling full profiles when IDs would have done. The two modes differ by roughly 18x in bytes per follower, and most exports only need the cheap one.
This covers both modes, the cursor pagination that quietly drops rows if you get it wrong, and how to decide which you need before you start.
Both responses below came from live calls while writing this. You can check the shapes against a free demo endpoint without signing up.
The two modes
Full profile — everything about each follower
GET /v1/user/followers?username=naval&limit=3
Each row is a complete user object:
{
"id": "1234567890",
"username": "someone",
"display_name": "Someone",
"bio": "...",
"avatar": "https://pbs.twimg.com/...",
"followers_count": 412,
"following_count": 180,
"statuses_count": 2041,
"created_at": "Tue Jun 02 20:12:29 +0000 2009",
"location": "",
"url": "https://x.com/someone",
"blue_verified": false
}
Twelve fields per follower. Useful when you're scoring, segmenting or filtering — you can't filter on followers_count if you didn't fetch it.
ID-only — just the handle and the numeric ID
GET /v1/user/followers_ids?username=naval&limit=5
{
"data": [
{"id": "2119051387224879104", "username": "gabrielmazarin_"},
{"id": "2119052145832210433", "username": "tamim7zrg"},
{"id": "2119051805015384064", "username": "Wendyzik5"}
],
"meta": {"count": 5, "next_cursor": null, "pages_fetched": 1, "username": "naval"}
}
Two fields. That's the whole point.
The size difference is the decision. A full profile row runs well over a hundred bytes once bios and avatar URLs are included; an ID row is a handful. Across a million followers that gap is the difference between a job you run casually and one you schedule overnight.
Which one do you actually need?
Work backwards from what you'll do with the file:
| What you're doing | Mode | Why |
|---|---|---|
| Diffing today's list against yesterday's | IDs | You only need set membership |
| Finding who unfollowed | IDs | Same — it's a set difference |
| Feeding another system that resolves profiles itself | IDs | Don't pay twice |
| Scoring followers by size or activity | Full | You need the counts |
| Segmenting by bio keywords or location | Full | You need the text |
| One-off export of a small account (<5k) | Either | Too small for it to matter |
The common mistake is pulling full profiles for a diff. If you're tracking follower changes over time, you're comparing ID sets — the bios and avatars are dead weight on every run. There's more on the change-detection side in building a follower tracker.
Pagination: the part that silently loses rows
Both endpoints are cursor-paginated, and the cursor behaviour differs in a way worth noticing.
Look at the two meta blocks from the live calls above:
// followers (full profile)
"meta": {"count": 3, "next_cursor": "1881204516267795489"}
// followers_ids
"meta": {"count": 5, "next_cursor": null, "pages_fetched": 1, "username": "naval"}
The ID endpoint reports pages_fetched because it pages internally — one call can cover a lot of ground. The profile endpoint hands you a cursor per page and expects you to drive.
A loop that handles both:
import requests
API = "https://api.socialapi.tech/v1"
HEADERS = {"X-API-Key": "your_key"}
def export_followers(username, ids_only=True, page=200, cap=None):
path = "/user/followers_ids" if ids_only else "/user/followers"
out, cursor, seen = [], None, set()
while True:
params = {"username": username, "limit": page}
if cursor:
params["cursor"] = cursor
r = requests.get(f"{API}{path}", params=params, headers=HEADERS, timeout=60)
r.raise_for_status()
body = r.json()
batch = body.get("data") or []
if not batch: # no more rows
break
for row in batch: # dedupe across pages
if row["id"] not in seen:
seen.add(row["id"])
out.append(row)
if cap and len(out) >= cap:
break
cursor = (body.get("meta") or {}).get("next_cursor")
if not cursor: # no further pages
break
return out
Three things that matter here, and each one costs you rows if you skip it:
Break on both conditions. Empty batch and missing cursor. Checking only one gives you either an infinite loop or a truncated file, and a truncated file looks exactly like "this account has fewer followers than I thought."
Deduplicate by ID. Follower lists shift while you're paging through them — someone follows, someone leaves, and the window slides. The same account can appear on two consecutive pages. Without the seen set you'll have duplicate rows and a count that doesn't match the profile.
Don't trust the total to match. The followers_count on the profile is a cached number; your exported list is a live walk. A gap of a fraction of a percent on a large account is normal, not a bug in your loop.
If you're exporting several accounts rather than paging deep into one, batching the requests changes the arithmetic in a different direction.
Writing the file
For IDs, a flat CSV is enough:
import csv
rows = export_followers("naval", ids_only=True)
with open("followers.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["id", "username"])
for r in rows:
w.writerow([r["id"], r["username"]])
Keep the ID as text. These are 19-digit snowflake IDs, above JavaScript's safe integer range and above what a spreadsheet will preserve. Open that CSV in Excel and it will happily round the last few digits — the IDs will look fine and be wrong. If the file is going to a human, prefix with ' or write it as .tsv.
For full profiles, bio contains newlines and commas, so quote properly (the csv module does this) and decide upfront whether you're keeping the raw text or flattening it. Format tradeoffs for larger exports are covered in exporting X data to CSV.
What you can't export
Worth stating plainly, because the questions come up every time:
- Protected accounts' followers — not accessible to anyone who isn't approved. No tool changes this.
- Email addresses — never exposed by the platform. Any service claiming to attach emails to handles is matching against some other dataset, with the accuracy and legality that implies.
- A follower's complete history — you get their profile, not their timeline. That's a separate fetch per account, and for a large follower list the arithmetic rarely works.
- The exact order followers were gained — the list is returned newest-first but without reliable timestamps per follow.
Running it against a large account
Three practical notes for anything over a few hundred thousand:
Checkpoint the cursor. Write it to disk every page. If the job dies at 80%, you resume rather than restart — and on a large account, restarting is hours.
Expect the number to move. A list of two million is never a clean snapshot; it's a walk through a shifting set. If you need a consistent point-in-time view, take the diff between two complete walks rather than trying to freeze one.
Rate limits are per-account, not per-key. A provider spreading requests across a pool will go faster than one tied to a single credential. If an export is unexpectedly slow, that's usually why — and it's worth asking a vendor directly before committing.
Our API returns exactly the shapes in this article, so the loop above runs unchanged against it. Signing up with Google includes $0.20 in free credits, no card — enough to export a mid-sized account and see the real cost before deciding.
FAQ
How do I export my Twitter followers to CSV? Page through the followers endpoint with a cursor, collect the rows, and write them with a CSV writer. Use the ID-only mode unless you need profile fields — it's dramatically smaller for the same list.
What's the difference between followers and followers_ids? The first returns a full user object per follower (twelve fields including bio, counts and avatar); the second returns just the numeric ID and username. Pick by what you'll do with the file.
How much cheaper is the ID-only mode? Roughly an order of magnitude in bytes per follower, because you're not carrying bios, avatar URLs and counts. On small accounts it doesn't matter; past a few hundred thousand it decides whether the job is practical.
Can I export followers without an X developer account? Yes, through a third-party read API — you use that provider's key rather than X credentials. A developer account is for official API access, which you'd need for write operations.
How do I find out who unfollowed me? Export the ID list on a schedule and take the set difference between consecutive runs. You don't need full profiles for this, which is why the ID mode exists.
Why doesn't my export match the follower count on the profile? The profile count is cached and your export is a live walk through a list that changes as you read it. A small discrepancy on a large account is expected behaviour, not a bug.
Why do I see the same follower twice?
Because the list shifted between pages. Deduplicate by ID as you collect — a seen set costs nothing and removes the problem entirely.
How do I paginate the followers endpoint?
Pass the next_cursor from the previous response back as cursor, and stop when the batch is empty or the cursor is missing. Checking only one of those causes either a truncated file or an infinite loop.
What is pages_fetched in the response? The ID endpoint pages internally and reports how many underlying pages it covered for your call. It's informational — your loop still follows the cursor.
Can I export followers of an account that isn't mine? Public accounts, yes — the follower list is public information. Protected accounts, no, regardless of tooling.
Can I get followers' email addresses? No. Email is never exposed by the platform. Services claiming otherwise are matching handles against some other data source.
How long does it take to export a million followers? Depends entirely on page size and how many accounts the provider spreads requests over. Ask about concurrency rather than raw rate limits — that's what actually sets the wall-clock time.
Should I store the follower ID as a number or a string? String. They're 19-digit snowflake IDs that exceed JavaScript's safe integer range and get silently rounded by spreadsheets. Numeric storage corrupts them without any error.
Why does Excel mangle my follower IDs? It parses long digit strings as numbers and loses precision past about 15 digits. Write the column as text, prefix with an apostrophe, or hand over a TSV instead.
Can I resume a failed export? If you checkpoint the cursor to disk each page, yes — start the next run from the saved cursor. Without checkpointing you restart from zero, which on a large account is painful.
Does exporting followers violate X's terms? Reading public data is treated differently from automating actions with credentials or accessing protected content. The practical lines: no one else's credentials, nothing protected, no attack-like volume. What you do with the data afterwards carries its own obligations.
How often should I re-export for change tracking? Match it to how fast the account moves. Daily is enough for most; a fast-growing account may warrant more. Each run is a full walk, so the cost is per-run, not per-change.
Can I filter followers by follower count or bio while exporting? Only in full-profile mode, since filtering needs the fields. If you filter aggressively, pulling IDs first and then fetching profiles for the subset is usually cheaper.
What happens if the account is suspended mid-export? Subsequent pages fail rather than returning partial data. Handle the error explicitly and keep what you collected — don't let one failure discard the whole run.
Is the follower list ordered? Newest-first in practice, but without reliable per-follow timestamps. Don't build features that depend on exact ordering or on inferring when someone followed.
Can I export following as well as followers? Yes, via the parallel endpoint. Same cursor pagination, same two-mode tradeoff, same dedupe requirement.
Can I export followers from a private account I'm approved to follow? No. Approval lets you see the account in the app; it doesn't make its follower list available through an API. Protected means protected regardless of your relationship to it.
What happens if someone blocks me mid-export? Their row simply stops appearing in subsequent pages. You won't get an error — which is why reconciling against the previous run matters more than trusting a single export.
Should I export followers or following to find non-mutuals? Both, then take the set difference. Following minus followers gives accounts that don't follow back; the reverse gives accounts you haven't followed back.
How do I export followers for several accounts at once? Loop the accounts and keep one state file per account, each with its own cursor. Don't share a cursor between accounts — they index different lists and you'll truncate both.
The arithmetic in this article is the whole decision: pick the mode from what you'll do with the file, and the cost follows. Signing up with Google gets you $0.20 in credits and no card — enough to export a mid-sized account in both modes and see the difference on your own bill.