DEV Community

Cover image for Building README star charts on GitHub's new star history endpoint
Yusuf Cengiz
Yusuf Cengiz

Posted on

Building README star charts on GitHub's new star history endpoint

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
Enter fullscreen mode Exit fullscreen mode

It returns stars grouped by calendar week, newest first:

[{ "week": 1754784000, "total": 19, "days": [0, 12, 7, 0, 0, 0, 0] }]
Enter fullscreen mode Exit fullscreen mode
  • It works for any public repo, without authentication.
  • Pages hold up to 30 weeks, and you can request up to 100 pages.
  • days is stars per day of that week, starting on Sunday.
  • It follows renamed repos. facebook/react returns the history of react/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);
Enter fullscreen mode Exit fullscreen mode

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' } });
Enter fullscreen mode Exit fullscreen mode

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

Have you built anything on the star history endpoint? I would like to compare notes.

Top comments (0)