DEV Community

Uya
Uya

Posted on • Originally published at zenn.dev

Self-Host Your GitHub Stats Badge on Vercel — Fixing the 'Broken Image' on Your Profile README

🌐 This is the English version of an article I originally wrote in Japanese on Zenn. The original is linked as the canonical source.

Introduction

You've probably seen that "GitHub Stats" card (github-readme-stats) on a lot of profile READMEs. Ever pasted it in, only to get a broken image? I have.

I touched on this problem in my previous article, but the fix — self-hosting — has enough steps that I split it into its own post.

💡 The background (why it breaks in the first place) is covered in the previous article. Worth reading alongside this one.
👉 I Built a GitHub Profile README — the 'It Won't Show Up' Traps and Tips to Stand Out

In this article, I'll walk through how to self-host github-readme-stats on your own Vercel account so it renders reliably, along with the points that are easy to trip over.

Why self-hosting is needed

The official instance of github-readme-stats (github-readme-stats.vercel.app) is shared by many users. Because of that, it easily hits the free-tier API limits, and during busy periods the image won't render (it returns 503 SERVICE_UNAVAILABLE / DEPLOYMENT_PAUSED).

This isn't a problem on your side — it's a known issue that happens intermittently due to the shared service. If you self-host, you get your own dedicated instance, so you no longer depend on someone else's quota and it renders reliably.

💡 Assume that badges depending on external services will go down someday. For the important ones, self-hosting is the most reassuring option in the end.

Background terms

Before the steps, here's a quick rundown of the terms that come up.

Term Description
Vercel A cloud deploy / hosting platform for frontend apps
Hobby plan The free plan for personal, non-commercial use. Fine for portfolio purposes
Serverless function A function that runs only on request instead of a server running constantly. Vercel uses this to generate the image dynamically
Environment variable A config value passed in safely from outside instead of hard-coded. Here it holds the GitHub API token
Fork Copying someone else's GitHub repository into your own account

Steps

The overall flow is six steps: register on Vercel → fork the repo → get a token → deploy → check the URL → embed in your README.

1. Create a Vercel account

  1. Go to https://vercel.com/signup
  2. Choose "Continue with GitHub" to link your GitHub account
  3. For the plan, choose "Hobby"
  4. If asked for phone verification, enter it in international format (for Japan, choose +81 and drop the leading 0)

Gotcha: the "The user could not be found" error

Even with a correctly entered phone number, you may get this error. It seems to be a known issue on Vercel's side.

Fix: Submit the form at https://vercel.com/accountrecovery to contact support. Example for the "Additional Details" field:

I tried to sign up via GitHub and was asked to verify my phone number. I entered my mobile number but received the error message "The user could not be found." I have tried re-entering the number multiple times but the issue persists. This is a new account and the phone number has not been used with any other Vercel account.

2. Fork the repository

  1. Go to https://github.com/anuraghazra/github-readme-stats
  2. Click the "Fork" button at the top right
  3. Confirm it's been forked into your own GitHub account

3. Get a GitHub Personal Access Token

This is needed so github-readme-stats can call the GitHub API.

  1. Go to https://github.com/settings/tokens
  2. Click "Generate new token" → "Generate new token (classic)"
  3. Settings:
    • Note: anything (e.g. vercel-readme-stats)
    • Expiration: No expiration (or any period you like)
    • Select scopes: check public_repo (use repo if you also want to count private repositories)
  4. Click "Generate token"
  5. Copy and save the token shown (the string starting with ghp_)

⚠️ Security note: The token is only shown once — once you close this screen it's gone, so be sure to save it. Also, never write the token directly in your code or README; pass it only as a Vercel environment variable in the next step, and don't leak it.

4. Deploy to Vercel

  1. Go to https://vercel.com/new
  2. From the Import Git Repository list, select github-readme-stats and click "Import"
  3. Open the "Environment Variables" section and add the following:

| Key | Value |
| :--- | :--- |
| PAT_1 | The token from step 3 (the string starting with ghp_) |

  1. Leave the other settings at their defaults and click "Deploy"
  2. When the "Congratulations!" screen appears, the deployment is done

5. Check the deploy URL

Back on the dashboard, two kinds of URL are shown. The one you use is the fixed URL, so be careful not to mix them up.

URL Use
The URL in the Domains field (e.g. github-readme-stats-xxx-yyy.vercel.app) The fixed production URL. Use this one
The long URL in the Deployment field (e.g. github-readme-stats-aabbccddee-xxx.vercel.app) A temporary URL that changes per deploy. Don't use it

To verify, open the following in a browser; if the Stats card appears, you're good.

https://[your Domains URL]/api?username=[your GitHub username]
Enter fullscreen mode Exit fullscreen mode

6. Embed it in your README

Add the following to your profile README.md, using your own Domains URL.

![GitHub Stats](https://[your Domains URL]/api?username=[username]&show_icons=true&hide_border=true&count_private=true)
![Top Languages](https://[your Domains URL]/api/top-langs/?username=[username]&layout=compact&hide_border=true)
Enter fullscreen mode Exit fullscreen mode

Now the Stats card renders from your own dedicated instance, unaffected by the congestion on the official one.

Common customization options

You can tweak the look and what's counted via URL query parameters. Here are the ones I use most.

Option What it does Example
&theme= Theme color radical, dark, tokyonight, merko
&show_icons=true Show icons
&hide_border=true Hide the border
&count_private=true Also count private repositories
&hide= Hide specific items &hide=issues,contribs
&layout=compact Compact layout for the language card for top-langs

Hobby plan limits and caveats

For this use case (just showing it on your own README), traffic is extremely low, so the Hobby plan is plenty. Still, keep these in mind:

  • Serverless function execution limit: 10 seconds (it could rarely time out if the GitHub API is slow to respond, but in practice it's almost never an issue)
  • Bandwidth: 100 GB/month (nowhere near reachable for personal use)
  • No commercial use (ads, paid services, etc. require the Pro plan)

Wrap-up

The broken-image problem with github-readme-stats is usually caused by "the official instance being paused" — not a mistake on your side. Even so, putting something that might go down at any time on the face of your profile is unsettling, so I recommend self-hosting to get your own dedicated instance.

To recap the steps:

  1. Register on Vercel (Hobby plan is enough)
  2. Fork github-readme-stats
  3. Get a GitHub Personal Access Token (public_repo)
  4. Put the token in the PAT_1 environment variable and Deploy
  5. Note the fixed Domains URL
  6. Swap your README's image URL for your own Domains URL

Six steps in total. Once you've done it, it just keeps working reliably.

You can see my card in action on my profile 👉 github.com/uya0526-design

Thanks for reading.

References

Top comments (0)