Mass Unfollow on Twitter (X) — A Developer's API Tutorial
Mass-unfollow workflows on Twitter (X) come up for several legitimate reasons — pruning a follower list down to a curated set, removing inactive accounts after years of following, cleaning up post-acquisition account migrations. The 'mass' in 'mass unfollow' just means programmatic + batched; the actual per-account unfollow is a single API call per target.
This guide walks the API workflow with runnable Python, the rate-limit + safe-pacing patterns, and the practical operational issues (followers-list iteration, dedup, dry-run mode). Note: twitterapi.io is read-only and does not offer write endpoints; mass-unfollow is X official path only.
Why this needs the official X API
Unfollowing requires write access against the account owner's identity. The call acts AS the owner — you can only unfollow accounts that you (the authenticated user) follow.
twitterapi.io is a read-only third-party path; it doesn't offer write endpoints by design. For any mass-unfollow workflow you'll use X official with OAuth user-context auth.
Pricing per docs.x.com/x-api/getting-started/pricing: $0.010 per write request (including unfollow). Cost for unfollowing 1,000 accounts: 1,000 × $0.010 = $10. Math derived directly from the cited rate.
Step 1 — list the accounts you currently follow
Before unfollowing, get the current following list. X official: /2/users/{id}/following, returns followers up to 1,000 per page with cursor pagination.
Pricing per docs.x.com pricing: $0.010 per follower-list read (counts as a read of following). For a 5,000-follow list, that's 5 paginated calls × $0.010 = $0.05.
# pip install tweepy
import tweepy
client = tweepy.Client(
consumer_key="YOUR_KEY",
consumer_secret="YOUR_SECRET",
access_token="USER_TOKEN",
access_token_secret="USER_TOKEN_SECRET",
)
def get_following(user_id: str) -> list:
"""Page through the entire following list."""
all_following = []
for page in tweepy.Paginator(
client.get_users_following,
id=user_id,
max_results=1000,
):
all_following.extend(page.data or [])
return all_following
following = get_following("YOUR_USER_ID")
print(f"Currently following {len(following)} accounts")
Step 2 — filter to the unfollow targets
Most mass-unfollow workflows filter by some criterion before issuing the deletes:
- Inactive accounts — fetch each followed account's created_at and last-tweet timestamp; unfollow if no posts in N days
- No engagement back — find accounts you follow that don't follow you back (asymmetric relationship)
- Below threshold — accounts under N followers, accounts that posted in a banned topic, accounts on a blocklist
- Explicit list — you have a CSV of usernames to drop, just resolve each to user_id
Build the target list client-side from the previous step's result + your filter criterion. This is also a good place to add a dry-run mode that prints what would be unfollowed without actually issuing the deletes.
Step 3 — execute the unfollows with rate-limit safety
X publishes rate limits in its docs (separate from per-call price). The unfollow endpoint has its own rate limit; consult docs.x.com for current numbers.
Safe pacing: 1 unfollow per second is a conservative baseline that fits comfortably under most rate limits. Faster bursting risks 429 and (rarely) account-level review.
Backoff on 429: when the API returns 429 Too Many Requests, sleep for the Retry-After header value plus jitter (1-5 seconds random). Don't retry tightly.
Don't unfollow during normal X UI activity: if you're using your account normally at the same time as the script, the combined activity can trip limits faster. Run the script during quiet hours.
import time, random
DRY_RUN = False # flip to False when ready
def mass_unfollow(targets: list, my_user_id: str):
success, fail = [], []
for target_id in targets:
if DRY_RUN:
print(f"[dry-run] would unfollow {target_id}")
continue
try:
client.unfollow_user(target_user_id=target_id)
success.append(target_id)
except tweepy.TooManyRequests:
print("429 — sleeping 60s + jitter")
time.sleep(60 + random.uniform(0, 5))
except Exception as e:
fail.append((target_id, str(e)))
time.sleep(1.0 + random.uniform(0.1, 0.5)) # safe pacing: ~1 unfollow/sec
return success, fail
# success, fail = mass_unfollow(target_ids, "YOUR_USER_ID")
# print(f"unfollowed {len(success)}, errors {len(fail)}")
Side-by-side comparison — 3 paths to mass unfollow
Same job (unfollow N accounts) framed across three options. Costs derived from cited pricing.
Two patterns: (a) the API path is the only programmatic option — twitterapi.io being read-only means dashboard tools or the X official path are your two choices; (b) at any meaningful volume (100+ accounts), the programmatic path is materially faster + cheaper than UI clicking.
Operational notes — suspension risk + reversibility
Suspension risk: very aggressive unfollow patterns can trigger X's anti-spam systems. Don't unfollow >1,000 accounts in a single hour; spread across multiple days for large lists. The risk is low for small workflows; non-zero for very-large ones.
Reversibility: an unfollow is reversible — you can refollow the account if you change your mind. There's no soft-delete window; the unfollow is immediate.
Audit trail: log every unfollow with timestamp + reason (filter criterion). Useful if you need to refollow later or audit the workflow.
Communication: if you're cleaning up a brand account's following list, consider sending a one-time tweet explaining the cleanup — some followers feel slighted by silent unfollows and the explanation softens that.
Cost framing — 100 / 1,000 / 10,000 unfollows
Math from docs.x.com/x-api/getting-started/pricing at $0.010 per write request:
- 100 unfollows = $1.00 — single-script run, no concern
- 1,000 unfollows = $10.00 — split across 2-3 days for safety
- 10,000 unfollows = $100.00 — split across 7-14 days, monitor for 429s and account warnings
Plus the cost of reading the current following list to find targets — at 1,000 entries per page × $0.010 = $0.01 per 1,000 followers listed. For a 10,000-follow account that's $0.10 to list, $100 to mass-unfollow.
# Practical example: end-to-end mass unfollow with dry-run, safe pacing, audit log.
import os, time, random, json
from datetime import datetime, timezone
import tweepy
client = tweepy.Client(
consumer_key=os.environ["X_CONSUMER_KEY"],
consumer_secret=os.environ["X_CONSUMER_SECRET"],
access_token=os.environ["X_USER_TOKEN"],
access_token_secret=os.environ["X_USER_SECRET"],
)
MY_USER_ID = os.environ["X_MY_USER_ID"]
DRY_RUN = True # start safe
def get_following():
return [u for page in tweepy.Paginator(
client.get_users_following, id=MY_USER_ID, max_results=1000)
for u in (page.data or [])]
def filter_targets(following):
# Replace with your real filter — example: inactive (none tweeted in 90d)
return [u for u in following if False] # placeholder
def mass_unfollow(targets):
log_path = f"unfollow_log_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.jsonl"
success, fail = 0, []
with open(log_path, "a") as log:
for u in targets:
entry = {"target_id": u.id, "username": u.username, "at": datetime.now(timezone.utc).isoformat()}
if DRY_RUN:
entry["action"] = "dry_run"
else:
try:
client.unfollow_user(target_user_id=u.id)
entry["action"] = "unfollowed"
success += 1
except tweepy.TooManyRequests:
entry["action"] = "rate_limited"
log.write(json.dumps(entry) + "\n")
time.sleep(60 + random.uniform(0, 5))
continue
except Exception as e:
entry["action"] = f"error: {e}"
fail.append(u)
log.write(json.dumps(entry) + "\n")
time.sleep(1.0 + random.uniform(0.1, 0.5)) # safe pacing
return success, fail, log_path
following = get_following()
print(f"following: {len(following)}")
targets = filter_targets(following)
print(f"would unfollow: {len(targets)}")
if targets:
s, f, p = mass_unfollow(targets)
print(f"done: {s} succeeded, {len(f)} errors. Log: {p}")
# Cost framing (math from cited pricing):
# 1,000 unfollow targets = 1,000 × $0.010 = $10.00 (write)
# + ~1 list page per 1,000 follows for the target-selection step = $0.01
# Total = $10.01 to mass-unfollow 1,000 accounts.Questions readers ask
Can I mass-unfollow via twitterapi.io?
No — twitterapi.io is a read-only third-party API. Write operations (unfollow, like, retweet, post create, etc.) require the X official surface with user-context auth. For mass unfollow, use X official.
How fast can I unfollow without getting suspended?
Conservative baseline is 1 unfollow per second, spread across hours not bursted. Most operational practice keeps under 1,000 unfollows per hour. The suspension risk is non-zero at very-high volume — split very-large lists across days.
Will the people I unfollow be notified?
No — X does not send notifications for unfollows. Some third-party tools surface 'unfollower notifications' to subscribers, so people running those tools may see; X itself does not notify.
Can I batch multiple unfollows in one API call?
No — the X unfollow endpoint is per-target. You issue N requests for N unfollows. Use sensible pacing + retry logic; don't try to parallel-burst many concurrent requests.
What if I unfollow an account I should have kept?
Just refollow. The unfollow is reversible. There's no soft-delete window; you can refollow immediately.
Are there X-rate limits on the read side (following-list pagination)?
Yes — /2/users/{id}/following has its own rate limit documented at docs.x.com. For a 10K-follow account that's 10 page reads, well under most rate-limit windows; no issue at typical mass-unfollow volumes.
Continue
- Twitter (X) API — cluster hub
- Twitter (X) follower tracking API guide
- Twitter (X) counter API guide
- Twitter (X) API in Python — complete guide
- twitterapi.io pricing
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API key