Twitter (X) Bulk Unfollow + Follower Remover — API Guide
People searching for a Twitter follower remover usually mean one of two things. Some want to unfollow a long list of accounts they no longer read. Others want to remove people from their own follower list. These are separate actions on X, and only the first one can be done with the twitterapi.io API.
This guide covers both honestly: the exact endpoint, body and price for bulk unfollowing, runnable Python that works at a human pace and keeps a log, the read endpoints that help you decide whom to unfollow, and where follower removal actually lives.
Two intents — clarify which you mean
Unfollow (your following list): you stop following an account. Your following count drops by one and their follower count drops by one. twitterapi.io supports this with POST /twitter/unfollow_user_v2.
Remove a follower (your follower list): an account follows you and you want that to stop. twitterapi.io does not offer this. There is no remove-follower endpoint, and there are no block or unblock endpoints either, so the block-then-unblock trick cannot be scripted through our API.
Where follower removal works: in the X app or on x.com, open the follower's profile or your followers list, open the menu next to the account and choose "Remove this follower".
The rest of this guide is about bulk unfollowing, with one section on follower removal near the end.
Runnable — log in once, then unfollow one account
The unfollow call acts as your X account, so it needs a login cookie. POST /twitter/user_login_v2 takes user_name, email, password and proxy (all required) plus an optional totp_secret, and returns login_cookies. The API reference strongly recommends enabling 2FA and passing the TOTP secret, because a cookie from an account without 2FA may not work for write actions. A cookie from this login only works with endpoints that carry the v2 suffix.
proxy is your own residential proxy in the form http://username:password@ip:port. Use the same value for login and for unfollow calls. Store the cookie and reuse it instead of logging in on every run.
user_id is the numeric ID, not the handle. GET /twitter/user/info?userName=... returns the profile under data, and data.id is the value to send.
A 200 response carries status (success or error) and message. Check status rather than assuming the unfollow happened.
import os, requests
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
PROXY = os.environ["RESIDENTIAL_PROXY"] # your own proxy: http://username:password@ip:port
def login() -> str:
"""Log in once and return login_cookies. Store it and reuse it."""
r = requests.post(
f"{BASE}/twitter/user_login_v2",
headers=HEADERS,
json={
"user_name": os.environ["X_USER_NAME"],
"email": os.environ["X_EMAIL"],
"password": os.environ["X_PASSWORD"],
"totp_secret": os.environ["X_TOTP_SECRET"], # optional, recommended
"proxy": PROXY,
},
timeout=120,
)
r.raise_for_status()
body = r.json()
if body.get("status") != "success":
raise RuntimeError(f"login failed: {body.get('message')}")
return body["login_cookies"]
def user_id_for(handle: str) -> str:
"""Resolve a handle to the numeric user ID the unfollow call needs."""
r = requests.get(
f"{BASE}/twitter/user/info",
headers=HEADERS,
params={"userName": handle},
timeout=30,
)
r.raise_for_status()
return r.json()["data"]["id"]
def unfollow(login_cookies: str, user_id: str) -> dict:
r = requests.post(
f"{BASE}/twitter/unfollow_user_v2",
headers=HEADERS,
json={"login_cookies": login_cookies, "user_id": user_id, "proxy": PROXY},
timeout=60,
)
r.raise_for_status()
return r.json() # {"status": "success" | "error", "message": "..."}
cookies = os.environ.get("X_LOGIN_COOKIES") or login()
target_id = user_id_for("some_account")
print(unfollow(cookies, target_id))
Decide whom to unfollow with read endpoints
A good cleanup starts with data, not with a blind loop. These read endpoints only need the X-API-Key header, no login cookie and no proxy.
Prices are list prices, where $1 buys 100,000 credits. Smaller follower and following pages cost more per account: 2 credits each for 100-199 per page and 3 credits each below 100, so ask for 200.
Inactive accounts: read the newest createdAt from GET /twitter/user/last_tweets. If the latest post is older than your cutoff, the account is a candidate.
Mutual follows: call GET /twitter/user/check_follow_relationship with your handle as source_user_name. If data.followed_by is true, the account follows you back, and you may want to keep it.
Pacing — keep it human
An unfollow sent through the API is still an action taken by your X account, and X's platform rules on automation apply to it. Run the cleanup the way a person would, only without the clicking.
Small daily batches: pick a modest number per day and spread a long list over days or weeks. The script below uses 20 per run. That is a conservative setting we chose for the example, not a limit published by X.
A few seconds between calls, with jitter: the script waits a random 4 to 9 seconds after each unfollow.
Do not mix follow and unfollow runs: following and unfollowing the same accounts in cycles is the pattern X's rules describe as follow churn. A cleanup is a one-way job.
Log every action: write the account ID, handle, time and API response to a local file. The log lets you resume the next day, audit what happened and re-follow an account you removed by mistake.
X does not publish exact numbers for how many unfollows an account can make in a day, so this guide does not quote one. For limits on the official API, see X's rate limits reference in the sources.
What a cleanup costs
The unfollow call itself costs $0.0006 (60 credits) per call. The reads around it are billed per item returned.
The login call is billed separately ($0.10 per login) and you only need it when you do not have a working cookie.
Removing a follower — not through this API
twitterapi.io has no endpoint that removes a follower from your account, and no block or unblock endpoint. Any guide that shows such a call against our API is wrong, and the call will fail.
In the X app or on x.com: open your followers list or the follower's profile, open the menu next to the account, and choose "Remove this follower". The account stops following you and can follow you again later unless you block it.
What our API can still do here: GET /twitter/user/followers lists your followers with profile data, so you can build the list of accounts you want to remove (for example accounts with no posts) and then work through it by hand in the app.
Follower removal in bulk is offered by some third-party tools through their own interface. Check how a tool gets access to your account before you connect it.
Alternative — the official X API
X's own API has an unfollow endpoint: DELETE /2/users/{source_user_id}/following/{target_user_id}, documented at docs.x.com/x-api/users/follows/introduction.
Auth is OAuth 2.0 user context with the follows.write scope, so the account owner has to authorize your app.
Access and rate limits depend on your X API plan. Read the current values at docs.x.com/x-api/fundamentals/rate-limits before you plan a bulk job.
It fits when you already run an app on X's developer platform and want X-issued tokens. twitterapi.io fits when you want an API key, a login cookie and pay-per-call pricing without an X developer app.
Comparison — 3 unfollow / removal paths
# Bulk unfollow, human-paced: list who you follow, keep mutuals, pick accounts
# that have not posted in a long time, unfollow a small batch, log every action.
import os, json, time, random, requests
from datetime import datetime, timezone, timedelta
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
PROXY = os.environ["RESIDENTIAL_PROXY"] # http://username:password@ip:port
LOGIN_COOKIES = os.environ["X_LOGIN_COOKIES"] # login_cookies from /twitter/user_login_v2
MY_HANDLE = os.environ["MY_HANDLE"]
DAILY_BATCH = 20 # your own choice; keep it small
INACTIVE_DAYS = 180
LOG_FILE = "unfollow_log.jsonl"
def get(path_url: str, params: dict) -> dict:
r = requests.get(path_url, headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
return r.json()
def list_following(handle: str) -> list:
users, cursor = [], ""
while True:
body = get(f"{BASE}/twitter/user/followings",
{"userName": handle, "pageSize": 200, "cursor": cursor})
users.extend(body.get("followings", []))
cursor = body.get("next_cursor")
if not body.get("has_next_page") or not cursor:
return users
def last_post_time(user_id: str):
body = get(f"{BASE}/twitter/user/last_tweets", {"userId": user_id})
payload = body.get("data") or {}
times = [
datetime.strptime(t["createdAt"], "%a %b %d %H:%M:%S %z %Y")
for t in payload.get("tweets", [])
if t.get("createdAt")
]
return max(times) if times else None
def follows_me(handle: str) -> bool:
body = get(f"{BASE}/twitter/user/check_follow_relationship",
{"source_user_name": MY_HANDLE, "target_user_name": handle})
return bool(body.get("data", {}).get("followed_by"))
def unfollow(user_id: str) -> dict:
r = requests.post(
f"{BASE}/twitter/unfollow_user_v2",
headers=HEADERS,
json={"login_cookies": LOGIN_COOKIES, "user_id": user_id, "proxy": PROXY},
timeout=60,
)
r.raise_for_status()
return r.json()
def already_done() -> set:
if not os.path.exists(LOG_FILE):
return set()
with open(LOG_FILE, encoding="utf-8") as f:
rows = [json.loads(line) for line in f if line.strip()]
return {row["user_id"] for row in rows if row["status"] == "success"}
def log(row: dict) -> None:
row["at"] = datetime.now(timezone.utc).isoformat()
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(json.dumps(row, ensure_ascii=False) + "\n")
cutoff = datetime.now(timezone.utc) - timedelta(days=INACTIVE_DAYS)
done = already_done()
following = list_following(MY_HANDLE)
print(f"following: {len(following)}")
unfollowed = 0
for user in reversed(following): # oldest follows first
if unfollowed >= DAILY_BATCH:
break
user_id, handle = user.get("id"), user.get("userName")
if not user_id or not handle or user_id in done:
continue
last = last_post_time(user_id)
if last is None or last > cutoff: # no readable posts, or still active: keep
continue
if follows_me(handle): # mutual follow: keep
continue
try:
result = unfollow(user_id)
status, msg = result.get("status"), result.get("message")
except requests.RequestException as exc:
status, msg = "error", str(exc)
log({"user_id": user_id, "handle": handle, "last_post": last.isoformat(),
"status": status, "msg": msg})
print(f"@{handle}: {status}")
if status == "success":
unfollowed += 1
time.sleep(random.uniform(4, 9)) # a few seconds, with jitter
print(f"unfollowed {unfollowed} this run; run again tomorrow for the next batch")
Questions readers ask
Which endpoint unfollows an account?
POST /twitter/unfollow_user_v2 on twitterapi.io. Send login_cookies, user_id and proxy in the JSON body and your key in the X-API-Key header. All three body fields are required.
Can I pass a handle instead of a user ID?
No. user_id is the numeric ID. Resolve a handle with GET /twitter/user/info?userName=... and read data.id, or take the id field from each entry returned by GET /twitter/user/followings.
Can I remove followers with the twitterapi.io API?
No. There is no remove-follower endpoint and no block or unblock endpoint. Remove a follower in the X app or on x.com: open the follower's profile or your followers list, open the menu and choose "Remove this follower". You can use GET /twitter/user/followers to build the list of accounts first.
Where do I get login_cookies?
From POST /twitter/user_login_v2. It takes user_name, email, password and proxy, plus an optional totp_secret, and returns login_cookies. The API reference recommends enabling 2FA and passing the TOTP secret. Store the cookie and reuse it; it only works with endpoints that have the v2 suffix.
What does a bulk unfollow cost?
Each unfollow call costs $0.0006 (60 credits), so 100 unfollows cost $0.06. Listing who you follow costs 1 credit per account at 200 per page, which is $0.01 per 1,000 accounts at $1 per 100,000 credits.
How many accounts can I unfollow per day?
X does not publish an exact daily number, so this guide does not quote one. Keep batches small, wait a few seconds between calls, spread a long list over several days and follow X's automation rules. The example script uses 20 per run as a conservative setting of our own.
Can I follow an account again after unfollowing it?
Yes. POST /twitter/follow_user_v2 takes the same body (login_cookies, user_id, proxy) and has the same price of $0.0006 per call. Keep a log of what you unfollow so you have the IDs.
Will the person I unfollow get notified?
X does not send a notification when you unfollow someone. They can still notice if they look at their follower list or use a tool that compares follower snapshots.
Continue
- Twitter (X) API — cluster hub
- Mass unfollow Twitter (X) API tutorial
- Twitter (X) follower tracking API guide
- Twitter (X) follower count API tutorial
- Twitter (X) automation guide
- twitterapi.io pricing
- 01How to Build an MCP Server in Python for Twitter (X) Data
- 02OpenClaw vs Claude Code: which agent should run your Twitter (X) jobs?
- 03Hermes Agent vs OpenClaw: Which Agent Fits Twitter (X) Work?
- 04Claude Code Channels: Telegram, Discord and Twitter (X) Alerts in Your Session
- 05Codex MCP: How to Add Any MCP Server to OpenAI Codex (CLI, IDE and App)
- 06Mass Delete Tweets — Bulk API Workflow + Free Tool Options
- 07Mass Unfollow on Twitter (X) — A Developer's API Tutorial
- 08Twitter (X) Follower Tracking via API — A Developer Guide
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API keyAlready building with TwitterAPI.io? Publish an honest tutorial or build log about it and earn up to $200 a month in API credits.