jstrieb/github-stats: Private-Repo Contribution Images Without a Server
Better GitHub statistics images for your profile, with stats from private repos too
At a glance
- What is it?
- A GitHub Actions workflow that turns your GitHub API data into theme-aware SVG cards, including private and contributed repositories. It is a self-hosted alternative to hosted stats cards, with the accuracy caveats that come with that.
- Who is it for?
- Adopt jstrieb/github-stats if you want stats cards that include private repositories and you are willing to keep a classic token with repo scope inside a repository you control, plus the Actions minutes each regeneration costs. Do not adopt it if you only want a hosted card with no token and no workflow, or if your profile depends on repositories with more than 10,000 commits, where GitHub refuses to count lines of code.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Zig, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What jstrieb/github-stats solves that a profile page does not
A GitHub profile shows stars, forks and pinned repositories. Those signals reflect public attention, not the work a developer actually does. Someone whose contributions are mostly in private repositories, or in repositories they contribute to but do not own, can look inactive on their own profile. The README states the problem directly: stars, forks and pinned repositories "do not necessarily reflect the contributions they make to private repositories", and the visible data does not cover contributions beyond the current year.
This project collects profile and repository statistics through the GitHub API and renders them as SVG images for a repository README or a profile README. Because the analysis runs in your own GitHub Actions workflow, it can use your personal access token to read private repositories. An external hosted service could not reach that data without you handing it the same token. The same run also dumps the raw statistics to a JSON file, which is the part worth knowing about if the rendered cards are not enough for you.
The intended user is someone who maintains a profile README and cares about private or contributed work being visible. It is not aimed at people who want a one-line embed and no repository of their own.
How the GitHub Actions workflow turns API data into SVG cards
There is no server component. The repository is a template you copy, and the workflow inside it does the work on a schedule or on demand. The analysis code is written in Zig, which is visible from the repository layout: build.zig, build.zig.zon and src/ sit at the top level, with .github/ holding the workflow that drives them.
The data flow has four stages. The workflow starts, reads the ACCESS_TOKEN secret, and calls the GitHub API for user and repository metadata. It then computes contributor statistics. The README notes that the GitHub API endpoint for computing contributor statistics no longer works reliably, so the project falls back on cloning each repository locally and tallying lines changed with the git CLI. The results are rendered into overview.svg and languages.svg, and the same statistics are written to a JSON file for further analysis.
Two details in the generated images are easy to miss. First, the README shows the SVGs referenced with #gh-dark-mode-only and #gh-light-mode-only fragments, so the images switch between GitHub light theme and dark theme. Second, the images live on a generated branch rather than in your main branch. The README's own image links point at blob/generated/overview.svg and blob/generated/languages.svg, which is how the workflow publishes output without committing to master.
The fallback to local cloning is the design decision that shapes everything else. It is slower and it costs Actions minutes, but it is the only way the project can produce line counts at all now that the API endpoint is unreliable.
Installing jstrieb/github-stats and running the workflow once
The README gives a linear path: make a classic personal access token, copy the repository, add the token as a secret, run the workflow, retrieve the images. The token must be a classic token, not a fine-grained one, and it needs read:user, user:email and repo permissions. The README explains why each is needed: read:user and repo for reading user and repository metadata, and user:email for attributing commits correctly when repositories are cloned locally. The README also warns that some users report a delay before a new token takes effect, referencing issue #30.
Copy the repository with the template button rather than forking it. The README is explicit that the two are not the same, because the template copy starts fresh without the large commit history. The secret is created through the repository settings, under Secrets and variables then Actions, with the name ACCESS_TOKEN.
Once the secret exists, trigger the workflow from the Actions tab. The project builds with Zig, and the README points to running the CLI locally when the numbers look wrong rather than documenting a local install for the average user.
When the run finishes, check the generated branch for overview.svg and languages.svg. In a README, reference them with the theme fragments the README itself uses:
<img src="https://github.com/jstrieb/github-stats/blob/generated/overview.svg#gh-dark-mode-only" />
<img src="https://github.com/jstrieb/github-stats/blob/generated/languages.svg#gh-dark-mode-only" />
<img src="https://github.com/jstrieb/github-stats/blob/generated/overview.svg#gh-light-mode-only" />
<img src="https://github.com/jstrieb/github-stats/blob/generated/languages.svg#gh-light-mode-only" />Substitute your own user and repository in those URLs. If the images do not appear, the first thing to check is whether the generated branch exists and contains both files.
Where the numbers go wrong, and when to use something else
The README keeps a disclaimer section, and it is unusually candid. Lines of code modified can be too high or too low. GitHub counts changes to files like package-lock.json, which inflates the line count, and it refuses to count lines of code for repositories with more than 10,000 commits, so contributions to those do not appear at all. The project's own fallback computation likely under-counts relative to GitHub's, because GitHub correctly attributes authorship for contributions to pull requests with several authors that end up squashed and merged by one author, and it correctly attributes commits made with old email addresses no longer connected to the account.
View counts are another weak spot. Repository view statistics often seem too low, and many referring sites are not captured. If you lack permission to read the view count for a repository, it is tallied as zero, which the README says is common for external repositories where your only contribution is a pull request.
Coverage has a hard boundary: only repositories with commit contributions are counted. Opening an issue on a repository does not make it show up. Repositories you created and own may not be counted if you never commit to them, or if the committer email is not connected to your GitHub account.
The practical consequence is that this tool is the wrong choice if you need numbers you can defend as exact. It is a visualization of an approximate signal. The README's own advice for strange numbers is to run the CLI locally and dump JSON output to find which repositories are skewing the result. That is a debugging step, not a fix.
How it differs from hosted stats card services
The obvious alternative is a hosted stats card service, the kind you embed with a single image URL and no repository of your own. The difference is not cosmetic. A hosted service runs the analysis on its infrastructure, so it can only see what its own token can see, which means public data. It cannot include your private repositories unless you hand it a token, and it typically cannot clone repositories on your behalf to tally lines changed.
jstrieb/github-stats inverts that trade. You own the token, the workflow and the compute. That buys private-repository coverage and a raw JSON dump you can analyze yourself. It costs you a repository, a classic token with repo scope, and Actions minutes for every regeneration. The token is the part to think about: repo scope is broad, and it lives as a repository secret in a repository you control.
A second alternative is to write the analysis yourself against the GitHub API. The README points out that the API endpoint for contributor statistics no longer works reliably, so a from-scratch implementation would face the same fallback problem this project already solved by cloning repositories and tallying with git. Rebuilding that is not a weekend task.
Maintenance, licence and what an upgrade costs you
The repository is not archived, and the last push was on 2026-09-23. The most recent release is 2.0.1 from 2026-04-25, following 2.0.0 on 2026-04-20 and three release candidates in April 2026. The 2.0 line is recent, and the release history shows a release candidate phase rather than a single jump.
Because the project is a template you copy rather than a dependency you install, upgrades are manual. Your copy does not track upstream. To pick up a new version you either pull the changes into your copy or take a fresh template copy and re-add the ACCESS_TOKEN secret. That is the real maintenance cost, and it is easy to underestimate: the longer your copy diverges, the more work the merge becomes.
The licence is GPL-3.0, as stated in the repository. That matters if you plan to redistribute a modified version or embed the code in another product, because the GPL carries obligations that permissive licences do not. If your use is a personal profile README, the question is mostly academic. If you intend to build a service on top of the code, read the licence text and consider advice from someone qualified to give it. This article is not that advice.
Editorial conclusion
Adopt jstrieb/github-stats if you want stats cards that include private repositories and you are willing to keep a classic token with repo scope inside a repository you control, plus the Actions minutes each regeneration costs. Do not adopt it if you only want a hosted card with no token and no workflow, or if your profile depends on repositories with more than 10,000 commits, where GitHub refuses to count lines of code. Before you commit, verify three things: that your copy of the repository runs the workflow to completion, that the generated overview.svg and languages.svg match what you expect for a repository you know well, and that the JSON dump attributes your commits to the email address connected to the account.
Frequently asked questions
How do I add jstrieb/github-stats to my GitHub profile README?
Copy the repository using the template button, create a classic personal access token with read:user, user:email and repo permissions, and store it as a repository secret named ACCESS_TOKEN. Run the Actions workflow, then reference the generated overview.svg and languages.svg from the generated branch in your README using the light and dark mode fragments.
How do I get GitHub stats that include private repositories?
The project runs the analysis inside your own GitHub Actions workflow, so it can use your personal access token to read private repositories that an external service could not access. The token needs repo scope for this to work.
Why are my jstrieb/github-stats numbers wrong?
The README lists several causes: GitHub counts changes to files like package-lock.json, refuses to count lines for repositories with more than 10,000 commits, and view counts are tallied as zero when you lack permission to read them. Only repositories with commit contributions are counted at all. The README suggests running the CLI locally and dumping JSON to find which repositories skew the results.
What token permissions does jstrieb/github-stats need?
A classic personal access token with read:user, user:email and repo permissions. The README states that user:email is needed to attribute commits correctly when repositories are cloned locally to compute lines of code changed.
Does jstrieb/github-stats run on macOS or Windows?
The project is designed to run on GitHub Actions, so no local machine is required for regular regeneration. The README mentions running the CLI locally to debug skewed statistics, and the repository is built with Zig, but the README does not document a local install procedure for macOS or Windows.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jstrieb-github-stats)