Skip to content

About

GitHub trending API for Node.js: fetch the daily, weekly or monthly trending repositories with optional language filters and get rank, star velocity, forks and repo URLs as plain objects, JSON or CSV. JavaScript example for the Devil Scrapes Apify Actor.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

1 Commit

Folders and files

Repository files navigation

GitHub Trending Scraper β€” JavaScript example

GitHub Trending API for JavaScript and Node.js: trending repositories as JSON

Run on Apify JavaScript MIT license CI

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

πŸ”₯ What you get

  • One row for every repository on the list, with its position, the owner/name pair 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_window counts only the stars earned during the chosen period, kept apart from the lifetime stars_total, so you never diff two snapshots to estimate momentum.
  • Three periods, two filters: switch since between daily, weekly and monthly, and narrow by programming language, spoken language or both, all producing the same row shape.
  • GitHub's ordering survives export: the rank column 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.

🎯 Use cases

Post a daily trending digest to Slack or Discord

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.

Serve trending data from your own API

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.

Alert when a watched repository lands on the list

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.

Give an AI agent a fresh open source signal

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.

⚑ Quick start

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 2

The 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.

🧩 The core call

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);

πŸ“₯ Input

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} β€”

πŸ“€ Output

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

πŸ’° Pricing

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.

πŸ› οΈ Working with the data in JavaScript

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);

❓ FAQ

Is this the same as GitHub's search API sorted by stars?

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.

Will this fit inside a Lambda or Vercel function?

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.

What about a TypeScript codebase?

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.

How do I keep costs predictable in production?

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.

Why is built_by sometimes an empty array?

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.

🧯 Troubleshooting

  • Set APIFY_TOKEN first: export the token in the same shell, or put it in a local .env file your shell loads. Never commit it.
  • Run finished with FAILED or ABORTED: open the printed Console link; the run log says why. Input validation errors name the field.
  • Fewer rows than expected: a --max-charge cap 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.

πŸ”— More from Devil Scrapes

Related Actors:


MIT licensed example code. The Actor itself runs on Apify and is maintained by Devil Scrapes. Not affiliated with GitHub, Inc..

About

GitHub trending API for Node.js: fetch the daily, weekly or monthly trending repositories with optional language filters and get rank, star velocity, forks and repo URLs as plain objects, JSON or CSV. JavaScript example for the Devil Scrapes Apify Actor.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages