Trending GitHub repositories from the terminal
A Python CLI with no dependencies that lists the most-starred GitHub repositories created in the past day, week, month or year, using the Search API.
github-trending-cli is my solution to the GitHub Trending CLI project on roadmap.sh. It installs a trending-repos command that asks the GitHub API for popular new repositories and prints them in the terminal, ranked by stars:
$ trending-repos --duration month --limit 3
Top 3 trending repositories of the past month (created since 2026-09-11)
========================================================================
1. storytold/photocraft
Stars: 43,520 | Language: Rust
An open-source, clean-room reimplementation of Adobe Photoshop in pure Rust
https://github.com/storytold/photocraft
2. NandhaKishorM/laya
Stars: 32,209 | Language: Python
Non-autoregressive System 1 decision engine. Typed choice, score and yes/no decisions
over any text in a single forward pass, in 100+ languages, with a router that picks
the right checkpoint per request.
https://github.com/NandhaKishorM/laya
3. browser-use/jev-ultrafast
Stars: 22,576 | Language: Python
Fastest and cheapest web agent
https://github.com/browser-use/jev-ultrafast
Try it#
It needs Python 3.9 or newer and nothing else. Everything comes from the standard library:
git clone https://github.com/0xgitpushpray/github-trending-cli.git
cd github-trending-cli
pip install .
That puts trending-repos on your path (pipx install . works too, if you want it isolated). Then:
trending-repos # top 10 of the past week
trending-repos --duration day # top 10 of the past day
trending-repos --duration month --limit 20 # top 20 of the past month
--duration takes day, week, month or year and defaults to week. --limit takes 1 to 100 and defaults to 10.
What “trending” means here#
GitHub’s REST API has no trending endpoint. The trending page on the website is not available through the API, so the tool approximates it with the Search Repositories API: find repositories created inside the time window and sort them by stars.
DURATIONS = {"day": 1, "week": 7, "month": 30, "year": 365}
def build_url(since: date, limit: int) -> str:
params = {
"q": f"created:>={since.isoformat()}",
"sort": "stars",
"order": "desc",
"per_page": str(limit),
}
return f"{API_URL}?{urllib.parse.urlencode(params)}"
For --duration week on 11 October that becomes created:>=2026-10-04. One request returns everything, because --limit is passed straight through as per_page, and 100 is the largest page GitHub allows. That is also where the limit’s upper bound comes from.
This is not the same list as github.com/trending. That page ranks by stars gained recently, so a ten-year-old project having a good week can be on it. Here, only repositories that did not exist before the window are counted. For “what new projects are people paying attention to”, that is a reasonable answer, and it is the one the API can give.
Only the standard library#
The HTTP request uses urllib.request, argument parsing uses argparse, and wrapping long descriptions uses textwrap. The request sends the headers GitHub asks for, including a User-Agent, which the API requires:
headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": f"trending-repos/{__version__}",
}
if token:
headers["Authorization"] = f"Bearer {token}"
The token is optional. Without one, GitHub allows about 10 search requests a minute, which is plenty for checking trends by hand. If you hit the limit, set GITHUB_TOKEN to a personal access token (it needs no scopes) and the tool sends it.
Errors that tell you what to do#
Everything that can go wrong with the request becomes one GitHubAPIError with a plain message. The tool prints it to stderr and exits with code 1. The most likely one is the rate limit. GitHub returns a 403 or 429 and puts the reset time in a header as a Unix timestamp, so the message turns it into a time you can read:
Error: GitHub API rate limit exceeded. Try again after 14:05:12 UTC. Set the GITHUB_TOKEN environment variable to raise the limit.
A bad token gets its own 401 message, GitHub outages (5xx) say to try again later, and timeouts, DNS failures, a response that is not JSON, and JSON without an items list each have a message too. Bad input is caught by argparse before any request is made, and exits with code 2:
$ trending-repos --limit 0
trending-repos: error: argument --limit: must be between 1 and 100, got 0
--duration is lowercased before it is checked, so --duration MONTH works.
Emoji on Windows#
Repository descriptions are full of emoji. Printing them on a Windows console that uses an older code page raises UnicodeEncodeError and kills the program in the middle of the list. The first thing main() does is tell stdout and stderr to replace characters they cannot encode, instead of crashing:
for stream in (sys.stdout, sys.stderr):
if hasattr(stream, "reconfigure"):
stream.reconfigure(errors="replace")
The worst case is now a ? where an emoji would have been.
Tests#
The 18 tests use unittest and mock urllib.request.urlopen, so they run offline and do not use up any rate limit. They check the date window for each duration, the query string, sorting and the fallbacks for missing descriptions and languages, that the token is sent only when it is set, and every error path: rate limit, 5xx, 422, network failure, timeout, invalid JSON and a missing items list.
PYTHONPATH=src python -m unittest discover -s tests -v