Stars, forks, commits, languages, contributors, releases, branches, tags, issues and pull requests, all as plain JSON. There's a WebSocket on the same address too, in case you'd rather have updates pushed to you instead of asking for them.
Try it • Endpoints • Self host • Rate limits • Contributing • Discord
For whatever you build, from nullCats™
You can start using Arove right now, this second, without installing anything.
https://api.arove.workers.dev
That's really the whole setup for most people. Drop in any public GitHub owner and repo name and you get data back. Keep reading to see it in action, or jump straight to self hosting if you'd rather run your own copy with your own rate limits.
curl https://api.arove.workers.dev/v1/repo/vercel/next.js{
"repo": {
"owner": "vercel",
"name": "next.js",
"fullName": "vercel/next.js",
"ownerAvatarUrl": "https://avatars.githubusercontent.com/u/14985020?v=4",
"ownerType": "Organization"
},
"stats": {
"stars": 132000,
"forks": 27200,
"openIssues": 2481,
"defaultBranch": "canary",
"license": "MIT",
"description": "The React Framework"
},
"languages": {
"TypeScript": { "bytes": 9431201, "percentage": 91.2 },
"JavaScript": { "bytes": 612044, "percentage": 5.9 }
},
"latestCommits": [
{
"shortSha": "c3bdfff",
"message": "fix: patch update for edge runtime",
"authorLogin": "someone",
"committedAt": "2026-09-04T09:52:24Z",
"additions": 101,
"deletions": 111,
"filesChanged": 12,
"branches": ["main"]
}
],
"topContributors": [
{
"login": "someone",
"contributions": 128,
"lastCommitMessage": "fix: patch update for edge runtime"
}
],
"health": {
"hasReadme": true,
"hasLicense": true,
"daysSinceLastCommit": 0
}
}That's trimmed down for readability, and the numbers are just illustrative rather than something pulled live for this page. The actual response includes full contributor lists, release info, and everything else described below. You don't need to register a repo before asking about it either, an unregistered one just gets fetched fresh on the spot instead of pulled from stored history.
Want updates pushed to you instead of asking again and again? Open that same address as a WebSocket instead of a plain request and you'll get events as they happen.
Tip
Join our Discord server for updates on the project, help with setup, and support from the community and the nullCats™ team.
| Endpoint | What it does |
|---|---|
GET /v1/repo/:owner/:name |
Full snapshot, works whether or not the repo is registered |
WS /v1/repo/:owner/:name |
Live updates on that exact same address |
GET /v1/repo/:owner/:name/commits |
Commit history |
GET /v1/repo/:owner/:name/branches |
Branch list |
GET /v1/repo/:owner/:name/tags |
Tag list |
GET /v1/repo/:owner/:name/issues |
Issue list |
GET /v1/repo/:owner/:name/pulls |
Pull request list |
GET /v1/repo/:owner/:name/languages |
Language breakdown |
GET /v1/repo/:owner/:name/contributors |
Top contributors |
GET /v1/repo/:owner/:name/releases |
Release history |
GET /v1/repo/:owner/:name/badge |
An embeddable stat badge for your own README |
POST /v1/repo/:owner/:name/register |
Start tracking history and enable webhooks |
GET /v1/repos?repos=a/b,c/d |
Batch lookup, up to 20 repos in one call |
POST /v1/keys |
Free, self serve API key for a higher rate limit |
GET /v1/keys/usage |
Check a key's own creation date, last use, and request count |
GET /v1/health |
Status of D1, Postgres and your GitHub tokens, with response times |
GET /v1/openapi.json |
Full OpenAPI spec |
Every one of these already works against api.arove.workers.dev, no setup needed on your end.
Since Arove exposes a badge endpoint, you can embed a live stat straight into your own project's README without touching an image editor.
Swap the owner and repo, pick a label of stars, forks, or issues, and pick whatever color fits your README. It updates on its own every time someone loads the page.
This one's kept deliberately light on purpose, it only ever asks GitHub for the single number it needs, not the full snapshot, so it stays fast even embedded somewhere that gets loaded a lot.
Most GitHub stat widgets lock you into one look and one host. Arove hands you raw data instead of a rendered widget, so your portfolio, dashboard, or README badge can end up looking like whatever you actually want it to look like. It borrows the "one URL, no config" feel of Lanyard, the Discord presence API, and points that same idea at GitHub instead.
You genuinely don't have to. The public instance is free, it isn't going anywhere, and most people building a dashboard or a portfolio widget can just point at api.arove.workers.dev and never think about this section again.
But maybe you want your own rate limits, your own registered repos, or you just like owning the whole stack. Fair enough, here's how.
git clone https://github.com/nullcatsHQ/arove.git
cd arove
npm installThen, in order:
1. Database. Create a D1 database through your Cloudflare dashboard, then run src/db/schema.sql against it through the query console. That's everything a fresh database needs. If you're upgrading a copy that was already running before webhooks and API keys existed, also run src/db/upgrade.sql once, it adds the couple of pieces that were missing.
2. Postgres. Arove keeps its rate limit counters, its response cache, and a few coordination flags in a Postgres database. Neon's free plan is plenty for this. Here's the whole setup:
- Go to neon.tech, sign up, and open the console.
- Create a new project. Call it whatever you like, pick the region closest to where your Worker mostly runs, and leave the Postgres version on the default.
- Open the project and press Connect. Switch connection pooling on, then copy the connection string. The hostname should contain
-poolerand the string should end insslmode=require. - Open the SQL Editor from the sidebar, paste in everything from
src/db/postgres.sql, and run it. That creates two tables,countersandcache. Running the file a second time drops and rebuilds both, which only resets rate limits and cached data, nothing you'd miss.
3. Config. Open wrangler.toml and drop your D1 database ID into the binding block near the top.
4. Secrets. Add your Postgres connection string as a secret named DATABASE_URL. From the terminal:
npx wrangler secret put DATABASE_URLOr in the dashboard, open your Worker, go to Settings, then Variables and Secrets, add a new one, set the type to Secret, name it DATABASE_URL, and paste the string in. For local development, copy .dev.vars.example to .dev.vars and fill it in there.
5. GitHub token. Generate a personal access token, no scopes needed for public repo data, and add it as a secret named GITHUB_TOKEN. Want a higher rate limit? Add more as GITHUB_TOKEN_2, GITHUB_TOKEN_3, and so on, then set TOKEN_COUNT in wrangler.toml to match. One token is fine too, this is optional.
6. Scheduled job. A polling schedule is already defined in wrangler.toml under triggers, running once a minute. Confirm it shows up in your dashboard after deploying.
7. Deploy. Use npx wrangler deploy, or connect the repository for continuous deployment through the dashboard.
Once it's live, confirm everything's working:
curl https://your-deployment.example/v1/health
curl https://your-deployment.example/v1/repo/vercel/next.js
curl -X POST https://your-deployment.example/v1/repo/vercel/next.js/registerThe health check should report everything healthy, the snapshot should come back immediately, and registering should hand you back a webhook URL and secret.
Note
Neon's free plan includes 100 compute hours per project each month, and the compute only goes to sleep after five minutes without a query. Arove talks to it constantly, so it will rarely sleep, and on a busy instance the allowance can run out before the month does. That's roughly 400 hours at the smallest compute size, so a little over two weeks of nonstop use. If it happens, Arove keeps answering. Rate limits and cache lookups get skipped and requests go straight to GitHub until your allowance resets or you move to a paid plan.
Caution
Whatever webhook secret or API key you get back from any endpoint is shown exactly once and cannot be retrieved again. Save it the moment you see it. If you lose one, the fix is regenerating a new one, not recovering the old one.
Registering a repo gets you scheduled polling by default, so updates land within a minute or two. If you'd rather a specific repo update the moment something actually happens, grab the webhook URL and secret from the register response, or from POST /v1/repo/:owner/:name/webhook if you registered earlier and lost it, then add it under that repo's webhook settings on GitHub. Works the same whether you're on the public instance or your own.
If you're self hosting, Arove can round robin across several GitHub tokens so your effective rate limit budget multiplies with however many you're running. Set a token count in your config, add that many tokens as secrets, and Arove handles the rest, including skipping any token that's currently rate limited until it resets on its own.
Tip
One token is enough for casual use. This only really matters if you're registering a lot of repos or expecting heavy traffic.
Every database call goes through src/db, and every cache and counter call goes through src/cache and src/state. Those last two reach Postgres through one small file, src/lib/postgres.ts. Nothing else touches storage directly. Want a different database, or Redis for the counters? See STORAGE.md for the full function list to reimplement, plus a couple of worked examples showing the actual shape of the swap.
The repo pins Node 22 in .nvmrc, so if you use nvm, nvm use gets you the right version.
nvm use
npm install
cp .dev.vars.example .dev.vars
npm run devFill in .dev.vars with your Postgres connection string and a GitHub token first. .env.example has the same values if your tooling prefers an .env file instead.
| Command | What it does |
|---|---|
npm run typecheck |
TypeScript check with no output files |
npm run lint |
ESLint over the whole project |
npm run lint:fix |
Same, and fixes whatever it safely can |
npm run format |
Prettier rewrites files to the house style |
npm run format:check |
Prettier only reports, changes nothing |
Formatting rules live in .prettierrc.json and .editorconfig, so most editors pick them up on their own. GitHub runs the type check on every push and pull request, see .github/workflows/ci.yml.
Anonymous requests get a modest rate limit per address. If you're building something that calls Arove a lot, get a free key instead.
curl -X POST https://api.arove.workers.dev/v1/keysThat hands you back a key on the spot, no email, no waiting. Use it as a bearer token and your limit goes up substantially. The exact numbers aren't published here on purpose, check the X-RateLimit-Remaining header on any response if you want to know exactly where you stand.
Want to check how much a key's actually been used, or when it was made? Same bearer token, different endpoint.
curl -H "Authorization: Bearer your-key-here" https://api.arove.workers.dev/v1/keys/usageNo accounts anywhere in this system, so this is also the only way to look a key up at all. There's no dashboard sitting behind it, just the token in your hand.
One thing worth actually knowing, a key that sits completely unused for 180 days quietly stops working on its own. Nothing personal, it's just a cleanup pass for keys that got forgotten somewhere, not a punishment for anything. If you're checking usage every so often anyway, you'll never come close to hitting it.
src/
index.ts entry point
routes/ HTTP and WebSocket handlers
jobs/ scheduled polling logic
github/ GitHub API client, token pool, data normalization
db/ D1 queries and the SQL schemas
cache/ response cache on top of Postgres
state/ rate limits, counters and cooldowns on Postgres
lib/ Postgres client and small helpers
middleware/ rate limiting and optional API key auth
types/ shared types
The dotfiles in the root (.editorconfig, .prettierrc.json, eslint.config.js, .nvmrc, .gitattributes) keep every contributor on the same formatting and Node version.
We spent literally $0 building this project. It runs on nothing but free tiers, and that includes the public instance you can already query above. This is a lesson for anyone who thinks they can't build something good without spending money. Everything is possible if you're willing to put in the work. At the end of the day, this has been a fun and rewarding experience for me (trmin) building this project <3
Pull requests are genuinely welcome. Open an issue first if you're planning something bigger than a small fix, just so nobody's work crosses paths with anyone else's. If you just want to ask a question or talk through an idea before writing any code, the Discord server is a faster way to reach us than an issue.
MIT, see LICENSE for the full text.
This entire project was built by a single developer (Trmin) at nullCats™. Made with 🖤 for cats
Copyright, all rights reserved, nullCats™
