DEV Community

qing
qing

Posted on

Build a Real-Time Leaderboard with Redis and FastAPI

Build a Real-Time Leaderboard with Redis and FastAPI

Imagine watching your game’s final match unfold, and seconds after a player crosses the finish line, their name instantly rockets to the top of the global leaderboard—no delays, no polling, no lag. That’s the power of a real-time leaderboard, and with Redis and FastAPI, you can build one in under an hour.

Leaderboards aren’t just for games. They fuel engagement in fitness apps, trading platforms, hackathons, and even customer support dashboards. But building one that updates instantly for thousands of users? That’s where most teams stumble. The secret lies in Redis’s sorted sets—a data structure designed specifically for ranking members by score with sub-millisecond performance.

Let’s cut through the noise and build a production-ready real-time leaderboard today.

Why Redis and FastAPI?

Redis is the gold standard for high-performance, real-time data. Its sorted set type (ZADD, ZREVRANGE, ZINCRBY) lets you add, update, and query ranked members in microseconds. Meanwhile, FastAPI is the fastest Python web framework, built on Starlette and Pydantic, with native async support and automatic OpenAPI docs.

Together, they give you:

  • Instant score updates via Redis commands
  • Low-latency ranking queries with ZREVRANGE
  • Real-time client updates using Redis Pub/Sub + Server-Sent Events (SSE)
  • Zero WebSocket overhead—SSE works over plain HTTP

According to Redis’s official guide, sorted sets are the ideal foundation for gaming leaderboards, supporting time-based rankings, filtered views (e.g., by country), and atomic operations via Lua scripts [5].

Step 1: Set Up Your Redis Instance

You can run Redis locally or use Redis Cloud. For this tutorial, we’ll use a local instance.

On macOS:

brew install redis
redis-server
Enter fullscreen mode Exit fullscreen mode

On Linux:

sudo apt install redis-server
redis-server
Enter fullscreen mode Exit fullscreen mode

Verify it’s running:

redis-cli ping
# Should return: PONG
Enter fullscreen mode Exit fullscreen mode

If you prefer cloud, Redis Cloud offers a free tier with instant setup and Redis Insight for visual debugging [1].

Step 2: Build the FastAPI Backend

Create a new directory and install dependencies:

mkdir redis-fastapi-leaderboard
cd redis-fastapi-leaderboard
pip install fastapi uvicorn redis python-sse
Enter fullscreen mode Exit fullscreen mode

Now, create main.py:

from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
import redis
import json
import asyncio

app = FastAPI()
redis_client = redis.Redis(host="localhost", port=6379, decode_responses=True)

LEADERBOARD_KEY = "game_leaderboard"

@app.post("/score")
async def add_score(member: str, score: int):
    """Increment or set a player's score"""
    redis_client.zincrby(LEADERBOARD_KEY, score, member)
    # Publish update for real-time clients
    redis_client.publish("leaderboard_updates", json.dumps({"member": member, "score": score}))
    return {"status": "updated", "member": member, "score": score}

@app.get("/leaderboard")
async def get_leaderboard(count: int = 10):
    """Get top N players"""
    if count <= 0:
        raise HTTPException(status_code=400, detail="count must be positive")
    top_players = redis_client.zrevrange(LEADERBOARD_KEY, 0, count - 1, withscores=True)
    return [{"member": p[0], "score": int(p[1])} for p in top_players]

@app.get("/stream")
async def stream_leaderboard():
    """Stream real-time leaderboard updates via SSE"""
    async def generate():
        pubsub = redis_client.pubsub()
        pubsub.subscribe("leaderboard_updates")
        while True:
            message = pubsub.get_message(ignore_subscribe_messages=True)
            if message:
                data = json.loads(message["data"])
                yield f"data: {json.dumps(data)}\n\n"
            await asyncio.sleep(0.1)
    return StreamingResponse(generate(), media_type="text/event-stream")
Enter fullscreen mode Exit fullscreen mode

Run the server:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

Your API is now live at http://localhost:8000. Test it with curl:

# Add scores
curl -X POST "http://localhost:8000/score?member=alice&score=150"
curl -X POST "http://localhost:8000/score?member=bob&score=200"

# Get leaderboard
curl "http://localhost:8000/leaderboard?count=5"
Enter fullscreen mode Exit fullscreen mode

Expected output:

[{"member": "bob", "score": 200}, {"member": "alice", "score": 150}]
Enter fullscreen mode Exit fullscreen mode

Step 3: Connect Real-Time Updates with Server-Sent Events

No need for WebSockets. SSE is simpler, works over HTTP, and scales effortlessly.

Create a simple HTML client in client.html:

<!DOCTYPE html>
<html>
<head><title>Real-Time Leaderboard</title></head>
<body>
  <h1>🏆 Live Leaderboard</h1>
  <ul id="leaderboard"></ul>

  <script>
    const list = document.getElementById("leaderboard");
    const eventSource = new EventSource("/stream");

    eventSource.addEventListener("message", (e) => {
      const data = JSON.parse(e.data);
      fetch(`/leaderboard?count=5`)
        .then(res => res.json())
        .then(players => {
          list.innerHTML = "";
          players.forEach((p, i) => {
            const item = document.createElement("li");
            item.textContent = `${i + 1}. ${p.member}: ${p.score}`;
            list.appendChild(item);
          });
        });
    });
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Open client.html in your browser. Now, every time you call /score, the leaderboard refreshes instantly without polling.

Step 4: Scale for Millions (Optional but Critical)

For high-scale apps:

  • Use Redis Pipelining to batch ZADD operations [5]
  • Enable TTL on leaderboard keys for daily/weekly resets [5]
  • Use Lua scripts for atomic rank+score updates [5]
  • Deploy Redis in cluster mode with sharding for horizontal scaling

CodeSignal’s guide confirms that ZINCRBY is essential for cumulative score leaderboards, while ZADD with GT/LT flags handles high/low-score tracking [5].

What You Can Do Today

You now have a fully functional real-time leaderboard that:
✅ Updates scores in milliseconds

✅ Returns top N players instantly

✅ Streams changes to clients via SSE

✅ Runs on local or cloud Redis

Try it in your next project:

  • Add a /score endpoint to your game backend
  • Replace your polling frontend with SSE
  • Add a ?country=US filter using Redis sorted set prefixes [5]

Ready to Go Further?

This isn’t just a tutorial—it’s a production pattern used by companies scaling to millions of users. The same architecture powers live trading dashboards [3], gaming tournaments [1], and hackathon ranking systems.

Your next step: Fork this code, add user authentication, and deploy it to your cloud provider. Then, share your leaderboard URL in the Dev.to comments—I’ll be the first to try it.

Let’s build real-time experiences that users actually feel. 🚀


If you found this helpful, consider buying me a coffee ☕ — it keeps these articles coming!

Also check out my AI tools collection: AI 次元世界 — free AI tools for developers.


💡 Related: **Content Creator Ultimate Bundle (Save 33%)* — $29.99*


🔒 Want More?

This article covers the basics. In Complete Python Automation Toolbox Bundle (Save 32%) ($24.99), you get:

  • Complete source code
  • Advanced techniques
  • Real-world examples
  • Step-by-step tutorials
  • Bonus templates

Get instant access →

Top comments (0)