# GitHub Scraper - Repos, Stars and Activity (`s-r/github-scraper`) Actor

Look up GitHub repositories by name or search them. Returns stars, forks, watchers, open issues, language, topics, licence, archived status and real push activity. Free API token optional but recommended.

- **URL**: https://apify.com/s-r/github-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## GitHub Scraper

Look up **GitHub repositories** by name, or search them with GitHub's own query
syntax. Stars, forks, watchers, open issues, language, topics, licence, archived
status and, importantly, **real push activity**.

### Two rate-limit buckets, and why that matters to you

This is the thing to understand before you size a run. GitHub does not have one
rate limit, it has several, and the two this Actor touches behave completely
differently:

| What you do | Bucket | Unauthenticated | With a free token |
|---|---|---|---|
| Look up a repo by name | `core` | **60 per hour** | 5,000 per hour |
| Search repositories | `search` | **10 per minute** | 30 per minute |

They reset independently. A run that has spent its search budget still has 59
repo lookups available, and the reverse. Treating them as one ceiling stops runs
that still have budget, so this Actor tracks each bucket by the
`x-ratelimit-resource` header GitHub returns and reports both in the run
summary.

**Sixty repositories an hour is the entire unauthenticated budget.** For any
real work, create a personal access token: it takes a minute, needs no approval,
and needs **no scopes at all** for public repository data. Paste it into
`token`.

The Actor stops with two requests to spare rather than draining a bucket, and
says which one it stopped on.

### `pushed_at`, not `updated_at`

The single most useful field here, and the one most easily got wrong.

`updated_at` moves when *anything* about the repo changes, including a
description edit, a topic being added or a star arriving. A repository with no
commit for four years can show an `updated_at` from last week and look alive.

`pushed_at` moves only when code is actually pushed. `days_since_push` is
computed from that, and the run summary counts how many of your repositories
have had no push in over a year.

Combined with `archived`, that is the honest answer to "is this dependency
maintained?" — a question the star count cannot answer at all.

### `fork_ratio` and what it tells you

Forks divided by stars. Most libraries sit around 0.08 to 0.25: `encode/httpx`
is 0.082, `psf/requests` 0.186, `torvalds/linux` 0.261.

A ratio far above that usually means the repository is **copied rather than
depended on** — a template, a course, a boilerplate, a coding-challenge starter.
That is a useful filter when you are looking for libraries and the search
results keep handing you awesome-lists.

### Two modes

**By name**, which uses the `core` bucket:

```
repos: ["encode/httpx", "psf/requests"]
```

Full GitHub URLs are accepted and normalised, so you can paste
`https://github.com/psf/requests` straight in.

**By search**, which uses the `search` bucket and GitHub's own query syntax:

```
query: "language:python stars:>20000"
sort: stars
```

Anything GitHub search accepts works here: `topic:scraper`, `org:apify`,
`created:>2024-01-01`, `license:mit`, and combinations.

### Fields

- **Identity**: `full_name`, `owner`, `owner_type`, `name`, `description`, `url`
- **Popularity**: `stars`, `forks`, `watchers`, `fork_ratio`
- **Work in progress**: `open_issues`
- **Classification**: `language`, `topics`, `license`
- **Activity**: `created_at`, `updated_at`, `pushed_at`, `days_since_push`
- **Health**: `archived`, `disabled`, `is_fork`
- **Other**: `homepage`, `default_branch`, `size_kb`

`license` returns the SPDX id, and **`null` when GitHub reports `NOASSERTION`** —
which is what it sends for a licence file it cannot identify. Passing that
through would give you a licence apparently called NOASSERTION, so it does not.

### Input reference

| Field | Type | Default |
|---|---|---|
| `repos` | list of `owner/name` or URLs | `["encode/httpx","psf/requests"]` |
| `query` | GitHub search syntax | — |
| `sort` | stars, forks, updated, help-wanted-issues | `stars` |
| `order` | desc, asc | `desc` |
| `token` | free personal access token | — |
| `limit` | 1-1000 | 100 |
| `retries` | 1-6 | 3 |

GitHub caps **any** search at 1,000 results however you page it. When your query
matches more than that, the run summary says so rather than quietly stopping.

### Typical uses

- **Dependency health review.** Feed your dependencies' repositories in and sort
  by `days_since_push`, filtering on `archived`. That is the maintenance picture
  a star count hides.
- **Licence audit.** `license` across every repository you depend on.
- **Ecosystem and competitor research.** Search a topic or language and rank by
  stars, then use `fork_ratio` to separate libraries from templates.
- **Technology tracking.** `created:>` queries sorted by stars show what is new
  and gaining traction in a language.
- **Organisation overview.** `org:<name>` returns everything a company
  publishes, with activity attached.

### Notes

A repository that does not exist and one that is private are **indistinguishable
without a token that can see it**: GitHub answers 404 to both, deliberately, so
the API cannot be used to probe for private repositories. The error names both
possibilities rather than guessing.

A 401 means the token was rejected and is not retried, because retrying a bad
credential cannot help. A 403 with `x-ratelimit-remaining: 0` is a spent bucket
rather than an auth problem, and is reported as such.

# Actor input Schema

## `repos` (type: `array`):

Repositories as owner/name, for example encode/httpx. Full GitHub URLs are accepted and normalised.

## `query` (type: `string`):

GitHub search syntax instead of naming repos, for example 'language:python stars:>20000' or 'topic:scraper'.

## `sort` (type: `string`):

How search results are ordered.

## `order` (type: `string`):

Descending puts the highest or most recent first.

## `token` (type: `string`):

Without a token you get 60 repo lookups an hour and 10 searches a minute. A free personal access token raises those to 5,000 and 30, needs no approval and no scopes for public data.

## `limit` (type: `integer`):

How many repositories to return. GitHub search caps any query at 1,000 results.

## `retries` (type: `integer`):

Retries with backoff before a request is reported as an error.

## Actor input object example

```json
{
  "repos": [
    "encode/httpx",
    "psf/requests"
  ],
  "query": "language:python stars:>20000",
  "sort": "stars",
  "order": "desc",
  "limit": 100,
  "retries": 3
}
```

# Actor output Schema

## `repos` (type: `string`):

One row per repository.

## `summary` (type: `string`):

Counts, archived repos and the remaining rate-limit budget per bucket.

## `errors` (type: `string`):

Failures with a code and a redacted message.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "repos": [
        "encode/httpx",
        "psf/requests"
    ],
    "limit": 100,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/github-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "repos": [
        "encode/httpx",
        "psf/requests",
    ],
    "limit": 100,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/github-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "repos": [
    "encode/httpx",
    "psf/requests"
  ],
  "limit": 100,
  "retries": 3
}' |
apify call s-r/github-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/github-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/bYo3t3wJhFsxgKKhd/builds/BeMxqDpe2fryhnQ7P/openapi.json
