# How to Optimize GitHub Readme Stats Performance for Users with Many Repositories

> Boost GitHub Readme Stats performance for users with many repos. Learn to optimize cache TTLs, exclude large repos, and use GraphQL pagination for faster loading.

- Repository: [Anurag Hazra/github-readme-stats](https://github.com/anuraghazra/github-readme-stats)
- Tags: performance
- Published: 2026-02-28

---

**Optimize GitHub Readme Stats performance by increasing cache TTLs, excluding large repositories via environment variables or query parameters, and implementing GraphQL pagination to reduce payload size for users with hundreds of repositories.**

The `anuraghazra/github-readme-stats` service generates SVG cards by querying the GitHub GraphQL API and processing the response on the fly. For users with many repositories, the **top-languages fetcher** in [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js) becomes the primary bottleneck because it retrieves up to 100 repositories and iterates over every language edge to compute weighted sizes. Tuning the caching strategy and request parameters keeps response times under one second and prevents GitHub API rate limit exhaustion.

## Understanding the Performance Bottleneck

The critical path for high-volume users lies in the data fetching layer. When [`api/top-langs.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/api/top-langs.js) receives a request, it invokes `fetchTopLanguages` in [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js), which constructs a GraphQL query requesting `first: 100` repositories with `languages(first: 10)` per repository.

This design creates three performance challenges for users with extensive repositories:

1. **Large payload size** – A single query returns up to 1,000 language edges (100 repos × 10 languages), increasing network latency and JSON parsing time.
2. **Triple iteration** – The fetcher runs three separate `reduce` operations (flatten, aggregate, weight) across every language edge, consuming CPU cycles proportional to repository count.
3. **No native pagination** – The current implementation fetches a static 100-repo block; users with 200+ repos lose visibility into newer repositories without manual exclusion of older ones.

## Key Optimization Strategies

### Leverage Repository Exclusion Lists

The most immediate performance gain comes from reducing the dataset before processing. The codebase supports exclusion at two levels: global environment variables and per-request query parameters.

In [`src/common/envs.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/common/envs.js), the `EXCLUDE_REPOS` environment variable defines a comma-separated list of repositories to ignore across all requests:

```dotenv

# .env configuration

EXCLUDE_REPOS=large-monorepo,auto-generated-docs,legacy-archive

```

For individual customization, users can pass the `exclude_repo` query parameter in the URL. In [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js), these arrays merge into `repoToHide`:

```js
const allExcludedRepos = [...exclude_repo, ...excludeRepositories];

```

Excluding even five large repositories can reduce the language edge count by 50+, cutting processing time by 20-30%.

### Increase Cache TTL for High-Traffic Users

The service uses [`src/common/cache.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/common/cache.js) to manage `Cache-Control` headers. By default, `CACHE_TTL.TOP_LANGUAGES.DEFAULT` sets a conservative TTL, but high-repo users should extend this to minimize redundant GraphQL calls.

You can override the default at the request level using the `cache_seconds` parameter:

```bash

# Set cache to 12 hours (43200 seconds)

https://github-readme-stats.vercel.app/api/top-langs?username=bigdev&cache_seconds=43200

```

The `resolveCacheSeconds` function in [`src/common/cache.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/common/cache.js) clamps the value between `MIN` and `MAX` bounds, ensuring you cannot accidentally set an invalid duration. For self-hosted instances, modify the `CACHE_TTL` object directly:

```js
// src/common/cache.js
const CACHE_TTL = {
  TOP_LANGUAGES: {
    DEFAULT: 43200, // 12 hours
    MIN: 3600,
    MAX: 86400,
  },
};

```

### Implement GraphQL Pagination

For users with hundreds of repositories, replace the single `first: 100` query with paginated requests using the `after` cursor. This reduces per-request payload size and avoids GitHub's 5-second query timeout.

In [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js), replace the static fetcher with a paginated version:

```js
const fetcherPaginated = async (variables, token) => {
  const perPage = 30; // Smaller pages reduce timeout risk
  let after = null;
  let allNodes = [];

  do {
    const res = await request(
      {
        query: `
          query ($login: String!, $after: String) {
            user(login: $login) {
              repositories(first: ${perPage}, after: $after, ownerAffiliations: OWNER, isFork: false) {
                pageInfo { hasNextPage endCursor }
                nodes {
                  name
                  languages(first: 5, orderBy: {field: SIZE, direction: DESC}) {
                    edges { size node { color name } }
                  }
                }
              }
            }
          }`,
        variables: { login: variables.login, after },
      },
      { Authorization: `token ${token}` },
    );

    const repoPage = res.data.data.user.repositories.nodes;
    allNodes = allNodes.concat(repoPage);
    after = res.data.data.user.repositories.pageInfo.hasNextPage
      ? res.data.data.user.repositories.pageInfo.endCursor
      : null;
  } while (after && allNodes.length < 200); // Stop after ~200 repos

  return { data: { user: { repositories: { nodes: allNodes } } } };
};

```

This approach fetches 30 repositories per request, stopping after accumulating 200 repos or reaching the end of the list. It prevents timeouts and reduces memory pressure during JSON parsing.

## Configuration Examples

### Exclude Specific Repositories via URL

```bash

# Exclude 'monorepo' and 'legacy-docs' from the calculation

https://github-readme-stats.vercel.app/api/top-langs?username=poweruser&exclude_repo=monorepo,legacy-docs

```

### Set Extended Cache Duration

```bash

# Cache the result for 24 hours to minimize API calls

https://github-readme-stats.vercel.app/api/top-langs?username=poweruser&cache_seconds=86400

```

### Global Environment Exclusions

```dotenv

# .env file for self-hosted deployments

EXCLUDE_REPOS=large-generated-client,automated-documentation

```

### Implement Pagination in Self-Hosted Instances

Replace the default fetcher in [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js) with the `fetcherPaginated` implementation shown above, then redeploy. This modification requires no changes to the card rendering logic in [`src/cards/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/cards/top-languages.js).

## Summary

- **The bottleneck** for users with many repositories is the [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js) fetcher, which processes up to 100 repositories and their language edges in a single request.
- **Exclude repositories** using the `EXCLUDE_REPOS` environment variable or `exclude_repo` query parameter to reduce payload size before processing begins.
- **Extend cache TTLs** via the `cache_seconds` parameter or by modifying `CACHE_TTL.TOP_LANGUAGES.DEFAULT` in [`src/common/cache.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/common/cache.js) to minimize redundant GraphQL calls.
- **Implement pagination** by replacing the static fetcher with a cursor-based approach that requests smaller repository batches, preventing timeouts and reducing memory usage.
- **Pre-compute static assets** for extremely high-traffic users by running a scheduled job that saves the SVG output, bypassing runtime computation entirely.

## Frequently Asked Questions

### How does the exclude_repo parameter improve performance?

The `exclude_repo` parameter filters out specified repositories before the language aggregation logic runs in [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js). By removing large or irrelevant repositories from the dataset, you reduce the number of language edges processed during the three `reduce` operations (flatten, aggregate, weight), which directly decreases CPU usage and response time.

### What is the default cache duration for top languages cards?

According to [`src/common/cache.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/common/cache.js), the default cache duration is controlled by `CACHE_TTL.TOP_LANGUAGES.DEFAULT`, which is typically set to a conservative value (often 2 hours). You can override this per-request using the `cache_seconds` query parameter, or modify the default value in the source code for self-hosted instances to reduce GraphQL API calls for frequently requested users.

### Can I use pagination with the public Vercel instance?

No, the public Vercel instance (`github-readme-stats.vercel.app`) uses the standard static fetcher that requests up to 100 repositories in a single GraphQL query. To implement pagination, you must deploy your own instance and modify [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js) to use a cursor-based fetcher that requests smaller batches (e.g., 30 repos per request) and aggregates the results before passing them to the card renderer.

### Why does the top-languages card timeout for some users?

The timeout occurs because the GitHub GraphQL API has a 5-second query timeout limit. For users with hundreds of repositories, the single `first: 100` query in [`src/fetchers/top-languages.js`](https://github.com/anuraghazra/github-readme-stats/blob/main/src/fetchers/top-languages.js) becomes too expensive to execute within this window, especially when calculating language statistics across many edges. Solutions include excluding large repositories to reduce query complexity, implementing pagination to split the workload across multiple smaller requests, or pre-computing the SVG to bypass runtime generation.