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_limit to see which quota is used. Use tcpdump/lsof/psutil to 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 to api.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:
  1. Confirm which quota is hit.
  2. Identify the live process causing the traffic.
  3. 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, recommended
curl -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: 1757667980
SEARCH: 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 python3
import psutil
import socket
target_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 python3
import psutil, socket, time
target_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.

  1. Get the process command line:



ps -p 27129 -o pid,ppid,command

2. In VS Code:

  • Cmd+Shift+PDeveloper: Show Running Extensions → examine running extensions and their PIDs.
  • Or run code --status In 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:

# Graceful
kill 27129
# If it doesn't go away, force kill
kill -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

  1. In VS Code: Cmd+Shift+PShow Running Extensions.
  2. 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 python3
import requests, os, time, json
from datetime import datetime
TOKEN = os.getenv("GITHUB_TOKEN") # set this in your env
HEADERS = {"Authorization": f"token {TOKEN}"} if TOKEN else {}
LOGFILE = "github_rate_log.jsonl"
THRESHOLD = 50 # alert when remaining < THRESHOLD
def 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)

Instrumenting a Java application with OpenTelemetry for distributed tracing and integrating with Elastic Observability

Reach out on LinkedIn for any questions or

Visit my website: https://rahulranjan.org


Discover more from Tech Insights & Blogs by Rahul Ranjan

Subscribe to get the latest posts sent to your email.

Leave a Reply

Trending

Discover more from Tech Insights & Blogs by Rahul Ranjan

Subscribe now to keep reading and get access to the full archive.

Continue reading