How to Auto-Unfollow GitHub Users Who Don’t Follow You Back (Using gh)

If you follow many GitHub accounts, it can be hard to see who still follows you back. Checking profiles one by one is slow. A better way is to use the GitHub API (through the GitHub CLI, gh) to compare two lists:

Anyone who is in Following but not in Followers is a “not following back” account.

This guide shows a clean method to find them and unfollow them automatically.


Why use the GitHub CLI + API (instead of scraping)

You could scrape GitHub HTML pages, but it’s not the best choice because:

Using the GitHub API is better because:


What you need (one time setup)

1) Install GitHub CLI

On macOS (Homebrew):

brew install gh

2) Login with gh

gh auth login

3) Make sure you have the right permission to unfollow

Unfollowing needs extra permission. If you see an error about scope, run:

gh auth refresh -h github.com -s user

Step 1 — Get the list of “not following back” users

This command prints usernames you follow who do not follow you back:

comm -23 \
  <(gh api --paginate /user/following --jq '.[].login' | sort) \
  <(gh api --paginate /user/followers  --jq '.[].login' | sort)

What it does (simple explanation)


Before unfollowing, save the list and review it:

comm -23 \
  <(gh api --paginate /user/following --jq '.[].login' | sort) \
  <(gh api --paginate /user/followers  --jq '.[].login' | sort) \
  > not-following-back.txt

cat not-following-back.txt

If the list looks correct, continue.


Step 3 — Unfollow them automatically (safe bulk version)

This script unfollows each username and prints OK or FAILED:

export GH_PAGER=cat
set +e 2>/dev/null
setopt NO_ERREXIT 2>/dev/null

while IFS= read -r u; do
  [[ -z "$u" ]] && continue
  printf "Unfollowing %s ... " "$u"

  gh api -X DELETE "/user/following/$u" --silent </dev/null
  rc=$?

  if [[ $rc -eq 0 ]]; then
    echo "OK"
  else
    echo "FAILED (exit $rc)"
  fi

  sleep 0.5
done < not-following-back.txt

Why some “simple scripts” fail after the first unfollow

Many people try something like this:

while read -r u; do
  gh api -X DELETE "/user/following/$u"
done < not-following-back.txt

Sometimes it unfollows only one user. Common reasons:

1) The shell stops on the first error

Some shells (or shell configs) stop a script when a command fails (this is called “exit on error”).
If one unfollow request returns a non-zero exit code (for example: temporary rate limiting), the loop can stop early.

The safer script turns that behavior off and handles errors manually.

2) GitHub blocks actions done too fast (“secondary rate limit”)

If you unfollow many users very quickly, GitHub may slow you down or block some requests temporarily. That’s why the script includes:

sleep 0.5

3) gh can behave differently with interactive paging

gh sometimes uses a pager (like less). In scripts, that can cause weird behavior. This is why we use:

export GH_PAGER=cat

4) Some tools can accidentally read from stdin

In loops that read from a file, a command inside the loop may also read stdin and “steal” input. Redirecting stdin to /dev/null for that command prevents that:

... </dev/null

Not every system needs this, but it’s a good safety habit.


One-block “do everything” command (no manual file step)

This version fetches followers + following, compares them, and unfollows in one run:

export GH_PAGER=cat
set +e 2>/dev/null
setopt NO_ERREXIT 2>/dev/null

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

gh api --paginate /user/following --jq '.[].login' | sort > "$tmpdir/following.txt"
gh api --paginate /user/followers  --jq '.[].login' | sort > "$tmpdir/followers.txt"

comm -23 "$tmpdir/following.txt" "$tmpdir/followers.txt" \
| while IFS= read -r u; do
    [[ -z "$u" ]] && continue
    printf "Unfollowing %s ... " "$u"
    gh api -X DELETE "/user/following/$u" --silent </dev/null
    rc=$?
    [[ $rc -eq 0 ]] && echo "OK" || echo "FAILED (exit $rc)"
    sleep 0.5
  done

Safety tips