GitHub never shipped an endpoint for its Trending list, so apps that want it end up parsing HTML. This repository skips that chore: it is a single ES module that calls the GitHub Trending Scraper through the official apify-client package and hands your code the list as plain objects. Each object carries the repository's rank, its owner/name, URL, tagline, language, forks and two star counts, one for the selected period and one for all time. The scraping happens on Apify's infrastructure, which means your Node service, serverless function or bot only starts a run and reads the result. Consider it a Trending endpoint you never had to sell your soul to GitHub for. Rows are also saved to results.json and results.csv when you run the script directly.
Actor: GitHub Trending Scraper Β· Publisher: Devil Scrapes Β· Also in Python: github-trending-scraper-python
- One row for every repository on the list, with its position, the
owner/namepair split into separate columns, the link, the tagline, the primary language, star and fork counts and the contributor avatars GitHub shows on the card. - Star velocity as its own number:
stars_in_windowcounts only the stars earned during the chosen period, kept apart from the lifetimestars_total, so you never diff two snapshots to estimate momentum. - Three periods, two filters: switch
sincebetweendaily,weeklyandmonthly, and narrow by programming language, spoken language or both, all producing the same row shape. - GitHub's ordering survives export: the
rankcolumn keeps position 1 at position 1 after the rows pass through CSV, a spreadsheet or a database sort. - No GitHub token or rate budget of your own: requests go out from the Apify run, and throttled or failed page loads are retried with backoff before anything is written.
- Every row is stamped with the window it came from and a UTC collection time, which makes stacking scheduled snapshots into a history table straightforward.
Schedule node index.js in GitHub Actions or a cron container, take the top five rows and send them to an incoming webhook. Each message can link html_url and show stars_in_window, which tells readers why the project is on the list.
Wrap the core call in an Express or Fastify route, cache the response for a few hours and expose /trending?lang=go to your frontend. Since every field has a stable name, the response mapping is written once.
Keep a set of full_name values for your own projects or competitors and check each new run against it. A match can open an issue, ping a channel or log an event in your analytics stack.
Expose the call as a tool in your agent framework so a briefing bot can ask what is climbing in TypeScript this week. The compact rows fit comfortably into a model's context.
You need an Apify account and an API token from Console β Settings β Integrations. Apify's free plan includes monthly usage credit; this Actor's event prices are listed under Pricing below.
git clone https://github.com/devilscrapes/github-trending-scraper-javascript.git
cd github-trending-scraper-javascript
npm install
export APIFY_TOKEN=your_token_here
node index.js # uses input.json
node index.js my-input.json --out data --max-charge 2The script starts the GitHub Trending Scraper, waits for it to finish, prints a link to the run in Apify Console and saves every result to results.json and results.csv. It exits with code 1 and the run's status message if the run fails.
Four statements are all the integration needs. call() waits for the run to end, and listItems() returns the repositories from its default dataset as an array.
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('DevilScrapes/github-trending-scraper').call({"since": "weekly", "language": "rust", "maxResults": 10, "proxyConfiguration": {"useApifyProxy": true}});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);index.js reads input.json and passes it through untouched. The sample requests this week's Rust list and stops at 10 repositories, enough to wire up a prototype without spending much. Change since, language or spokenLanguage per request when you call it from a server.
{
"since": "weekly",
"language": "rust",
"maxResults": 10,
"proxyConfiguration": {
"useApifyProxy": true
}
}| Field | Type | Required | Default | Allowed values |
|---|---|---|---|---|
since |
string | no | "daily" |
daily, weekly, monthly |
language |
string | no | "" |
β |
spokenLanguage |
string | no | "" |
β |
maxResults |
integer | no | 25 |
β |
proxyConfiguration |
object | no | {"useApifyProxy": true} |
β |
The result is an array with one object per listed repository. Numeric fields arrive as integers, description and language may be null, and built_by is an array of avatar URL strings that can be empty.
| Field | Type | Always present |
|---|---|---|
rank |
integer | yes |
full_name |
string | yes |
owner |
string | no |
name |
string | no |
html_url |
string | yes |
description |
string or null | no |
language |
string or null | no |
stars_total |
integer | no |
stars_in_window |
integer | no |
forks |
integer | no |
built_by |
array | no |
scraped_at |
string | no |
window |
string | no |
| Event | Price (USD) |
|---|---|
| Actor start | $0.2 |
| Result emitted | $0.002 |
Prices as configured on 2026-08-20. The Actor page always shows the current price. Runs are billed to the Apify account that owns the token. Use --max-charge to cap what a single run can cost.
The snippet below loads the saved rows, orders them by stars earned in the period and writes a Markdown digest you could post to a chat or commit to a repository. Only Node.js built-ins are used.
import { readFile, writeFile } from 'node:fs/promises';
const rows = JSON.parse(await readFile('results.json', 'utf8'));
const top = [...rows]
.sort((a, b) => (b.stars_in_window ?? 0) - (a.stars_in_window ?? 0))
.slice(0, 10);
const period = rows[0]?.window ?? 'daily';
const lines = top.map((repo, i) => {
const lang = repo.language ?? 'n/a';
const blurb = repo.description ? ` - ${repo.description}` : '';
return `${i + 1}. [${repo.full_name}](${repo.html_url}) (${lang}, +${repo.stars_in_window ?? 0} stars, ${repo.stars_total ?? 0} total)${blurb}`;
});
const digest = `# GitHub trending (${period})\n\n${lines.join('\n')}\n`;
await writeFile('digest.md', digest, 'utf8');
console.log(digest);No. Search sorted by stars ranks all-time popularity, while Trending ranks recent activity over a day, week or month. This Actor returns GitHub's own published Trending order, including the stars gained during that period.
Usually, provided the platform lets the handler wait for the Actor to finish. Under a tight timeout, kick the run off with start() rather than call() and pick up the dataset later from a webhook or a second invocation.
The client package bundles its own declarations, so the core call drops into a .ts file as is. Declare an interface for the row fields in the Output table and cast the dataset items to it.
Set maxResults to what you actually display and pass a cap such as --max-charge 0.5. Each run costs the start fee plus one result charge per repository written.
GitHub shows contributor avatars on some cards and not on others. The Actor returns what the page includes, so treat the field as optional in your UI.
Set APIFY_TOKEN first: export the token in the same shell, or put it in a local.envfile your shell loads. Never commit it.- Run finished with
FAILEDorABORTED: open the printed Console link; the run log says why. Input validation errors name the field. - Fewer rows than expected: a
--max-chargecap also limits how many results can be charged. Raise the cap or lower the limits in your input. - Something looks wrong in the data: open an issue on the Actor page so the maintainers see it.
- GitHub Trending Scraper on Apify Store
- Python version of this example
- All Devil Scrapes Actors
- Apify web scraping cookbook
Related Actors:
- GitHub Repo Scraper β fuller metadata for any repository that catches your eye on Trending.
- GitHub Organization Scraper β profile data for the organizations behind the projects that keep climbing.
- Hacker News Scraper β see whether a climbing repository is also being discussed on Hacker News.
MIT licensed example code. The Actor itself runs on Apify and is maintained by Devil Scrapes. Not affiliated with GitHub, Inc..