This year GitHub limited the stargazers listing to repo admins and collaborators. Star history tools had relied on that endpoint, so they had to find another way to get their data.
In September GitHub added a replacement built for exactly this: a star history endpoint. I built a README chart generator on it. Here is what it returns and what I ran into.
The endpoint
GET /repos/{owner}/{repo}/stargazers/history
It returns stars grouped by calendar week, newest first:
[{ "week": 1754784000, "total": 19, "days": [0, 12, 7, 0, 0, 0, 0] }]
- It works for any public repo, without authentication.
- Pages hold up to 30 weeks, and you can request up to 100 pages.
-
daysis stars per day of that week, starting on Sunday. - It follows renamed repos.
facebook/reactreturns the history ofreact/react.
I checked it against live star counts on seven repos, including vercel/next.js and torvalds/linux. The weekly totals matched the star count exactly in every case.
Fetching it
Work out how many pages the repo needs from its age and fetch them in parallel:
const weeksOld = Math.ceil((Date.now() - new Date(created_at).getTime()) / (7 * 864e5)) + 2;
const pages = Math.min(100, Math.ceil(weeksOld / 30));
const results = await Promise.all(
Array.from({ length: pages }, async (_, i) => {
const res = await fetch(
`https://api.github.com/repos/${repo}/stargazers/history?per_page=30&page=${i + 1}`,
{ headers: { Accept: 'application/vnd.github+json', 'X-GitHub-Api-Version': '2026-03-10' } }
);
return res.json();
})
);
// newest first, so sort ascending and accumulate
const weeks = results.flat().sort((a, b) => a.week - b.week);
A running sum gives the chart, and I thin it to about 120 points. Results are cached for an hour.
What did not work
Before I found this endpoint I tried the public GH Archive data. It records star events by repo name instead of repo id, so renamed repos lose their old history. vuejs/core showed 51% of its stars because the older ones sat under vuejs/vue-next. I wrote a layer to find old names and verify them against the repo id, then deleted all of it when the official endpoint worked.
Why SVG
The chart has to render inside a GitHub README, where scripts do not run. So the chart is a Svelte component, and the /owner/repo.svg route renders it on the server:
import { render } from 'svelte/server';
import StarGraphSVG from '$lib/components/StarGraphSVG.svelte';
const { html } = render(StarGraphSVG, { props: { meta, history, theme, chartStyle } });
return new Response(html, { headers: { 'Content-Type': 'image/svg+xml' } });
The animation is plain CSS keyframes inside the SVG: a line drawn with stroke-dashoffset and a fill that fades in. Setting pathLength="1000" on the path keeps the dash exact whatever the real length is.
Having it as a component made styles cheap. There are eight looks, including an ASCII one made from block characters and one where a small rocket rides the curve.
Social images
Open Graph needs PNG, and resvg supports neither CSS variables nor animation. So the social card is a separate plain SVG with literal colours, rendered to PNG with @resvg/resvg-js and fonts bundled from npm.
Try it
- Site: starhistory.link
- Source: github.com/YusufCeng1z/star-history
Have you built anything on the star history endpoint? I would like to compare notes.
Top comments (0)