TryHackMe provides a “badge” you can embed in websites. The catch:
- The image badge URL is static (it doesn’t refresh live).
- The iframe badge is live, but GitHub Markdown doesn’t allow iframes.
So if you want your TryHackMe stats to stay up-to-date in a GitHub profile README (like other “stats cards”), you need something that can render the live badge and publish it as an image.
This repo does exactly that:
- A scheduled GitHub Actions workflow opens the live badge page (Playwright/Chromium)
- Takes a screenshot and crops it
- Commits the updated PNG into
docs/ - You serve it via GitHub Pages, then embed that PNG in any Markdown
- A public, cache-friendly image URL you can use anywhere (including GitHub profile README)
- Automated refresh on a schedule you control
- No hardcoded personal IDs in the repository (use GitHub Secrets)
Note: The workflow triggers in .github/workflows/update-badge.yml are commented out by default to prevent automatic runs when you first push the repo. When you're ready for the workflow to run (on schedule or manually), simply uncomment the on: block at the top of the file:
# on:
# schedule:
# - cron: '0 */2 * * *'
# workflow_dispatch:Change it to:
on:
schedule:
- cron: '0 */2 * * *'
workflow_dispatch:This ensures the workflow only starts when you want it to.
Fork it to your account.
In your fork:
- Settings → Secrets and variables → Actions → New repository secret
- Name:
THM_USER_PUBLIC_ID - Value: your TryHackMe
userPublicId(a number)
How to find it:
- Go to your TryHackMe profile badge embed option and copy the iframe.
- It contains a URL like:
https://tryhackme.com/api/v2/badges/public-profile?userPublicId=1234567 - The number after
userPublicId=is what you want.
Note: this value is not committed to the repo; it’s pulled from Actions secrets at runtime.
In your fork:
- Settings → Pages
- Source: Deploy from a branch
- Branch:
main/ folder:docs
IMPORTANT: When configuring GitHub Pages, make sure you select the docs folder (not the repository root). If you deploy from the root directory, GitHub Pages won’t find tryhackme_badge.png and your badge URL will 404.
After the first workflow run, the generated image will be available at:
https://<your-github-username>.github.io/thm-badge/tryhackme_badge.png
Why there is no /docs/ in the URL:
- The workflow commits the file to
docs/tryhackme_badge.pngin the repo. - When GitHub Pages is configured with Branch:
main/ Folder:docs, that folder becomes the website root. - So
docs/tryhackme_badge.pngis published as/tryhackme_badge.png.
Example (for a GitHub profile README or any repo README):
<a href="https://tryhackme.com/p/<your-tryhackme-username>">
<img src="https://<your-github-username>.github.io/thm-badge/tryhackme_badge.png" alt="TryHackMe Badge" style="width: 50%;">
</a>Edit the cron in .github/workflows/update-badge.yml.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m playwright install chromium
# Generate into docs/
THM_USER_PUBLIC_ID="1234567" python update_badge.py --output docs/tryhackme_badge.png- This tool avoids hardcoding your TryHackMe identifiers in the repository.
- Your generated badge image is intended to be public (that’s the point of embedding it in Markdown).
BSD 3-Clause License - see LICENSE.
