GitHub Repository Scraper: Stars, Releases & Contributors avatar

GitHub Repository Scraper: Stars, Releases & Contributors

Pricing

from $1.50 / 1,000 repository scrapeds

Go to Apify Store
GitHub Repository Scraper: Stars, Releases & Contributors

GitHub Repository Scraper: Stars, Releases & Contributors

Scrape public GitHub repositories: stars, forks, issues, licence, topics, languages, latest releases and top contributors. Works unauthenticated; supply a token to raise limits.

Pricing

from $1.50 / 1,000 repository scrapeds

Rating

0.0

(0)

Developer

Arman Hossain

Arman Hossain

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

5 hours ago

Last modified

Share

GitHub Repo Scraper — Stars, forks, licences, the full language breakdown, releases and top contributors

GitHub Repository Scraper turns a list of public GitHub repositories into structured rows, stars, forks, watchers, open issues, licence, topics, the full language breakdown, recent releases and the top contributors by commit count.

It reads the public GitHub REST API directly: no browser, no proxies, no scraping of HTML, no login required. A personal access token is optional and does one thing, raises the request budget from 60/hour to 5000/hour.

What you get

Output fieldMeaning
requestedAs, fullNameWhat you passed in, and the canonical owner/repo GitHub resolved it to
owner, url, homepageOwner login, repo URL, and the project's own site if it declares one
descriptionRepo tagline
stars, forks, watchers, openIssuesCurrent counts, see the note on watchers below
licenseSPDX identifier where GitHub has one (MIT, Apache-2.0), otherwise the licence name
topicsThe repo's topic tags
primaryLanguage, languagesDominant language, and the full { language: bytes } map when includeLanguages is on
defaultBranch, isArchived, isFork, sizeKbBranch name, archive/fork status, checkout size in KB
createdAt, pushedAtCreation time and the last push, pushedAt is the real activity signal
latestRelease, releasesNewest stable release, plus the last 10, when includeReleases is on
contributorsTop 10 by commit count, when includeContributors is on
scrapedAtRun timestamp

A RUN_SUMMARY record in the key-value store holds per-run counts, remaining rate-limit budget, which repos failed and which were skipped.

On watchers. GitHub's API has a long-standing quirk: watchers_count is a legacy alias that returns the star count, while the number of people actually watching the repo lives in subscribers_count. This Actor maps watchers to the real subscriber count, so stars and watchers are different numbers here, unlike in a raw API response.

On requestedAs vs fullName. GitHub answers renamed and transferred repos with a 301 to the new location. The Actor follows it, so asking for facebook/react today saves a row with fullName: "react/react". Both are kept so you can join back to your input list.

Common use cases

Track OSS competitor traction. Sweep a watchlist on a schedule and diff stars and pushedAt week over week.

{
"repositories": ["vercel/next.js", "remix-run/react-router", "nuxt/nuxt", "sveltejs/kit"],
"includeReleases": true,
"includeContributors": false,
"includeLanguages": false,
"githubToken": "ghp_your_token_here"
}

Build a repo-ranking leaderboard. Metrics only, three requests saved per repo, so a long list fits inside the unauthenticated budget.

{
"repositories": ["torvalds/linux", "facebook/react", "tensorflow/tensorflow", "kubernetes/kubernetes"],
"includeReleases": false,
"includeContributors": false,
"includeLanguages": false
}

Due diligence on an open-source dependency. Everything on, for a handful of repos, licence, bus factor, release cadence and whether it is still maintained.

{
"repositories": ["expressjs/express", "lodash/lodash"],
"includeReleases": true,
"includeContributors": true,
"includeLanguages": true
}

Input

FieldTypeDefaultNotes
repositoriesarray-Required. owner/repo, a github.com URL, or a git clone string. Mixed input is fine.
includeReleasesbooleantrueLast 10 releases + newest stable as latestRelease. +1 request per repo.
includeContributorsbooleantrueTop 10 contributors by commit count. +1 request per repo.
includeLanguagesbooleantrueFull { language: bytes } map. +1 request per repo.
githubTokenstring""Optional PAT. Raises 60 → 5000 requests/hour. Stored as a secret.

Which combinations make sense. The three booleans are the whole cost model. With all of them off, a repo costs 1 request; with all on it costs 4. Unauthenticated that is the difference between roughly 60 repos an hour and roughly 15. Topics, stars, forks, licence, language name, dates and archive status all ride along on the base request and are always present. You never need to turn an extra on to get those. Turn extras on for depth on a short list; turn them off for breadth on a long one.

Output example

{
"requestedAs": "facebook/react",
"fullName": "react/react",
"owner": "react",
"description": "The library for web and native user interfaces.",
"url": "https://github.com/react/react",
"homepage": "https://react.dev",
"stars": 247059,
"forks": 51190,
"watchers": 6600,
"openIssues": 1248,
"license": "MIT",
"topics": ["declarative", "frontend", "javascript", "library", "react", "ui"],
"primaryLanguage": "JavaScript",
"languages": {
"JavaScript": 5659949,
"Rust": 2965252,
"TypeScript": 2567778,
"HTML": 116629,
"CSS": 95930
},
"defaultBranch": "main",
"isArchived": false,
"isFork": false,
"sizeKb": 1045734,
"createdAt": "2013-05-24T16:15:54Z",
"pushedAt": "2026-08-05T19:11:16Z",
"latestRelease": {
"tagName": "v19.2.8",
"name": "19.2.8 (July 21st, 2026)",
"publishedAt": "2026-07-21T15:49:09Z",
"isPrerelease": false,
"isDraft": false,
"url": "https://github.com/react/react/releases/tag/v19.2.8",
"notes": "## React Server Components\n\n- Performance improvements when decoding …"
},
"releases": ["… the last 10, same shape as latestRelease …"],
"contributors": [
{ "login": "sebmarkbage", "contributions": 1939, "type": "User", "url": "https://github.com/sebmarkbage" },
{ "login": "zpao", "contributions": 1778, "type": "User", "url": "https://github.com/zpao" },
{ "login": "gaearon", "contributions": 1687, "type": "User", "url": "https://github.com/gaearon" }
],
"scrapedAt": "2026-08-06T12:00:00.000Z"
}

RUN_SUMMARY in the key-value store:

{
"reposRequested": 4,
"reposSaved": 4,
"reposFailed": 0,
"reposSkipped": 0,
"failures": [],
"skipped": [],
"requestsUsed": 16,
"requestsPerRepo": 4,
"rateLimited": false,
"rateLimit": {
"limit": 60,
"remaining": 44,
"resetsAt": "2026-08-06T13:12:11.000Z",
"authenticated": false
},
"filters": { "includeReleases": true, "includeContributors": true, "includeLanguages": true },
"finishedAt": "2026-08-06T12:00:07.221Z"
}

Limits and behaviour

  • 60 requests per hour, per IP, unauthenticated. This is the constraint that decides what this Actor can do for you. With all extras on, one repo costs four requests, so an unauthenticated run tops out around 15 repos per hour. With all extras off it is about 60. A token raises the ceiling to 5000 requests/hour, roughly 1,250 repos an hour with everything on. If you plan to scrape more than a handful of repos, supply a token.
  • The budget is read from every response. rate-limit headers and rate-limit headers are logged after every repo and written into RUN_SUMMARY.rateLimit.
  • Exhaustion stops the run cleanly, before a partial row. The Actor checks the remaining budget before starting each repo and stops if it cannot afford to finish that repo, so you never get a row with the extras silently missing. Remaining repos are listed in RUN_SUMMARY.skipped, rateLimited is set to true, and the reset time is logged. It does not hammer into a wall of 403s.
  • One bad repo never aborts the run. A typo or a private repo returns 404, lands in RUN_SUMMARY.failures, and the run moves on. The Actor only throws if every repo failed, or if the budget was already gone before a single repo could be read.
  • Transient errors are retried. 429 and 5xx get three attempts with linear backoff. 404, 401 and 403-with-budget-remaining are fatal for that repo and are not retried.
  • Empty repos are handled. A repo with no commits answers 204/409 on /contributors and /languages; those come back as empty rather than as an error.
  • Duplicates are removed before any request is made, so passing the same repo twice costs you nothing.
  • Public data only. No authentication is required, no private repos are reachable, no access control is bypassed. Contributor logins are public profile data.

API example

curl -X POST "https://api.apify.com/v2/acts/arman-bd~github-repo-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"repositories": ["facebook/react", "vercel/next.js"],
"includeReleases": true,
"includeContributors": false,
"includeLanguages": false
}'

JavaScript example

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/github-repo-scraper').call({
repositories: ['facebook/react', 'vercel/next.js', 'sveltejs/kit'],
includeReleases: true,
includeContributors: false,
includeLanguages: false,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const r of items) {
console.log(`${r.fullName}: ${r.stars} stars, latest ${r.latestRelease?.tagName ?? 'no releases'}`);
}

FAQ

Do I need a proxy? No. Proxy configuration is not required to run this Actor.

Do I need a GitHub account? Not to run it. You need one only to create a token, which you want for anything beyond a dozen or so repos.

What scopes does the token need? None. A classic PAT with zero scopes ticked, or a fine-grained token with read access to public repositories, is enough, the token exists purely to identify you for rate-limiting.

Can it read private repos? Only if you supply a token that has access to them. With no token, or a public-only token, private repos return 404 and are reported as failures.

Why is stars different from watchers? Because they should be. GitHub's raw watchers_count field returns the star count for historical reasons; this Actor reports the real subscriber count instead.

Why did fullName come back different from what I typed? The repo was renamed or transferred and GitHub 301'd to its new home. requestedAs keeps your original string so you can join the two.

What happens if GitHub is unavailable? 5xx responses are retried three times, then that repo is recorded in RUN_SUMMARY.failures and the run continues.

Can I schedule it? Yes, it is built for it. Run a watchlist daily and diff on stars, forks and pushedAt.

Can I integrate it with something else? Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.