# GitHub Scraper (`dami_studio/github-scraper`) Actor

GitHub scraper: search repos and users with the query you would type into GitHub's search box, or paste a list of usernames or repos. Repository rows give stars, forks, issues, language, topics, licence and dates; user rows give name, bio, company, location and followers. $0.90 per 1,000 rows.

- **URL**: https://apify.com/dami_studio/github-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Developer tools, Integrations
- **Features:** Limited permissions
- **Stats:** 95 total users, 39 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.87 / 1,000 items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**GitHub scraper for repositories and users, from a search query or a list**: type the query you would
type into GitHub's own search box, `language:rust stars:>10000`, or paste the usernames or
`owner/repo` names you already have, and get one row per result. For repositories that is stars,
forks, open issues, language, topics, licence, the three dates and, if you ask, the README. For
users it is the profile: name, bio, company, location, followers and public repo count.

Any search stops at 1,000 results.
Split a big job by star range or by date and put the pieces in one run.

| | |
|---|---|
| **Input** | A GitHub search query in GitHub's own syntax, up to 10 of them, or a list of usernames or repositories |
| **Output** | One row per repository (stars, forks, issues, language, topics, licence, dates, README on request) or per user (name, bio, company, location, followers) |
| **Ceiling** | 1,000 results per search; 1,000 names per list |
| **Account needed** | None. Your own GitHub token is optional and lifts the rate limits a long way |
| **Price** | $0.90 per 1,000 rows on the free plan, down to $0.87 on Business. The free plan's $5 a month covers about 5,500 |

| Using an AI agent? | |
|---|---|
| Actor | `dami_studio/github-scraper` |
| Set one field | `query`, labelled "Search query" in the Console |
| Cap the bill | `maxTotalChargeUsd` on the run, for example `0.10`: it never charges past that |
| MCP, one link | `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/github-scraper` |
| How often | Weekly, to track stars and new repositories |

### 🔍 What GitHub Scraper does

It runs your query against GitHub's search, pages through the results 100 at a time, and writes a
row for each one. Repository searches sort by stars, forks, recent update, help-wanted issues or
best match. User searches sort by followers, public repositories or join date, or GitHub's own
ranking.

Every qualifier GitHub documents works, since the query goes through as you wrote it:
`topic:cli created:>2023-01-01`, `location:berlin followers:>500`, `fullstack in:bio`. If you
would rather not write qualifiers, the **Language**, **Minimum stars** and **Topics** fields add
them for you.

Already know what you want? Paste usernames or profile links into **Usernames**, or `owner/repo`
names into **Repositories**, and it reads each one directly. A list run does no search.

Duplicates are dropped before anything is written, so a repository that turns up twice, across
pages or across your searches, does not cost you twice.

Add your own GitHub token and the run gets GitHub's signed-in allowance, 5,000 requests an hour and
30 searches a minute. No special scopes: public data only.

### 📋 What data you get from each GitHub repository or user

| What you get | Field |
|---|---|
| The repository and who owns it | `fullName`, `name`, `owner`, `url`, `description` |
| Stars, forks and open issues | `stars`, `forks`, `openIssues` |
| Main language, topic tags and licence | `language`, `topics`, `license` |
| Homepage and default branch | `homepage`, `defaultBranch` |
| Created, last updated and last pushed | `createdAt`, `updatedAt`, `pushedAt` |
| Whether it is a fork, archived or a template, and its size | `isFork`, `archived`, `isTemplate`, `sizeKb` |
| The README as plain text, when you switch it on | `readme`, `readmeTruncated` |
| A user or organisation | `login`, `url`, `type`, `id` |
| Their profile text | `name`, `bio`, `company`, `location`, `blog` |
| Followers, following, public repos and gists | `followers`, `following`, `publicRepos`, `publicGists` |
| The X handle and the available-for-hire setting they put on the profile | `twitterUsername`, `hireable` |
| When the account was made and when the profile last changed | `createdAt`, `updatedAt` |
| When the row was read, and which of your searches found it | `scrapedAt`, `searchQuery` |

### ▶️ How to scrape GitHub repositories

1. Open [GitHub Scraper](https://apify.com/dami_studio/github-scraper) and click **Try for free**.
2. Put your query into **Search query**. Build it on GitHub first if you are not sure of it, then
   paste it across. Or fill **Usernames** or **Repositories** instead.
3. Pick **Search type**: repositories or users.
4. Set **Max items**, up to 1,000. For anything past a hundred or so, paste a **GitHub token**.
5. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it from the API.

### 💰 How much does it cost to scrape GitHub repositories?

**$0.90 per 1,000 rows**, which is $0.0009 each. That is the free plan's price; paid plans pay a little less: $0.89 on Starter, $0.88 on Scale and $0.87 on Business. On the
free plan, the $5 Apify gives you each month covers about 5,500 rows.

You pay per result row, repository or user, and the README text costs nothing on top. Diagnostics
are not charged, nor are names GitHub doesn't know, nor duplicates, since they are dropped before
anything is written.

### 📥 What you give it

```json
{
  "query": "language:python stars:>5000 web framework",
  "type": "repositories",
  "sort": "stars",
  "maxItems": 200
}
```

For a list instead: `{"usernames": ["torvalds", "octocat"]}` or `{"repositories": ["rust-lang/rust"], "includeReadme": true}`.

| Field | Default | What it is |
|---|---|---|
| `query` | none | GitHub search syntax, exactly as the site takes it. The form arrives with an example in it. |
| `queries` | none | Up to 10 searches in one run, each up to `maxItems`. When set, `query` is not run. |
| `usernames` | none | Up to 1,000 usernames or profile links. When this or `repositories` is set, no search runs. |
| `repositories` | none | Up to 1,000 repositories as `owner/repo` or links. |
| `type` | `repositories` | `repositories` or `users`, for searches. Users and organisations both come back under `users`. |
| `sort` | `stars` | `stars`, `forks`, `updated` or `help-wanted-issues` for repositories; `followers`, `repositories` or `joined` for users; or `best-match`. A value that doesn't fit the search type gets best match. |
| `language` | none | Adds `language:` to every search, in place of any `language:` the search already has. |
| `minStars` | none | Adds `stars:>=` to every repository search. |
| `topics` | none | Adds `topic:` for each one. All must match. |
| `includeReadme` | `false` | README text on repository rows, cut at 20,000 characters. |
| `maxItems` | `100` | 1 to 1,000 per search. Lists are read in full. |
| `githubToken` | none | Your own personal access token, marked secret. No scopes needed for public data. Recommended for anything past a small run. |
| `notionConnector`, `notionParentId` | none | Optional. Also write each row into your connected Notion workspace. |
| `proxyConfiguration` | none | Optional and usually unnecessary. |

### 📤 What you get back

![Real run output](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/github-scraper-output.png)

A real repository row from a real run:

```json
{
  "ok": true,
  "fullName": "fastapi/fastapi",
  "name": "fastapi",
  "owner": "fastapi",
  "url": "https://github.com/fastapi/fastapi",
  "description": "FastAPI framework, high performance, easy to learn, fast to code, ready for production",
  "stars": 103143,
  "forks": 10048,
  "openIssues": 81,
  "language": "Python",
  "topics": ["api", "async", "asyncio", "fastapi", "framework", "json", "openapi", "python", "rest", "swagger"],
  "license": "MIT",
  "homepage": "https://fastapi.tiangolo.com/",
  "defaultBranch": "master",
  "createdAt": "2018-12-08T08:21:47Z",
  "updatedAt": "2026-10-16T16:24:31Z",
  "pushedAt": "2026-10-16T14:13:36Z",
  "isFork": false,
  "archived": false,
  "isTemplate": false,
  "sizeKb": 55214,
  "scrapedAt": "2026-10-16T17:46:00.033Z"
}
```

| Field | How to read it |
|---|---|
| `fullName` | The repo and its owner in one string. Use it as your key. |
| `stars`, `forks`, `openIssues` | Numbers at read time, not running totals. |
| `license` | The licence as its SPDX id. |
| `homepage`, `defaultBranch` | As set on the repo. Null when there is none. |
| `pushedAt`, `updatedAt` | `pushedAt` is the one that tells you whether a project is alive. `updatedAt` moves on a star, too. |
| `sizeKb` | The size GitHub reports for the repository, in kilobytes. |
| `readme` | Null when the repository has no README. `readmeTruncated` is true when it was cut at 20,000 characters. |
| `type` | On user rows, separates a person from an organisation. |
| `name`, `bio`, `company`, `location`, `blog`, `twitterUsername` | On user rows, profile text as filled in. Plenty of accounts leave these empty. |
| `hireable` | True only when the person ticked it on their profile. |

### 🧾 Reading the output

Data rows carry `ok: true`. Notes carry `ok: false` and an `errorCode`, and are never charged.

| Row | How to spot it | Charged |
|---|---|---|
| A repository or user | `ok` is true | yes |
| A diagnostic | `ok` is false, and `errorCode` says what happened | no |

| Code | What it means |
|---|---|
| `BAD_INPUT` | The query was empty, GitHub refused the query or the token (its reason is in `error`), or a list entry is not a username or `owner/repo`. |
| `NO_RESULTS` | GitHub returned no matches for that search. |
| `RATE_LIMITED` | GitHub's limit was hit. `rateLimitResetsAt` says when it clears. Add a token, or ask for fewer items. |
| `NOT_SERVED` | This request was not served this run. Re-run it later. Runs before 13 October 2026 wrote `BLOCKED` here, and runs from 13 to 17 October wrote `NETWORK`. |
| `NOT_FOUND` | A username or repository from your list that GitHub doesn't know. `input` says which. |
| `SERVER_ERROR` | GitHub's own error. Worth one re-run. |
| `NETWORK` | GitHub was unreachable or answered badly. |
| `TIME_LIMIT` | The run got close to its time limit and stopped on its own. The results delivered before that are complete and charged. Give the run more time, or ask for fewer items, to get the rest. |
| `UNEXPECTED_ERROR` | The run stopped early on an unexpected error. The results before this row are kept. |

A note never fails the run, `TIME_LIMIT` included: a run the time limit stopped before any result came back still ends as succeeded, with an amber status message.

The default table view is built for repositories, so a `users` run looks blank in it. Open a row, or
export the dataset, and the profile fields are all there.

### 💡 What people use it for

- Tracking a topic over time. The same query weekly, keyed on `fullName`, shows what is growing.
- Checking a dependency list: paste every `owner/repo` you rely on and filter for `archived`, or a
  `pushedAt` more than a year old.
- Licence audits across a topic, using the SPDX id on every row.
- Building a reading set from READMEs, with `includeReadme` on a `topic:` search.
- Feeding a Notion database of interesting repos straight from the run.

### 🚧 What it does not do

- **1,000 results per search.** Split by `stars:1000..5000`, by `created:` window, or
  by language, and put the pieces in **Several searches**.
- **No repo contents beyond the README.** No file tree, no issues, no pull requests, no contributor list.
- **No private data.** Public repositories and public profiles only, whatever token you paste.
- **No e-mail addresses**, even where a profile shows one.
- **A rate limit part-way through a single search loses that run's rows.** Use a token for long
  jobs. With lists, several searches or README text, the rows read before the limit are kept.
- **The odd user row from a search comes back thin**, with only the login and URL, when GitHub's
  profile call did not answer for that account. It is charged like any other row.
- **Counts are a snapshot.** Stars move.
- It is not an official GitHub tool. Dami's Studio is independent and is not affiliated with or endorsed by GitHub, or by any other company named on this page.

### 🧭 Which developer scraper do you need?

| If you want | Use |
|---|---|
| Articles from DEV.to by tag or author, with the full text on request | [DEV.to Scraper](https://apify.com/dami_studio/devto-scraper) |
| Hacker News stories, Ask and Show HN posts, and comments | [Hacker News Scraper](https://apify.com/dami_studio/hacker-news-scraper) |
| Stack Overflow questions with the body and the accepted answer | [Stack Overflow Scraper](https://apify.com/dami_studio/stackexchange-scraper) |
| Repositories and developer profiles from a GitHub search or a list | This one |
| npm and PyPI package details and download counts | [npm + PyPI Package Scraper](https://apify.com/dami_studio/package-registry-scraper) |
| Product launches and their upvotes | [Product Hunt Scraper](https://apify.com/dami_studio/producthunt-scraper) |

### ❓ Questions people ask

#### Do I need a GitHub token to scrape repositories?

Not for a small run. For anything bigger, yes, because a run
that hits the rate limit stops. A list or README text uses one request per name, so past about 60 you need one.

#### Is my GitHub token safe?

It goes in a secret input field, it is only sent to GitHub, and it never appears in the dataset or
the log. A read-only token with no scopes is enough.

#### Why did my GitHub search return nothing?

If GitHub refused the query, you get a `BAD_INPUT` row with GitHub's own reason, such as a
misspelt qualifier (`star:>1000` instead of `stars:>1000`). A `NO_RESULTS` row means GitHub really
found nothing.

#### How do I get more than 1,000 repositories?

Split the job into narrower ranges, star bands or creation-date windows, and put up to 10 of them
in **Several searches**. A repository two of them find is charged once.

#### Can I get specific GitHub users or organisations?

Yes. Paste their usernames or profile links into **Usernames**. Organisations also come back in a
`users` search, with `type` telling you which is which.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/github-scraper/api/python) has code for Python, JavaScript and the CLI.
MCP clients like Claude and ChatGPT use `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/github-scraper`;
coding assistants can load [our skill
file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-social-communities-SKILL.md). All
three bill your Apify account at the same price.

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("dami_studio/github-scraper").call(
    run_input={
        "query": "language:python stars:>5000 web framework",
        "type": "repositories",
        "maxItems": 20,
    },
    max_total_charge_usd=0.10,  # the most this run may spend
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row.get("ok") is False:  # free note rows, not results
        continue
    print(row.get("fullName"), row.get("stars"))
```

#### Is it legal to scrape GitHub?

This returns public repositories and public profiles only, read through GitHub's public API. Profiles
are personal data under GDPR and similar laws, so have a reason for holding them, and the rules on
unsolicited contact apply if you use a profile's links to reach someone. GitHub's own terms also
limit what you may do with data taken from it. Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the exact query and the run ID. If a diagnostic row
landed, its `errorCode` and `hint` usually name the reason already. Never paste your token into an
issue.

# Actor input Schema

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

GitHub search syntax. For repositories: "language:python stars:>1000 machine learning", "topic:cli created:>2023-01-01". For users: "location:berlin followers:>500", "fullstack in:bio". See GitHub's search docs for all qualifiers. Not run when you fill in Several searches, Usernames or Repositories below.

## `usernames` (type: `array`):

Up to 1,000 GitHub usernames or profile links, one per line, for people or organisations you already know. Each comes back as a user row and costs the same as one. A name GitHub doesn't know gets a free NOT_FOUND row. When this list or Repositories has entries, no search runs. Past about 60 names you need a GitHub token (below).

## `repositories` (type: `array`):

Up to 1,000 repositories as owner/repo or links, one per line. Each comes back as a repository row and costs the same as one. A repository GitHub doesn't know gets a free NOT_FOUND row. When this list or Usernames has entries, no search runs. Past about 60 repositories you need a GitHub token (below).

## `queries` (type: `array`):

Up to 10 searches in one run, one per line, each up to Max items. A result found by two of them is written and charged once, with searchQuery saying which search found it first. When this list has entries, Search query above is not run, so put every search here.

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

What to search for: repositories or users/organizations. Users are additionally enriched with profile details (name, bio, company, location, followers, public repos).

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

Stars, forks, recently updated and help-wanted issues sort a repository search. Followers, repositories and join date sort a user search. A choice that doesn't fit the search type gets GitHub's best match, which is also what Best match means.

## `language` (type: `string`):

Optional. Only repositories in this language, or users who write it, such as Python or Rust. Added to the search as language:, in place of any language: in Search query. Leave Search query empty and the shortcuts here become the search on their own.

## `minStars` (type: `integer`):

Optional. Only repositories with at least this many stars. Added to the search as stars:>=. Repository searches only.

## `topics` (type: `array`):

Optional. Only repositories tagged with every one of these topics, such as llm or cli. Added to the search as topic:. GitHub can't match either-of for topics, so each one narrows the search further. Repository searches only.

## `includeReadme` (type: `boolean`):

Adds each repository's README as plain text, up to 20,000 characters (readmeTruncated says when it was cut). It takes one more request per repository but costs nothing extra: the row is still one result. Past about 60 repositories you need a GitHub token (below). Repository rows only.

## `maxItems` (type: `integer`):

Maximum number of repositories/users to return for each search. GitHub's Search API caps at 1000 results per query (10 pages of 100), so split large jobs by qualifier (e.g. star range or date range). Usernames and Repositories are all read, whatever this says.

## `notionConnector` (type: `string`):

Optional. Write each item as a page into your Notion when the run finishes. Authorize a Notion connector once in Settings → API & Integrations → MCP connectors, then pick it here. Leave empty to skip (default), results are always saved to the dataset regardless.

## `notionParentId` (type: `string`):

Optional. The Notion data source ID of the database to write into (only used if a Notion connector is set). Leave empty to create the pages privately in your workspace instead.

## `githubToken` (type: `string`):

Optional GitHub personal access token. Strongly recommended: without it you get only 60 requests/hr and 10 searches/min; with it you get 5000 requests/hr and 30 searches/min, so larger jobs finish. No special scopes needed for public data. Kept private.

## `proxyConfiguration` (type: `object`):

Optional proxy for the GitHub API calls. The GitHub API is a public endpoint, so a proxy is usually unnecessary.

## Actor input object example

```json
{
  "query": "language:python stars:>5000 web framework",
  "type": "repositories",
  "sort": "stars",
  "includeReadme": false,
  "maxItems": 100
}
```

# Actor output Schema

## `results` (type: `string`):

Scraped rows are stored in the default dataset (one row per result). Blocked/empty/error runs return a single uncharged diagnostic row instead.

# 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 = {
    "query": "language:python stars:>5000 web framework"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/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 = { "query": "language:python stars:>5000 web framework" }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/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 '{
  "query": "language:python stars:>5000 web framework"
}' |
apify call dami_studio/github-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/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/mUljStAxkfPqYPKMS/builds/DNFHsIhPSz46tOVcH/openapi.json
