VulCode

ghapi

Our own GitHub statistics API powering dualfroz.com, vulcode.com and profile READMEs: raw JSON and SVG cards with commits, languages, frameworks, tests, streaks and open source contributions. The collector holding the GitHub token and the API that only reads ready snapshots never share secrets, and a Discord app answers the same questions from any chat.

TypeStatistics API
LanguagePHP 8.2, no framework
SourceGitHub GraphQL and REST
OutputJSON, SVG cards
ghapi

Overview

ghapi is our own GitHub statistics API at ghapi.dualfroz.com. It powers dualfroz.com, vulcode.com and the profile READMEs on GitHub: it serves raw JSON and ready SVG cards, plus a Discord app that answers the same questions from any chat.

We wrote it in plain PHP 8.2, with no framework and no extensions beyond the default build, so it runs on an ordinary PHP-FPM.

Two halves that share no secrets

Collector

run hourly by a systemd timer, it alone holds the GitHub token and writes ready snapshots.

API

reads only the snapshots and never calls GitHub, so traffic on the sites never turns into requests to GitHub.

No repository is kept on disk. The collector talks to the GitHub API: a GraphQL query lists the repositories of the configured owners with their branches, heads, stars and languages. A full refresh happens once a day, runs in between only finish the commit details that did not fit into the rate limit, and after every full refresh a summary goes to a Discord webhook.

How it counts commits

An unchanged branch head costs nothing. A moved one is compared with the previous head through the compare API, and a new branch against the default branch. That way commits pushed late or with a backdated date count too, which a date-based query would miss.

When a compare shows the old head as behind, meaning history was rewritten, the collector walks the repository again and drops commits that can no longer be reached. It recognises its own commits by the author's email, skips merges, and fetches every new commit's file list once, so the language, test and file kind numbers are exact line counts. When the default branch moves, its code archive is unpacked briefly into a temporary directory where tokei counts living code and a manifest scanner finds frameworks, and then the directory is deleted.

Public and private data

Everything under /v2/public is open, has CORS enabled and never contains private repository names. The same paths without public require a key and add repository names and per-repository data. Every response sends an ETag and answers 304 to If-None-Match, and the old first-version paths keep working.

What the API returns

EndpointWhat it returns
summaryTotals, streak, top languages and frameworks, tests, commit size and open source
commitsCommits per year, month, week, day, hour and weekday
locLines added and removed over time, living code and file kinds
languages, frameworks, testsLanguages with their share, frameworks from manifests and test lines against code
opensourcePull requests to public projects and own public repositories
cardsSVG cards in a dark and a light theme

Cards and Discord

The SVG cards show stats, top languages, the streak, activity, a heatmap, lines of code, a split by year, top repositories, an hourly punchcard, achievements, open source, frameworks and working hours. The Discord card reads the profile and presence written by the VulCode server bot, and the Discord app answers questions about the stats from any chat.

Questions about ghapi

More projects

More work from the same category - see how we tackle similar challenges.

Have a similar project?

Get in touch - a quote is free and comes back within an hour.