github-pretty-readme is a Node.js/Express API that generates dynamic, styled SVG graphics and Markdown content for GitHub profile READMEs. It integrates with the GitHub API to fetch user and repository data, uses AI for personalized summaries and recommendations, and can optionally pull typing statistics from Monkeytype. The generated content, including developer ratings, tech stack visualizations, and AI-driven insights, can be embedded directly into markdown files or used to update your GitHub profile repository.
The application relies on several environment variables for configuration and API keys.
| Name | Purpose | Required |
|---|---|---|
SESSION_SECRET |
Secret key used by express-session for signing the session ID cookie. |
Yes (has dev default) |
NODE_ENV |
Determines if session cookies should be secure (HTTPS only). Set to production for secure cookies. |
No (defaults to development) |
PORT or port |
The port on which the Express server will listen. | No (defaults to 8088) |
GOOGLE_AI_STUDIO_KEY |
API key for Google's Generative AI Studio (Gemini model) for AI-powered features. | Yes (for AI features) |
GITHUB_TOKEN |
GitHub Personal Access Token used for accessing GitHub API data without user authentication (fallback). | No (recommended for public API access) |
GITHUB_APP_CLIENT_ID |
Client ID for your GitHub OAuth App, used for user authentication. | Yes (for GitHub OAuth) |
GITHUB_APP_CLIENT_SECRET |
Client Secret for your GitHub OAuth App, used for user authentication. | Yes (for GitHub OAuth) |
BASE_URL |
The base URL for OAuth callbacks. Should match your deployed application URL. | No (defaults to http://localhost:8080) |
MONKEYTYPE_API_KEY |
Monkeytype API key for fetching typing statistics (fallback). | No (can be set per-session) |
MONKEYTYPE_USERNAME |
Monkeytype username (for linking, fallback). | No (can be set per-session) |
To run the server locally:
- Install dependencies:
npm install
- Set environment variables: Create a
.envfile in the project root with the necessary variables. - Start the server:
The server will start on the port specified in
npm start
PORTor8088by default.
To generate a set of example SVG tiles to the preview/ directory, configure preview.config.js and run:
npm run previewThis section describes the API endpoints, their purpose, accepted query parameters, and examples. Most SVG/data endpoints can be used without user authentication if GITHUB_TOKEN is set in your environment, otherwise, they require an authenticated user session. Endpoints that modify GitHub data always require an authenticated user session.
The full route list lives in api/_routes.js (the source of truth). Auth-gated endpoints accept either a signed-in session cookie or an Authorization: Bearer <token> header carrying a minted API token. The public SVG/data endpoints are rate-limited for anonymous traffic (default 60 requests / 60 s per IP+token; configurable via RATE_LIMIT_WINDOW_MS / RATE_LIMIT_MAX / RATE_LIMIT_DISABLED); authenticated sessions are exempt. Over-limit requests get a 429 with Retry-After and X-RateLimit-* headers. The single-page UI is served at the site root / (there is no /dashboard route). See docs/api.md for the complete reference.
These endpoints manage user authentication via GitHub OAuth.
Initiates the GitHub OAuth flow. Redirects the user to GitHub for authorization.
Query Parameters: None.
Example URL:
http://localhost:8088/auth/github
The callback URI for GitHub OAuth. Exchanges the authorization code for an access token and stores user information in the session.
Query Parameters:
| Name | Default | Description |
|---|---|---|
code |
The authorization code provided by GitHub. | |
state |
The OAuth state parameter for CSRF protection. |
Example URL:
http://localhost:8088/auth/callback?code=YOUR_CODE&state=YOUR_STATE
Destroys the user's session, effectively logging them out.
Query Parameters: None.
Example URL:
http://localhost:8088/auth/logout
Returns JSON data about the currently authenticated GitHub user. Requires an active session.
Query Parameters: None.
Example URL:
http://localhost:8088/auth/me
Long-lived tokens for headless automation (cron, CI). All require an authenticated session (you mint a token while signed in), then the token is sent as Authorization: Bearer <token> to any auth-gated endpoint. Tokens are stored only as SHA-256 hashes, are in-memory (do not survive a restart), and never expire -- revoke to rotate.
Mints a new API token. Optional JSON body { "label": "ci" }. Returns 201 { token, id, login, label, createdAt, lastUsedAt } -- the token is shown exactly once.
Lists the signed-in user's tokens as metadata (never the secret): { tokens: [...] }.
Revokes one of the signed-in user's tokens. 204 on success, 404 for an unknown id.
Manage the .pretty-readme.json allowlist in your profile repo. Both require an authenticated session.
Returns { config, exists } -- the parsed .pretty-readme.json, or null when absent.
Writes the allowlist. JSON body { "repos": ["repo-a", "repo-b"] }. Returns { ok: true }; 400 when repos is not an array.
Generates and pushes various SVG graphics and markdown content to the user's GitHub profile README repository ({username}/{username}). This includes a developer bio, rating, tech stack charts, language badges, and a detailed insights markdown file. Requires an active session with repo scope.
Query Parameters:
| Name | Default | Description |
|---|---|---|
dry_run |
false |
If true, the process runs without applying any changes to GitHub and returns the generated content. |
Example URL (Dry Run):
http://localhost:8088/apply-readme?dry_run=true
Generates (or returns cached) the full profile preview -- bio, all SVGs, opt-in account tiles, and the insights markdown -- as JSON. Requires an active session. Pass refresh=true to bypass the cache.
Example URL:
http://localhost:8088/preview-readme?refresh=true
Applies enabled features across selected repos: pushes SCORE.md / README.md via a per-repo pull request on a pretty-readme/YYYY-MM-DD branch, and applies topics/descriptions immediately. Skips repos whose HEAD SHA is unchanged since the last run (tracked in .pretty-readme-state.json). Supports Server-Sent Events when the request Accepts text/event-stream. Requires an active session (or a Bearer API token for cron).
Query Parameters:
| Name | Default | Description |
|---|---|---|
repos |
Comma-separated repo names, or * for all eligible repos. When absent, the .pretty-readme.json allowlist is required (cron mode). |
|
score |
false |
Generate and push SCORE.md per repo (via PR). |
readme |
false |
Generate and push README.md per repo (via PR). |
topics |
false |
Suggest and apply GitHub topics (immediately). |
descriptions |
false |
Suggest and apply descriptions (immediately). |
workflow |
false |
Push a daily pretty-readme-score.yml workflow per repo (via PR). |
Returns a lightweight JSON list of the authenticated user's repos (name, description, language, isProfile, stars, pushedAt), profile repo first. Requires an active session.
Scans a repo (file tree, key files, source samples), grades code quality across six dimensions, and returns the analysis (suggested topics, README outline, prioritised suggestions). Cached per user/repo for 4 hours. Requires an active session.
Query Parameters:
| Name | Default | Description |
|---|---|---|
repo |
Required. Repo name. | |
refresh |
false |
true forces a fresh scan, bypassing the cache. |
Generates a README preview for a single repo -- the rendered markdown plus the underlying analysis -- so the UI can preview before applying. Reuses the shared scan cache. Requires an active session.
Query Parameters:
| Name | Default | Description |
|---|---|---|
repo |
Required. repo or owner/repo. |
|
refresh |
false |
true forces a fresh scan. |
Pushes SCORE.md directly to a single target repo, optionally a generated README.md, and optionally a daily score workflow. Uses cached scan data when available. Requires an active session (or a Bearer token for cron).
Query Parameters:
| Name | Default | Description |
|---|---|---|
repo |
Required. repo or owner/repo. |
|
readme |
false |
Also generate and push README.md from the scan outline. |
workflow |
false |
Push .github/workflows/pretty-readme-score.yml (daily, 05:00 UTC) if absent. |
These endpoints manage the connection to the Monkeytype API.
Connects a Monkeytype API key to the user's session. This key is then used for GET /monkeytype and /apply-readme.
Request Body Parameters:
| Name | Default | Description |
|---|---|---|
api_key |
Your Monkeytype API key. | |
username |
null |
Your Monkeytype username (optional, for linking). |
Example (using curl):
curl -X POST -H "Content-Type: application/json" -d '{"api_key": "YOUR_API_KEY", "username": "YourMTUsername"}' http://localhost:8088/monkeytype/connectRemoves the Monkeytype API key and username from the user's session.
Request Body Parameters: None.
Example (using curl):
curl -X POST http://localhost:8088/monkeytype/disconnectThese endpoints connect a WakaTime API key to your session for GET /wakatime. They mirror the Monkeytype connect/disconnect endpoints.
Stores a WakaTime API key on the session.
Request Body Parameters:
| Name | Default | Description |
|---|---|---|
api_key |
Required. Your WakaTime API key. 400 when missing. |
Example (using curl):
curl -X POST -H "Content-Type: application/json" -d '{"api_key": "waka_..."}' http://localhost:8088/wakatime/connectRemoves the WakaTime API key from the session. The WAKATIME_API_KEY env fallback (if set) remains in effect.
These endpoints generate SVG images or JSON data based on GitHub and other API data.
Generates an SVG image displaying a developer account summary.
Query Parameters:
| Name | Default | Description |
|---|---|---|
username |
(Required if not authenticated) The GitHub username to summarize. | |
background |
null |
Applies a background theme: cherry-blossom, geometric, or vapor-wave. |
projects |
all |
Filters repositories: a number (e.g., 5 for top 5 by stars) or a comma-separated list of repo names. |
Example URLs:
http://localhost:8088/account-summary?username=octocat&background=cherry-blossom
http://localhost:8088/account-summary?username=octocat&projects=top5
http://localhost:8088/account-summary?username=octocat&projects=my-repo-1,my-repo-2
Generates a plain text developer bio summary based on the authenticated user's repositories.
Query Parameters: None.
Example URL:
http://localhost:8088/account-summary-md
Generates an SVG image displaying the developer rating for the authenticated user (or user associated with GITHUB_TOKEN).
Query Parameters: None.
Example URL:
http://localhost:8088/developer-rating
Generates a detailed Markdown report with developer rating insights and actionable recommendations for the authenticated user (or user associated with GITHUB_TOKEN).
Query Parameters: None.
Example URL:
http://localhost:8088/developer-rating-insights
Generates an SVG contribution heatmap with current-streak, longest-streak, and total-contribution figures, sourced from the GitHub GraphQL contribution calendar.
Query Parameters:
| Name | Default | Description |
|---|---|---|
username |
GitHub username (falls back to the session user). | |
background |
null |
Theme: cherry-blossom, geometric, or vapor-wave (alias: theme). |
Example URL:
http://localhost:8088/contribution-graph?username=octocat&background=geometric
Generates an SVG stats card -- total stars, commits, pull requests, issues, followers, and repositories contributed to -- aggregated from the GitHub GraphQL API.
Query Parameters:
| Name | Default | Description |
|---|---|---|
username |
GitHub username (falls back to the session user). |
Example URL:
http://localhost:8088/stats-card?username=octocat
Generates an SVG image displaying a summary of the top technologies used by a user, represented by icons.
Query Parameters:
| Name | Default | Description |
|---|---|---|
background |
null |
Applies a background theme: cherry-blossom, geometric, or vapor-wave. |
limit |
all |
Maximum number of technology icons to display. |
sort |
frequency |
Sort order for technologies: frequency (most used) or alpha (alphabetical). |
exclude |
null |
Comma-separated list of technology names to exclude from the summary (e.g., HTML,CSS). |
Example URLs:
http://localhost:8088/tech-summary?background=geometric&limit=10&sort=frequency
http://localhost:8088/tech-summary?background=vapor-wave&exclude=JavaScript,HTML
Returns a JSON list of technologies used by the authenticated user (or user associated with GITHUB_TOKEN), including counts and icon metadata.
Query Parameters:
| Name | Default | Description |
|---|---|---|
sort |
frequency |
Sort order for technologies: frequency (most used) or alpha (alphabetical). |
exclude |
null |
Comma-separated list of technology names to exclude. |
Example URLs:
http://localhost:8088/tech-list?sort=alpha
http://localhost:8088/tech-list?exclude=CSS,Markdown
Generates an SVG chart visualizing programming language usage.
Query Parameters:
| Name | Default | Description |
|---|---|---|
chart |
donut |
Type of chart: donut (pie chart) or spider (radar chart). |
Example URLs:
http://localhost:8088/tech-chart?chart=donut
http://localhost:8088/tech-chart?chart=spider
Generates a technology visualization SVG. This versatile endpoint can render spider charts, treemaps, or a grid of technology cards.
Query Parameters:
| Name | Default | Description |
|---|---|---|
type |
spider |
Visualization type: spider, treemap, cards, or grid. |
categories |
languages,frameworks,cloud |
Comma-separated list of tech category keys (e.g., languages,frameworks,ai). |
limit |
6 |
Maximum number of technologies to display per category. |
exclude |
null |
Comma-separated list of technology names to exclude. |
columns |
2 |
(Only for type=grid) Number of columns in the grid layout (max 4). |
title |
TECH RADAR (for spider type) |
Custom title for the visualization (not all types use this parameter). |
Example URLs:
http://localhost:8088/tech-spider?type=spider&categories=languages,databases,devops&limit=5
http://localhost:8088/tech-spider?type=treemap&categories=languages,frameworks,cloud&limit=8
http://localhost:8088/tech-spider?type=cards&categories=languages,frameworks,ai,databases&limit=12
http://localhost:8088/tech-spider?type=grid&categories=languages,frameworks,cloud,ai&columns=2
Returns a JSON list of technology categories that have at least one detected technology for the authenticated user (or user associated with GITHUB_TOKEN).
Query Parameters:
| Name | Default | Description |
|---|---|---|
limit |
8 |
Maximum number of technologies per category to consider. |
Example URL:
http://localhost:8088/tech-categories?limit=5
Uses AI to suggest and apply GitHub topics to repositories that have fewer than MIN_TOPICS (currently 3) topics, are not archived, and are not forks. Requires an active session with repo scope.
Query Parameters:
| Name | Default | Description |
|---|---|---|
dry_run |
false |
If true, the process runs without applying any changes to GitHub and returns the suggested topics. |
Example URL (Dry Run):
http://localhost:8088/improve-topics?dry_run=true
Uses AI to suggest and apply descriptions to owned GitHub repositories that currently lack them, are not archived, and are not forks. Requires an active session with repo scope.
Query Parameters:
| Name | Default | Description |
|---|---|---|
dry_run |
false |
If true, the process runs without applying any changes to GitHub and returns the suggested descriptions. |
Example URL (Dry Run):
http://localhost:8088/improve-descriptions?dry_run=true
Generates an SVG tile of the languages a user has worked in recently, weighted by commit activity inside the window (not lifetime repo bytes). Cached for 1 hour per (username, days). Always responds with an SVG — a missing user or any error renders a graceful empty tile rather than a 4xx/5xx body.
Query Parameters:
| Name | Default | Description |
|---|---|---|
username |
The GitHub login whose recent activity is charted (required; absent renders an empty tile). | |
days |
90 |
Recent window in days. Non-numeric or <= 0 falls back to 90; capped at 365. |
background |
null |
Theme: cherry-blossom, geometric, or vapor-wave. |
Example URL:
http://localhost:8088/active-languages?username=octocat&days=180&background=geometric
Renders a 7×24 (day-of-week × hour-of-day) heatmap SVG of a user's coding activity, derived from GitHub public-event timestamps, with the busiest day and hour labelled. Falls back to a graceful empty tile when no data is available.
Query Parameters:
| Name | Default | Description |
|---|---|---|
username |
GitHub login (required; absent renders an empty tile). | |
background |
null |
Theme: cherry-blossom, geometric, or vapor-wave. |
Example URL:
http://localhost:8088/activity-clock?username=octocat&background=vapor-wave
Generates an SVG chart visualizing Monkeytype typing speed personal bests across different time modes. Requires an active session with a connected Monkeytype API key or MONKEYTYPE_API_KEY set in the environment.
Query Parameters: None.
Example URL:
http://localhost:8088/monkeytype
Generates an SVG chart of coding time by language from the WakaTime API. Resolves the key from the session (POST /wakatime/connect) or the WAKATIME_API_KEY env var. 401 when no key is connected; 404 when the range has no data.
Query Parameters:
| Name | Default | Description |
|---|---|---|
range |
last_7_days |
last_7_days, last_30_days, last_6_months, last_year, or all_time. |
Example URL:
http://localhost:8088/wakatime?range=last_30_days
Approximates how a user's language usage has grown over time by bucketing each repository's language bytes by the repo's creation year and rendering a cumulative stacked-area chart. Reads the session GitHub token when present, else GITHUB_TOKEN. Cached for 15 minutes per user. 401 when no GitHub token is available.
Query Parameters:
| Name | Default | Description |
|---|---|---|
repos |
40 |
Cap on the number of (largest, non-fork) repos scanned for /languages (min 1). |
limit |
6 |
Max languages shown as stacked series (min 1). |
username |
env |
Cache key when unauthenticated (the session user takes precedence when signed in). |
Example URL:
http://localhost:8088/language-trend?repos=60&limit=8
Renders a row of brand badges linking to a user's social profiles. Links come (in order) from the ?links= query param, or — when authenticated — the social map in the user's .pretty-readme.json. Unknown platforms degrade to a generic link badge.
Query Parameters:
| Name | Default | Description |
|---|---|---|
links |
Comma-separated platform:handle pairs, e.g. github:octocat,email:me@example.com. Overrides config when present. Only the first colon splits each pair, so handles may be full URLs. |
|
username |
When unauthenticated and links is omitted, the login whose config social map is read (requires GITHUB_TOKEN). |
Example URL:
http://localhost:8088/social-links?links=github:octocat,linkedin:octocat,email:octocat@github.com
Returns a Markdown (plain-text) row of shields.io tech badges derived from a repo's primary language and topics. 200 with an empty body when no tech is detected; 400 for a missing/malformed repo; 404 when not found; 502 on an upstream error.
Query Parameters:
| Name | Default | Description |
|---|---|---|
repo |
Required. owner/name. |
Example URL:
http://localhost:8088/repo-tech-badges?repo=octocat/Hello-World
Renders a theme-aware SVG card for a single repo with its stars, forks, open issues, primary language, and last-updated time. 400 for a missing/malformed repo; 404 when not found; 502 on an upstream error.
Query Parameters:
| Name | Default | Description |
|---|---|---|
repo |
Required. owner/name. |
Example URL:
http://localhost:8088/repo-card?repo=octocat/Hello-World
Renders a repository's weekly commit activity for the last year as a themed SVG bar chart. Always responds with an SVG — error and empty states render as SVG cards so the image never breaks in a README.
Query Parameters:
| Name | Default | Description |
|---|---|---|
repo |
Required. owner/name, or a bare name combined with user=. |
|
user |
Owner login, used when repo is a bare name. |
Example URLs:
http://localhost:8088/repo-activity?repo=octocat/Hello-World
http://localhost:8088/repo-activity?user=octocat&repo=Hello-World
Renders a grid of repo cards for a user's most notable repositories (reusing the repo-card renderer). Forks are excluded by default; an empty selection renders a graceful placeholder. 400 for a missing username; 502 on an upstream error.
Query Parameters:
| Name | Default | Description |
|---|---|---|
username |
Required. GitHub login. | |
sort |
stars |
stars or updated (most recently pushed). |
limit |
6 |
Number of cards (clamped to 1–12). |
columns |
2 |
Grid columns. |
forks |
false |
true/1 includes forks. |
Example URL:
http://localhost:8088/top-repos?username=octocat&sort=updated&limit=8&columns=3
Liveness/health probe. Unauthenticated, dependency-free, and never rate-limited (useful for Cloud Run health checks). Returns 200 { "status": "ok", "version": "<package version>" }.
Example URL:
http://localhost:8088/healthz
github-pretty-readme can run on a daily schedule to keep your profile and repositories up to date. The workflow below calls the hosted service, reads your .pretty-readme.json allowlist, and for each repo:
- Fetches the current HEAD commit SHA and compares it against
.pretty-readme-state.json(stored in your profile repo). - Skips the repo entirely if no commits have landed since the last run.
- Runs and updates the open PR if new commits are detected, or creates a new PR if this is the first run.
This means the daily cron is cheap — only repos that actually changed trigger AI calls and GitHub writes.
Add .pretty-readme.json to your profile repository ({username}/{username}):
{
"repos": [
"my-project",
"another-repo",
"cool-cli"
]
}Only repos in this list will be touched. You can also manage this file through the app UI at GET /config / PUT /config.
The same .pretty-readme.json can enable extra account tiles in your profile
README. All tiles are off by default — add a tiles block and set the ones
you want to true:
{
"repos": ["my-project"],
"tiles": {
"contributionGraph": true,
"statsCard": true,
"languageTrend": true,
"socialLinks": true
},
"social": {
"github": "octocat",
"linkedin": "octocat",
"email": "octocat@github.com"
}
}| Tile id | Renders |
|---|---|
contributionGraph |
Contribution heatmap + current/longest streak (/contribution-graph). |
statsCard |
Stars / commits / PRs / issues / followers / contributed-to card (/stats-card). |
languageTrend |
Cumulative language-usage-over-time stacked area chart (/language-trend). |
socialLinks |
Brand badges linking to your social profiles, built from the social map below (/social-links). |
activeLanguages |
Languages worked in recently, weighted by commit activity (/active-languages). |
topRepos |
A grid of repo cards for your most notable repositories (/top-repos). |
activityClock |
A 7×24 day-by-hour heatmap of your coding activity (/activity-clock). |
wakatime |
Coding time by language from WakaTime — requires a connected WakaTime key (/wakatime). |
Each tile is also a live, standalone, rate-limited GET endpoint (linked above) you
can embed directly; the opt-in tiles block just renders the same output into your
profile README at apply time.
The socialLinks tile reads the top-level social map — platform: handle
pairs (e.g. github, twitter/x, linkedin, devto, mastodon, email,
website). Unknown platforms degrade to a generic link badge.
Each enabled tile is pushed to assets/ and injected into your README between
its markers (e.g. <!-- contribution-start --> / <!-- contribution-end -->)
during GET /apply-readme. The GET /preview-readme response includes the
rendered tiles under accountTiles, and GET /apply-readme?dry_run=true lists
the enabled tile ids under tiles.
Create .github/workflows/pretty-readme.yml in your profile repository:
name: pretty-readme daily update
on:
schedule:
- cron: "0 5 * * *" # 05:00 UTC every day
workflow_dispatch: # allow manual runs from the Actions tab
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Trigger full update
run: |
curl -fsSL \
-H "Authorization: Bearer ${{ secrets.PRETTY_README_TOKEN }}" \
"${{ secrets.PRETTY_README_URL }}/apply-all?score=true&readme=true&topics=true&descriptions=true"| Secret | Value |
|---|---|
PRETTY_README_URL |
Your deployed service URL, e.g. https://github-pretty-readme-xxx-uc.a.run.app |
PRETTY_README_TOKEN |
A long-lived API token. Sign in to your instance, then POST /tokens to mint one (returned once) — see self-hosting. Revoke any time with DELETE /tokens/:id. |
Tip: API tokens do not expire — mint one and store it as the
PRETTY_README_TOKENsecret. They are revocable, so rotate by minting a new token and deleting the old.Deprecated: the previous
PRETTY_README_SESSIONcookie path is no longer recommended. A copied session cookie still works until it expires (7 days), but the convenientAuthorization: Bearer <any-value>shortcut that some setups relied on has been removed — the service now verifies the Bearer token against its token store. Switch to a mintedPRETTY_README_TOKEN.
| Step | How it lands |
|---|---|
| Account summary, developer rating, tech charts | Pushed directly to your profile README |
| SCORE.md + README.md per repo | Opened as a pull request on a pretty-readme/YYYY-MM-DD branch |
| Topics + descriptions | Applied immediately via GitHub API (reversible from the repo settings page) |
| Run state | Written to .pretty-readme-state.json in your profile repo after each run |
Repos with no new commits since the last run are skipped automatically — no AI calls, no GitHub writes.
The service is deployed to Google Cloud Run. Pushes to main trigger the production deploy via GitHub Actions.