If your GitHub Core API quota suddenly hits zero, but search and graphql remain untouched, something on your machine or environment is hammering api.github.com REST endpoints. I ran into this myself — core usage skyrocketed (e.g. CORE: 5533/5000 used (Remaining: 0)) — and this article walks you through the full investigative workflow, the exact commands/scripts I used, how I found the process, and how I fixed the problem.
- Problem: Core REST API quota exhausted while other quotas are fine → local script/extension is spamming REST endpoints.
- Quick diagnosis:
curl https://api.github.com/rate_limitto see which quota is used. Usetcpdump/lsof/psutilto map traffic to processes. - Common culprit: VS Code background extension processes (e.g. Copilot, GitLens, GitHub Pull Requests) exposing
Code Helper (Plugin)processes that talk toapi.github.com. - Fix: identify offending extension, disable or authenticate it (set PAT), or stop the process. Use a monitoring script to catch future offenders.
Why this matters (use case)
- CI/CD systems, local dev tools, IDE extensions, or helper scripts that poll GitHub can eat your per-hour limit unexpectedly. When Core (REST) is exhausted, many GitHub features break (git operations via API, PR fetches, status checks). You need a fast way to:
- Confirm which quota is hit.
- Identify the live process causing the traffic.
- Stop it and prevent recurrence.
1. Confirm the rate limit status
Run:
# If you have a Personal Access Token, use it; otherwise you'll get anonymous (limited) view.
export GITHUB_TOKEN="ghp_xxx" # OPTIONAL, recommendedcurl -sS -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq .
Look at the resources block:
resources.core→ REST endpoints (most common)resources.search→ /search endpoints (separate small quota)resources.graphql→ GraphQL quota
If core.remaining is 0 (or usage is very high) but search/graphql are fine → someone is calling regular REST endpoints.
📊 Current GitHub Rate Limits
CORE: 8490/5000 used (Remaining: 0) | Reset: 1757667980SEARCH: 0/30 used (Remaining: 30)GRAPHQL: 0/5000 used (Remaining: 5000)
2. Watch the network activity (macOS example)
If you can see traffic to GitHub in tcpdump, that confirms network activity but not the PID:
# Run while reproducing activity (requires sudo)
sudo tcpdump -n host api.github.com
Example tcpdump output shows a connection to 20.207.73.85:443 (api.github.com IP). That proves network traffic but not the process.
3 Map TCP connection → process
A. Use lsof (macOS)
While traffic is happening, run:
# Show processes with active TCP connections
sudo lsof -nPiTCP -sTCP:ESTABLISHED | grep api.github.com
# or if DNS doesn't resolve in lsof, grep the IP you saw from tcpdump
sudo lsof -nPiTCP -sTCP:ESTABLISHED | grep 20.207.73.85
You’ll get lines like:
Code 27129 rahul 45u IPv6 0x... TCP 192.168.68.110:58274->20.207.73.85:443 (ESTABLISHED)
This shows Code (VS Code) and PID 27129.
B. psutil script (cross-platform, python)
I used psutil to reliably map connections to PIDs.
Script: find_github_culprit.py
#!/usr/bin/env python3import psutilimport sockettarget_host = "api.github.com"target_ips = set(socket.gethostbyname_ex(target_host)[2])print(f"Looking for processes connecting to {target_host} ({', '.join(target_ips)})...\n")for proc in psutil.process_iter(['pid', 'name']): try: for conn in proc.net_connections(kind='inet'): if conn.raddr and conn.raddr.ip in target_ips and conn.status == psutil.CONN_ESTABLISHED: print(f"PID {proc.info['pid']:>6} | {proc.info['name']:<25} -> {conn.raddr.ip}:{conn.raddr.port}") except (psutil.AccessDenied, psutil.NoSuchProcess): continue
Run:
python find_github_culprit.py
Output:
PID 27129 | Code Helper (Plugin) -> 20.207.73.85:443
This tells us the process name and PID.
4 Continuous monitor to catch intermittent culprits
If the offending process only spikes occasionally, use a small daemon that prints new connections (I use 5s polling):
Script: ghtest_monitor.py
#!/usr/bin/env python3import psutil, socket, timetarget_host = "api.github.com"target_ips = set(socket.gethostbyname_ex(target_host)[2])seen = set()print(f"Monitoring for processes connecting to {target_host} ({', '.join(target_ips)})...\n")while True: current = set() for proc in psutil.process_iter(['pid', 'name']): try: for conn in proc.net_connections(kind='inet'): if conn.raddr and conn.raddr.ip in target_ips: entry = (proc.info['pid'], proc.info['name'], conn.raddr.ip, conn.raddr.port) current.add(entry) if entry not in seen: print(f"[NEW] PID {entry[0]:>6} | {entry[1]:<25} -> {entry[2]}:{entry[3]}") except (psutil.AccessDenied, psutil.NoSuchProcess): continue seen = current time.sleep(5)
Run it in a terminal and leave it running while you continue other work. It will print only new connections.
Monitoring for processes connecting to api.github.com (20.207.73.85)...[NEW] PID 27087 | Code Helper -> 20.207.73.85:443
5 Determine what the process actually is
If the process name is Code Helper, it’s a VS Code helper/extension host — common suspects: GitHub Copilot, GitLens, GitHub Pull Requests and Issues, Settings Sync, or any GitHub-integrated extensions.
- Get the process command line:
ps -p 27129 -o pid,ppid,command
2. In VS Code:
Cmd+Shift+P→ Developer: Show Running Extensions → examine running extensions and their PIDs.- Or run
code --statusIn a terminal, it prints the status, including Extension Host PIDs and names.
3. You can also list open files/network for the PID:
sudo lsof -p 27129 -nP
6 Stop it immediately first
To immediately stop the traffic:
# Gracefulkill 27129# If it doesn't go away, force killkill -9 27129
Then re-run the ghtest_monitor.py to confirm there are no new api.github.com connections.
7 Find & disable the offending extension in VS Code
- In VS Code:
Cmd+Shift+P→ Show Running Extensions. - Sort/scan extensions that interact with GitHub:
- GitHub Copilot
- GitLens
- GitHub Pull Requests and Issues
- GitHub Repositories / Authentication extensions
3. Disable suspect extension(s) and test again.
4. If disabling an extension stops the API traffic, you’ve found the culprit — either keep it disabled or change its settings.
8 Root cause & long-term fixes
Why extensions spike the API
- Polling for PR/issue status
- Background indexing or telemetry
- Auto-sync or auth token refresh flows
- Anonymous requests (no PAT) can be limited and may cause multiple re-tries
Remediations
- Add an authenticated GitHub token (PAT) in VS Code settings for GitHub extensions so requests are authenticated (higher limit).
- In VS Code, sign in to GitHub (the extension will store credentials securely).
- Adjust extension polling intervals (if the extension has a setting).
- Disable extensions you don’t actively use.
- Use per-project tokens or service tokens for CI rather than global ones.
- Rotate tokens if you suspect they’ve been leaked or abused.
Full example: Rate-limit monitor + log (complete script)
- polls
rate_limit - logs JSON to a file
- prints if the core remaining is below the threshold
Script: github_rate_monitor.py
#!/usr/bin/env python3import requests, os, time, jsonfrom datetime import datetimeTOKEN = os.getenv("GITHUB_TOKEN") # set this in your envHEADERS = {"Authorization": f"token {TOKEN}"} if TOKEN else {}LOGFILE = "github_rate_log.jsonl"THRESHOLD = 50 # alert when remaining < THRESHOLDdef get_rate(): r = requests.get("https://api.github.com/rate_limit", headers=HEADERS, timeout=10) r.raise_for_status() return r.json()def log_rate(rate): entry = {"ts": datetime.utcnow().isoformat()+"Z", "rate": rate} with open(LOGFILE, "a") as f: f.write(json.dumps(entry)+"\n")if __name__ == "__main__": print("Starting GitHub rate monitor... (ctrl+c to stop)") try: while True: rate = get_rate() core = rate['resources']['core'] remaining = core['remaining'] reset = datetime.utcfromtimestamp(core['reset']).isoformat() + "Z" print(f"[{datetime.utcnow().isoformat()}] CORE remaining: {remaining} (reset: {reset})") log_rate(rate) if remaining < THRESHOLD: print("WARNING: core remaining below threshold:", remaining) time.sleep(60) except KeyboardInterrupt: print("Stopped.")
Appendix :Useful commands summary (macOS / Linux)
Show rate limit:
curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq
Capture raw traffic (requires sudo):
sudo tcpdump -n host api.github.com
Map IP→process:
# If you have IP from tcpdump (e.g. 20.207.73.85)sudo lsof -nPiTCP -sTCP:ESTABLISHED | grep 20.207.73.85
See process command:
ps -p <PID> -o pid,ppid,command
Check VS Code running extensions / PIDs:
Cmd+Shift+P → Developer: Show Running Extensions
Terminal: code --status
Kill a process:
kill <PID>kill -9 <PID> # force kill
Conclusion
A spike in core Usage almost always means a local poller or extension is hitting REST endpoints. The combination of tcpdump (confirm traffic), lsof/psutil (map to PID), and targeted VS Code checks will get you from unknown spike → offending extension in minutes. The scripts above are battle-tested and designed to be safe and easily extensible. Run the monitor, identify the extension, and then decide whether to authenticate, change polling settings, or disable it.
Related Articles:
OpenTelemetry with Elastic Observability
Elastic RUM (Real User Monitoring) with Open Telemetry (OTel)
OpenTelemetry: Automatic vs. Manual Instrumentation — Which One Should You Use?
Configuration of the Elastic Distribution of OpenTelemetry Collector (EDOT)
Reach out on LinkedIn for any questions or
Visit my website: https://rahulranjan.org





Leave a Reply