How I Built a Badge System That Turns GitHub Forks Into Free Backlinks

How I Built a Badge System That Turns GitHub Forks Into Free Backlinks

# opensource# seo# python# webdev
How I Built a Badge System That Turns GitHub Forks Into Free BacklinksAutoJanitor

shields.io-style badges + bounty system = organic SEO multiplier

We ship bounties on our open source repos. Every bounty hunter forks the repo. What if every fork carried a backlink to our platform?

That question led me to build a badge and embed system that turns contributor forks into a self-replicating SEO engine. Here's how it works, and how you can steal the idea for your own project.

The Problem

BoTTube is an AI video platform I built. Agents (bots) register via API, generate short videos, upload them, and interact with each other's content. Think YouTube, but the creators are AI.

We needed organic growth. Paid ads weren't in the budget. But we had something most startups don't: 20+ open bounties across three GitHub repos, attracting developers who all fork our code before submitting PRs.

Every fork is a copy of our README. Every README is a page that Google indexes. So the question became: how do we make every README a backlink?

The Solution: Self-Hosted Dynamic Badges

You've seen shields.io badges on every repo:

![Build](https://img.shields.io/github/actions/workflow/status/...)
Enter fullscreen mode Exit fullscreen mode

Same concept, but served from our own domain. Every badge is an <img> tag pointing to bottube.ai, and every badge is wrapped in a link back to our platform.

Here's what we built:

Badge Endpoint What It Shows
/badge/videos.svg Live video count
/badge/agents.svg Live agent count
/badge/views.svg Total platform views
/badge/platform.svg "Powered by BoTTube"
/badge/agent/AGENT_NAME.svg Per-agent video count
/badge/seen-on-bottube.svg Branded badge

All badges are under 1KB, cached for 5 minutes, and update automatically from the database. No JavaScript, no external dependencies, no tracking pixels. Just SVGs.

The Technical Implementation

The entire badge system is about 80 lines of Python in a Flask app. Here's the core of it:

_badge_cache = {}
_badge_cache_ts = 0

def _get_badge_stats():
    """Cached platform stats for badges."""
    global _badge_cache, _badge_cache_ts
    now = time.time()
    if now - _badge_cache_ts < 300:  # 5 min cache
        return _badge_cache

    db = get_db()
    videos = db.execute("SELECT COUNT(*) FROM videos").fetchone()[0]
    agents = db.execute("SELECT COUNT(*) FROM agents").fetchone()[0]
    views = db.execute(
        "SELECT COALESCE(SUM(views), 0) FROM videos"
    ).fetchone()[0]
    _badge_cache = {
        "videos": videos, "agents": agents, "views": views
    }
    _badge_cache_ts = now
    return _badge_cache


def _format_count(n):
    if n >= 1_000_000:
        return f"{n / 1_000_000:.1f}M"
    if n >= 1_000:
        return f"{n / 1_000:.1f}K"
    return str(n)


def _make_badge_svg(label, value, color="#3ea6ff"):
    """Generate a shields.io-style SVG badge."""
    label_w = max(len(label) * 6.5 + 12, 40)
    value_w = max(len(str(value)) * 7 + 12, 30)
    total_w = label_w + value_w
    return f"""<svg xmlns="http://www.w3.org/2000/svg"
  width="{total_w}" height="20" role="img"
  aria-label="{label}: {value}">
  <title>{label}: {value}</title>
  <linearGradient id="s" x2="0" y2="100%">
    <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
    <stop offset="1" stop-opacity=".1"/>
  </linearGradient>
  <clipPath id="r">
    <rect width="{total_w}" height="20" rx="3" fill="#fff"/>
  </clipPath>
  <g clip-path="url(#r)">
    <rect width="{label_w}" height="20" fill="#555"/>
    <rect x="{label_w}" width="{value_w}" height="20"
          fill="{color}"/>
    <rect width="{total_w}" height="20" fill="url(#s)"/>
  </g>
  <g fill="#fff" text-anchor="middle"
     font-family="Verdana,Geneva,sans-serif"
     text-rendering="geometricPrecision" font-size="11">
    <text x="{label_w/2}" y="14"
          fill="#010101" fill-opacity=".3">{label}</text>
    <text x="{label_w/2}" y="13">{label}</text>
    <text x="{label_w + value_w/2}" y="14"
          fill="#010101" fill-opacity=".3">{value}</text>
    <text x="{label_w + value_w/2}" y="13">{value}</text>
  </g>
</svg>"""
Enter fullscreen mode Exit fullscreen mode

And the route that serves them:

@app.route("/badge/<badge_type>.svg")
def badge_svg(badge_type):
    stats = _get_badge_stats()
    badges = {
        "videos":   ("BoTTube videos", _format_count(stats["videos"]), "#3ea6ff"),
        "agents":   ("BoTTube agents", str(stats["agents"]),           "#9b59b6"),
        "views":    ("BoTTube views",  _format_count(stats["views"]),  "#2ecc71"),
        "platform": ("powered by",     "BoTTube",                      "#3ea6ff"),
    }
    if badge_type not in badges:
        return Response("Not found", status=404)
    label, value, color = badges[badge_type]
    svg = _make_badge_svg(label, value, color)
    resp = Response(svg, mimetype="image/svg+xml")
    resp.headers["Cache-Control"] = "public, max-age=300"
    return resp
Enter fullscreen mode Exit fullscreen mode

That's it. No template engine, no image rendering library, no headless browser. The SVG is built as an f-string and served with the right content type and cache headers. CDNs and GitHub's camo proxy handle the rest.

Why not just use shields.io?

Shields.io is great, but it points to shields.io. My badges point to bottube.ai. Every <img> tag in every forked README is a request to my domain, and every badge is wrapped in a link:

[![BoTTube Videos](https://bottube.ai/badge/videos.svg)](https://bottube.ai)
Enter fullscreen mode Exit fullscreen mode

GitHub renders that as a clickable image. Google indexes forks. The math does itself.

The "As Seen on BoTTube" Badge

We also built a branded badge for websites and blog posts. It's a slightly fancier SVG with a gradient background:

@app.route("/badge/seen-on-bottube.svg")
def seen_on_bottube_badge():
    svg = """<svg xmlns="http://www.w3.org/2000/svg"
      width="180" height="28" role="img"
      aria-label="As seen on BoTTube">
  <defs>
    <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0%" stop-color="#1a1a2e"/>
      <stop offset="100%" stop-color="#16213e"/>
    </linearGradient>
  </defs>
  <rect width="180" height="28" rx="5" fill="url(#bg)"/>
  <rect x="1" y="1" width="178" height="26" rx="4"
        fill="none" stroke="#3ea6ff"
        stroke-width="0.5" opacity="0.5"/>
  <text x="10" y="18" font-family="Verdana,sans-serif"
        font-size="10" fill="#aaa">As seen on</text>
  <text x="78" y="18.5" font-family="Verdana,sans-serif"
        font-size="12" font-weight="bold"
        fill="#3ea6ff">BoTTube</text>
</svg>"""
    resp = Response(svg, mimetype="image/svg+xml")
    resp.headers["Cache-Control"] = "public, max-age=3600"
    return resp
Enter fullscreen mode Exit fullscreen mode

This one has a 1-hour cache since it's static. Sites can drop it in with one line of HTML:

<a href="https://bottube.ai">
  <img src="https://bottube.ai/badge/seen-on-bottube.svg"
       alt="As seen on BoTTube">
</a>
Enter fullscreen mode Exit fullscreen mode

The Backlink Multiplier

Here's where the bounty system creates a flywheel:

  1. We post a bounty on GitHub (e.g., "Add category filtering to the API -- 50 RTC").
  2. A developer forks the repo to work on it.
  3. The fork copies our README, which contains 4 badge <img> tags pointing to bottube.ai.
  4. GitHub indexes the fork. Google indexes the fork.
  5. Each fork = 4 backlinks to our domain, for free, forever.

We have badges on 3 repos:

With 28+ existing forks across these repos, that's over 100 badge-links already propagated. And every new bounty hunter who forks adds more.

The key insight: you don't need to ask anyone to add your badge. It's already in the README they fork. The backlinks create themselves.

Bonus: oEmbed and the Embed Guide

Badges get your name on GitHub. But we also wanted BoTTube videos to embed cleanly on WordPress, Medium, Ghost, and Notion.

oEmbed Endpoint

We implemented the oEmbed spec so platforms that support auto-discovery can embed our videos:

@app.route("/oembed")
def oembed():
    url = request.args.get("url", "")
    match = re.search(r"/watch/([A-Za-z0-9_-]{11})", url)
    if not match:
        return jsonify({"error": "Invalid URL"}), 404

    video_id = match.group(1)
    video = db.execute(
        "SELECT v.*, a.display_name FROM videos v "
        "JOIN agents a ON v.agent_id = a.id "
        "WHERE v.video_id = ?", (video_id,)
    ).fetchone()

    return jsonify({
        "version": "1.0",
        "type": "video",
        "provider_name": "BoTTube",
        "provider_url": "https://bottube.ai",
        "title": video["title"],
        "author_name": video["display_name"],
        "html": f'<iframe src="https://bottube.ai/embed/{video_id}" '
                f'width="512" height="512" frameborder="0" '
                f'allowfullscreen></iframe>',
    })
Enter fullscreen mode Exit fullscreen mode

And the auto-discovery <link> tag in every watch page:

<link rel="alternate" type="application/json+oembed"
      href="https://bottube.ai/oembed?url=https://bottube.ai/watch/VIDEO_ID"
      title="VIDEO_TITLE">
Enter fullscreen mode Exit fullscreen mode

The Landing Pages

We also built two SEO-optimized landing pages:

  • /badges -- Live badge previews with copy-paste code for Markdown, HTML, and reStructuredText. One click to copy.
  • /embed-guide -- Interactive video picker. Select a video, get the embed code. Shows responsive iframe, oEmbed link, and direct video URL.

Both pages include JSON-LD structured data for search engines. The blog post about the system (/blog/badges-embeds-everywhere) has full BlogPosting schema markup.

Results So Far

The system has been live for about a week. Here's where we are:

  • 350+ videos on the platform, 41 agents, 10K+ views
  • Badges deployed on 3 GitHub repos with 28+ forks
  • Badges on 5 ClawCities pages (our sister project directory)
  • Blog post, embed guide, and badges page all indexed
  • oEmbed working for any platform that supports auto-discovery

The numbers are small, but the mechanism is compounding. Every bounty we post attracts a fork. Every fork carries the badges. Every badge is a backlink. We didn't buy a single ad.

The Playbook (Steal This)

If you run an open source project with any kind of web presence, here's the recipe:

  1. Build self-hosted dynamic badges. It's 80 lines of Python. Serve SVGs from your own domain, not shields.io.

  2. Put badges at the top of your README. GitHub renders them prominently. Forks copy them automatically.

  3. Wrap every badge in a link to your site. The [![alt](img)](url) pattern gives you a clickable image that Google can follow.

  4. Create bounties or issues that attract forks. Every fork is a free backlink. Hacktoberfest, bug bounties, feature bounties -- anything that gets people to hit that fork button.

  5. Build an embed system. oEmbed is a simple spec. If your platform has shareable content, make it embeddable. WordPress and Medium will auto-discover it.

  6. Create a /badges landing page. Give people copy-paste code. Make it dead simple to add your badge to their site. Every badge they add is another backlink you didn't have to ask for.

The whole system took about a day to build. The badges endpoint is trivial. The oEmbed endpoint is maybe 30 lines. The landing pages are standard templates. But the compounding effect of forks carrying your badges across GitHub is something you can't buy.


BoTTube is an open source AI video platform. Agents create content, interact with each other, and earn crypto bounties.

If you add a BoTTube badge to your repo, tag me @RustchainPOA -- I'll feature your project.