Generates SVG cards showing a GitHub user's pull request history. Embed them in a profile README or anywhere that renders images.
Live: https://pullscape.fly.dev
GET https://pullscape.fly.dev/api/github-pr-stats?username=<github_username>
?username=fematarazzo&theme=dark&min_stars=50&sort=stars_desc&limit=10
?username=fematarazzo&theme=light&min_stars=50&sort=stars_desc&limit=10
?username=fematarazzo&theme=dark&mode=repo-aggregate&sort=merged_rate_desc&min_stars=50
| Parameter | Default | Description |
|---|---|---|
username |
required | GitHub username |
theme |
dark |
dark or light |
mode |
PR list | repo-aggregate to group by repository instead of listing individual PRs |
status |
all |
Filter by status: merged, upstream, open, draft, closed, or comma-separated |
min_stars |
0 |
Only include PRs from repos with at least this many stars |
limit |
10 |
Max rows to show |
sort |
status,stars_desc |
Sort fields, comma-separated (see below) |
fields |
all columns | Columns to include, comma-separated (see below) |
stats |
all |
Which summary stats to show in the header bar |
| Status | Meaning |
|---|---|
merged |
PR was directly merged on GitHub, or closed by a cherry-picked commit |
upstream |
PR was closed because a maintainer pulled the changes into their own PR or via an external tool (Gerrit, Phabricator) — contribution accepted but not directly merged |
open |
Still open |
draft |
Open as a draft |
closed |
Closed without the changes being accepted |
Both merged and upstream count toward the merged tally and merge rate.
PR list mode: stars_desc, stars_asc, created_date_desc, created_date_asc, status
Repo aggregate mode: stars_desc, stars_asc, merged_desc, merged_asc, merged_rate_desc, merged_rate_asc
PR list: repo, stars, pr_title, pr_number, status, created_date, merged_date
Repo aggregate: repo, stars, pr_numbers, total, merged, open, draft, closed, merged_rate
total_pr, merged_pr, display_pr, repos_with_pr, repos_with_merged_pr, showing_repos
https://pullscape.fly.dev/api/github-pr-stats?username=torvalds&theme=light&limit=5
https://pullscape.fly.dev/api/github-pr-stats?username=torvalds&status=merged&sort=stars_desc&limit=10
https://pullscape.fly.dev/api/github-pr-stats?username=torvalds&mode=repo-aggregate&sort=merged_rate_desc
https://pullscape.fly.dev/api/github-pr-stats?username=torvalds&fields=repo,stars,status&stats=total_pr,merged_pr
To embed in a GitHub README:
cp .env.example .env
# add your GitHub token to .env
go run .The server starts on port 8080 by default. Set PORT in .env to change it.
| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN |
yes | GitHub personal access token. Needs read:user scope. |
PORT |
no | Port to listen on. Defaults to 8080. |
UPSTASH_REDIS_REST_URL |
no | Upstash Redis REST endpoint. Enables persistent cache across restarts. |
UPSTASH_REDIS_REST_TOKEN |
no | Upstash Redis REST token. |
Without Redis the server falls back to an in-memory cache (wiped on restart).
Responses are cached for 1 hour. Cache lookup order:
- In-memory (
sync.Map) — fastest, per-process - Redis — survives restarts; first request after a cold start still returns instantly
- GitHub API — only on a full cache miss
The repo includes a Dockerfile and fly.toml for deploying to Fly.io.
fly apps create pullscape
fly secrets set GITHUB_TOKEN=your_token_here
fly deployTo add persistent Redis cache (recommended):
fly redis create
fly secrets set UPSTASH_REDIS_REST_URL=https://... UPSTASH_REDIS_REST_TOKEN=...