Fortnite’s in-game rank badge tells you your current tier, but it doesn’t tell you how you got there, when you last dropped a division, or how your Kill/Death ratio trends across a season. If you want that history, you have to build it yourself. This tutorial walks through building a personal Fortnite rank tracker: a small Python project that pulls your Fortnite stats through a public API, stores a snapshot every day, flags rank changes, and pings you on Discord when you climb (or fall). By the end you’ll have a working tool you can run on a schedule, plus a chart of your season progress.
Total setup time runs about 40 minutes if you already have Python installed. No prior API experience is required, though basic comfort with the command line helps. Everything you build here stays on your own machine, nothing gets uploaded to a third-party dashboard, and you control exactly what data gets stored and for how long.
Why Build Your Own Tracker Instead of Using a Ready-Made App
Plenty of sites already show your Fortnite stats. So why write your own? Three reasons come up most often among players who’ve tried both routes. First, most public trackers only show a live snapshot, not history. They’ll tell you your rank right now, but not that you gained two divisions in the last week or that your K/D has been sliding since a specific patch. A tracker with its own database fixes that by design.
Second, alerts. Third-party sites rarely notify you the moment your rank changes, you have to go check. Wiring your own Discord webhook means the notification finds you instead. Third, control. Once your data lives in a SQLite file you own, you can query it however you want: average score by day of week, longest losing streak, whatever question you’re curious about. A hosted dashboard only answers the questions its developer thought to build.
None of that means public trackers are bad. Fortnite Tracker’s own site is a fine quick-lookup tool, and this tutorial actually builds on top of its API. The point is that a personal project gives you history, alerts, and query flexibility that a shared public tool generally won’t. It’s also a genuinely approachable first API project if you’ve never built one before: a single endpoint, simple header-based auth, and a JSON response, no OAuth dance, no complicated pagination, and no cost to experiment.
Prerequisites: What You Need Before You Start
Gather these before Step 1. Version numbers matter less than having something recent, but the table below shows what this guide was tested against. If you’re on an older Python 3 release, most of this code will still run without modification, since nothing here depends on cutting-edge language features.
| Requirement | Version Used Here | Notes |
|---|---|---|
| Python | 3.12 | 3.10+ works fine; check with python3 --version |
| pip | 24.x | Ships with modern Python installs |
| requests library | 2.32 | Installed via pip in Step 3 |
| SQLite | 3.45 (bundled) | No separate install needed, it’s in Python’s standard library |
| matplotlib | 3.9 | Optional, only needed for the charting step |
| Fortnite Tracker account | Free tier | Sign up at fortnitetracker.com to get an API key |
| Discord server (optional) | Any | Only needed if you want rank-up alerts |
| Cron or Task Scheduler | Built into OS | For running the tracker automatically |
You’ll also need your exact Epic Games display name and the platform you play on (PC, Xbox Live, or PlayStation Network), since the API looks players up by nickname rather than account ID at first.
Understanding the Fortnite Ranked Ladder in 2026
Before you write a line of code, it helps to know exactly what you’re tracking. Fortnite Ranked runs on an eight-tier ladder: Bronze, Silver, Gold, Platinum, Diamond, Elite, Champion, and Unreal. Bronze through Diamond each split into three divisions (I, II, III), and as of the v40.20 update in July 2026, Elite and Champion were also expanded into three divisions apiece, according to esportstales.com’s rank distribution tracking. Unreal remains a single, undivided tier since it functions as a leaderboard rather than a rank you can fall out of.
That gives 22 total divisions across the current Chapter 7 Season 3 ladder. Player distribution is heavily skewed toward the bottom, which is normal for any skill-based ladder. Here’s the breakdown reported by tesoro.gg’s ranking data in August 2026:
| Rank | Divisions | Share of Players |
|---|---|---|
| Bronze | I, II, III | 38.1% |
| Silver | I, II, III | 28.8% |
| Gold | I, II, III | 8.4% |
| Platinum | I, II, III | 13.6% |
| Diamond | I, II, III | 3.8% |
| Elite | I, II, III | 3.1% |
| Champion | I, II, III | 2.3% |
| Unreal | Single tier, leaderboard-based | 1.8% |
Notice that Bronze and Silver alone account for two-thirds of the ranked population. That matters for the tracker you’re about to build: your rank-change detector needs to fire correctly whether you’re bouncing between Bronze divisions or grinding toward Unreal, so the logic can’t assume you’re near the top of the ladder.
How Rank Points Work Behind the Scenes
Epic doesn’t publish the exact formula behind Fortnite’s ranked scoring, but community testing and pattern analysis across 2025 and 2026 point to a few consistent inputs. Placement matters most: surviving longer and finishing higher earns more points than kills alone. Eliminations still count, but a cautious player who places top five consistently tends to climb faster than an aggressive player who dies early with a high kill count. The system also appears to weigh the average rank of your lobby, so beating tougher opponents nets more points than beating weaker ones.
This is worth understanding before you build the tracker, because it explains why your score field (Step 5) moves in a way that doesn’t map cleanly to kills or wins alone. Your tracker records the outcome, the resulting score, and the percentile that score sits at, it can’t reverse-engineer Epic’s private formula. That’s a reasonable trade-off: you still get an accurate history of where you stood over time, even without knowing precisely how each match’s points were calculated.
One more wrinkle: ranked progress tracks separately for Battle Royale and Zero Build, even though both modes share the same eight-tier naming. If you play both, decide up front which playlist your tracker should follow, or extend the schema in Step 6 to store a mode column and track both independently.
Step 1: Choose Your Data Source
Epic Games doesn’t publish a public, self-serve stats API for individual accounts, so third-party trackers fill the gap. Two options dominate the ecosystem right now.
| Source | Auth | Rate Limit | Best For |
|---|---|---|---|
| Fortnite Tracker Network | Single TRN-Api-Key header | ~1 request per 2 seconds | Lifetime and per-mode stats, K/D, score, percentile |
| fortnite-api.com | Optional API key for higher limits | Generous free tier | Cosmetics, shop rotation, news; not player stats |
| Direct Epic endpoints | OAuth via registered app | Strict, enterprise-only | Official integrations, not hobby projects |
This tutorial uses the Fortnite Tracker Profile API as the primary data source, since it’s the one that actually returns per-player combat stats. Worth flagging up front: the documentation itself notes the service has been in what it calls a “silent end-of-life state” for several years, so some calls return intermittent errors. Build your error handling around that reality from day one rather than bolting it on later. You’ll optionally pull cosmetic and shop data from fortnite-api.com later if you want to enrich the tracker’s output, but it’s not required for rank tracking itself.
It’s tempting to reach for Epic’s own developer portal instead, but that route is built for studios shipping integrations, not for a weekend project checking one account. Registering a first-party Epic application involves an approval process and terms of service scoped around commercial use. For a personal rank tracker, the community-run Fortnite Tracker API gets you working data in minutes instead of a review queue.
Step 2: Get a Fortnite Tracker API Key
Head to the Fortnite Tracker developer signup page and create a key. The free tier doesn’t require a credit card, and per the API Evangelist’s Fortnite provider notes, most hobby integrations run comfortably within the free quota. Once issued, your key gets passed as a single header on every request:
TRN-Api-Key: your-key-goes-here
Store this somewhere outside your codebase. Don’t hardcode it into a script you might commit to GitHub. An environment variable works fine for a personal project:
export FN_TRACKER_KEY="your-key-goes-here"
Add that line to your shell profile (.bashrc, .zshrc, or equivalent) so it persists across sessions, then restart your terminal or run source ~/.bashrc.
Step 3: Set Up Your Python Project
Create a project folder, an isolated virtual environment, and install the one external dependency you actually need for the core tracker.
mkdir fortnite-rank-tracker
cd fortnite-rank-tracker
python3 -m venv venv
source venv/bin/activate
pip install requests matplotlib
mkdir -p data logs
On Windows, replace the activation line with venv\Scripts\activate. The data folder will hold your SQLite database, and logs will catch anything the tracker writes when it runs unattended via cron.
Step 4: Write the API Client
Now build a thin wrapper around the Fortnite Tracker endpoint. Keep it simple: one function to fetch a profile, with the rate limit and error states from the docs handled explicitly rather than ignored.
import os
import time
import requests
BASE_URL = "https://api.fortnitetracker.com/v1"
API_KEY = os.environ["FN_TRACKER_KEY"]
HEADERS = {"TRN-Api-Key": API_KEY}
def get_profile(platform: str, epic_nickname: str, retries: int = 3) -> dict:
"""Fetch a player's Fortnite profile. platform is one of pc, xbl, psn."""
url = f"{BASE_URL}/profile/{platform}/{epic_nickname}"
for attempt in range(retries):
response = requests.get(url, headers=HEADERS, timeout=10)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
wait = 2 * (attempt + 1)
print(f"Rate limited, waiting {wait}s")
time.sleep(wait)
continue
if response.status_code == 404:
raise ValueError(f"No player found: {epic_nickname} on {platform}")
response.raise_for_status()
raise RuntimeError("Fortnite Tracker API failed after retries")
The retry loop matters more than it looks. Because the service runs close to end-of-life, a single dropped request is common and shouldn’t crash an unattended cron job. Waiting and retrying twice clears the vast majority of transient failures in practice. Notice also that the function raises specific exceptions rather than returning None on failure, that choice makes the calling code in later steps easier to debug, since a stack trace tells you exactly which player or platform triggered the problem instead of a silent empty result.
Step 5: Test Your First API Call
Run a quick interactive test before wiring anything else together. Open a Python shell in your activated virtual environment:
python3
>>> from client import get_profile
>>> profile = get_profile("pc", "YourEpicName")
>>> profile["epicUserHandle"]
>>> profile["stats"]["p9"]["kd"]
A successful response looks roughly like this (trimmed for length):
{
"accountId": "4735ce91-3292-4caf-8a5b-17789b40f79c",
"platformNameLong": "PC / Epic Games",
"epicUserHandle": "YourEpicName",
"stats": {
"p9": {
"kd": {"value": "3.21", "percentile": 99.4},
"score": {"value": "182000", "percentile": 99.8},
"matches": {"value": "5421", "percentile": 99.9}
}
},
"lifeTimeStats": [
{"key": "Wins", "value": "247"},
{"key": "Top 10", "value": "1820"},
{"key": "Kills", "value": "17421"}
]
}
The percentile field inside each stat category is what you’ll lean on in Step 8, since the API doesn’t return a literal “Bronze” or “Diamond” label. You have to derive tier from where your score sits relative to the rest of the player base.
Step 6: Build the Local Database
You need somewhere to store snapshots over time so the tracker can compare “now” against “last time.” SQLite is the right tool here: zero setup, a single file, and more than fast enough for one player’s history.
import sqlite3
from datetime import datetime
DB_PATH = "data/tracker.db"
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
captured_at TEXT NOT NULL,
epic_handle TEXT NOT NULL,
kd REAL,
score INTEGER,
score_percentile REAL,
wins INTEGER,
matches INTEGER,
estimated_tier TEXT
)
""")
conn.commit()
conn.close()
if __name__ == "__main__":
init_db()
print(f"Database ready at {DB_PATH}")
Run this once with python3 db.py and you should see the confirmation printed with no errors. A single table is enough for this scope, you can normalize it further later if you decide to track multiple accounts.
Step 7: Save Your First Rank Snapshot
With the client and database in place, write the function that pulls fresh data and stores it. Keep this separate from the alerting logic so you can test it in isolation.
import sqlite3
from datetime import datetime, timezone
from client import get_profile
def save_snapshot(platform: str, epic_nickname: str):
profile = get_profile(platform, epic_nickname)
squad_stats = profile["stats"].get("p9", {})
kd = float(squad_stats.get("kd", {}).get("value", 0))
score = int(squad_stats.get("score", {}).get("value", 0))
percentile = squad_stats.get("score", {}).get("percentile", 0.0)
wins = next(
(int(s["value"]) for s in profile["lifeTimeStats"] if s["key"] == "Wins"),
0,
)
matches = int(squad_stats.get("matches", {}).get("value", 0))
conn = sqlite3.connect("data/tracker.db")
conn.execute(
"""INSERT INTO snapshots
(captured_at, epic_handle, kd, score, score_percentile, wins, matches, estimated_tier)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(
datetime.now(timezone.utc).isoformat(),
profile["epicUserHandle"],
kd, score, percentile, wins, matches,
None,
),
)
conn.commit()
conn.close()
return {"kd": kd, "score": score, "percentile": percentile}
Run python3 -c "from snapshot import save_snapshot; save_snapshot('pc', 'YourEpicName')" and check the database with sqlite3 data/tracker.db "SELECT * FROM snapshots". You should see one row.
Step 8: Estimate Your Tier from Percentile Data
Since the raw API only gives you a percentile, not a rank label, map that percentile against the published 2026 distribution table from Step 0. It’s an estimate, not an exact readout, but it tracks closely enough to spot real rank-tier movement over time.
# Cumulative thresholds built from the 2026 Chapter 7 Season 3
# distribution: top-X% cutoff for each tier, highest tier first.
TIER_THRESHOLDS = [
("Unreal", 98.2),
("Champion", 95.9),
("Elite", 92.8),
("Diamond", 89.0),
("Platinum", 75.4),
("Gold", 67.0),
("Silver", 38.2),
("Bronze", 0.0),
]
def estimate_tier(percentile: float) -> str:
for tier_name, cutoff in TIER_THRESHOLDS:
if percentile >= cutoff:
return tier_name
return "Bronze"
Wire this into save_snapshot() by replacing the hardcoded None with estimate_tier(percentile) before the insert. This gets you a readable tier label alongside every raw score you store, which makes the change detector in the next step far easier to reason about. Keep in mind these thresholds reflect one snapshot of the 2026 season distribution, and Epic occasionally rebalances the ladder between seasons. Revisit the table in Step 0 each new season and update the cutoffs if the published distribution shifts noticeably, otherwise your estimated tier will slowly drift out of sync with your actual in-game rank.
Step 9: Detect Rank Changes Automatically
A tracker that only stores numbers isn’t much better than the in-game HUD. The useful part is comparing the newest snapshot against the previous one and flagging a change.
import sqlite3
def detect_change(epic_handle: str):
conn = sqlite3.connect("data/tracker.db")
rows = conn.execute(
"""SELECT captured_at, estimated_tier, score
FROM snapshots WHERE epic_handle = ?
ORDER BY captured_at DESC LIMIT 2""",
(epic_handle,),
).fetchall()
conn.close()
if len(rows) < 2:
return None
(latest_time, latest_tier, latest_score), (_, prev_tier, prev_score) = rows
if latest_tier != prev_tier:
direction = "up" if latest_score > prev_score else "down"
return {
"changed": True,
"direction": direction,
"from_tier": prev_tier,
"to_tier": latest_tier,
"timestamp": latest_time,
}
return {"changed": False}
This only needs two rows of history, so it works from your very second run onward. Call it right after every save_snapshot() to check whether anything worth reporting just happened.
Step 10: Send Rank-Up Alerts to Discord
Discord webhooks are the simplest way to get a push notification without building a full app. Create one from your server’s channel settings (Integrations → Webhooks → New Webhook), copy the URL, and store it as another environment variable.
import os
import requests
WEBHOOK_URL = os.environ["DISCORD_WEBHOOK_URL"]
def send_alert(change: dict, epic_handle: str):
if not change or not change["changed"]:
return
arrow = "climbed to" if change["direction"] == "up" else "dropped to"
message = (
f"**{epic_handle}** {arrow} **{change['to_tier']}** "
f"(from {change['from_tier']}) at {change['timestamp']}"
)
requests.post(WEBHOOK_URL, json={"content": message}, timeout=10)
Tie the three pieces together in a small runner script that Step 11 will schedule:
from snapshot import save_snapshot
from changes import detect_change
from alerts import send_alert
def run(platform: str, epic_nickname: str):
save_snapshot(platform, epic_nickname)
change = detect_change(epic_nickname)
send_alert(change, epic_nickname)
if __name__ == "__main__":
run("pc", "YourEpicName")
Step 11: Automate Everything with Cron
Manually running the script defeats the point. Schedule it instead. On Linux or macOS, open your crontab with crontab -e and add a line that runs the tracker a few times a day (checking too often burns your rate limit for no benefit):
0 */6 * * * cd /home/you/fortnite-rank-tracker && venv/bin/python3 run.py >> logs/tracker.log 2>&1
That runs every 6 hours and appends output to a log file instead of silently discarding it. If you’re not confident reading raw cron syntax, crontab.guru lets you paste a schedule and see it explained in plain English before you commit it. On Windows, Task Scheduler’s “Create Basic Task” wizard achieves the same result by pointing at venv\Scripts\python.exe run.py.
Step 12: Chart Your Progress Over Time
Once you’ve got a few days or weeks of snapshots, a simple chart tells the story better than a table of rows. This pulls your score history and plots it with matplotlib.
import sqlite3
import matplotlib.pyplot as plt
from datetime import datetime
def plot_progress(epic_handle: str):
conn = sqlite3.connect("data/tracker.db")
rows = conn.execute(
"""SELECT captured_at, score, estimated_tier FROM snapshots
WHERE epic_handle = ? ORDER BY captured_at ASC""",
(epic_handle,),
).fetchall()
conn.close()
dates = [datetime.fromisoformat(r[0]) for r in rows]
scores = [r[1] for r in rows]
plt.figure(figsize=(10, 5))
plt.plot(dates, scores, marker="o")
plt.title(f"{epic_handle} — Score History")
plt.xlabel("Date")
plt.ylabel("Score")
plt.tight_layout()
plt.savefig("data/progress.png")
print("Saved chart to data/progress.png")
if __name__ == "__main__":
plot_progress("YourEpicName")
Run it any time with python3 chart.py. Because every snapshot also stores an estimated tier, you can extend this later to color-code points by tier instead of just plotting a raw score line.
Complete Working Project Structure
Once every step above is done, your folder should look like this. Each file maps to exactly one step from the walkthrough, which is deliberate, if something misbehaves later you’ll know precisely which file to open rather than hunting through one large monolithic script.
fortnite-rank-tracker/
├── venv/
├── data/
│ ├── tracker.db
│ └── progress.png
├── logs/
│ └── tracker.log
├── client.py # get_profile()
├── db.py # init_db()
├── snapshot.py # save_snapshot(), estimate_tier()
├── changes.py # detect_change()
├── alerts.py # send_alert()
├── chart.py # plot_progress()
└── run.py # ties it all together, called by cron
Seven small files, one SQLite database, and a cron entry. Nothing here needs a server, a container, or a cloud account, which keeps the whole thing free to run indefinitely.
Testing the Full Pipeline End to End
Before you hand the whole thing off to cron, run it manually twice in a row to confirm each piece talks to the next one correctly. The first run establishes a baseline snapshot, the second run gives the change detector something to compare against.
python3 run.py
# wait a moment, or manually edit a test row in the DB to simulate a change
python3 run.py
sqlite3 data/tracker.db "SELECT captured_at, estimated_tier, score FROM snapshots ORDER BY captured_at DESC LIMIT 5"
If the second run’s tier differs from the first, you should see a message posted in your Discord channel within a few seconds. If it doesn’t arrive, jump to the troubleshooting table below rather than guessing. Once both runs complete cleanly and the database shows two rows, you’re ready to hand the job to cron. It’s worth leaving the script running manually for a day or two before fully trusting the scheduled version, that way you catch any environment differences between your interactive shell and cron’s stripped-down environment while you’re still watching.
Security and Privacy: Protecting Your API Key and Webhook
Two secrets matter in this project: your Fortnite Tracker API key and your Discord webhook URL. Both act as bearer credentials, meaning anyone who has the string can use it as you, no separate password check required. Treat them the same way you’d treat a password. Beyond keeping them out of Git history, a few habits reduce your exposure meaningfully.
Set restrictive file permissions if you’re storing secrets in a .env file rather than shell exports, chmod 600 .env on Linux or macOS keeps other local users on a shared machine from reading it. If you ever post a screenshot of your terminal or your crontab for troubleshooting help, redact both values first, a webhook URL leaked in a public forum lets anyone spam your Discord channel until you regenerate it. The API key is lower stakes since it only grants read access to public Fortnite stats, but there’s no reason to leave it exposed either.
On the data side, the only personal information this project stores is your Epic display name and your ranked stats, both already visible to anyone in your public matches. Still, if you’re running this on a shared or cloud machine, encrypt the disk or restrict access to the data/ folder the same way you would for the credentials themselves.
Common Pitfalls to Avoid
Most of the problems people hit with a project like this aren’t in the ranked-tracking logic itself, they’re in the small operational details around credentials, scheduling, and error handling. The list below covers the ones that come up most often.
- Hardcoding your API key in a script you commit to Git. Use an environment variable instead, and add a
.envor credentials file to.gitignorebefore your first commit. - Polling too aggressively. The Fortnite Tracker API allows roughly one request every two seconds, but hitting it every few minutes around the clock adds no real signal since ranked stats don’t move that fast for most players.
- Assuming the percentile-to-tier mapping is exact. It’s an estimate built from published distribution data, not a value Epic returns directly. Treat “estimated_tier” as a label, not ground truth.
- Ignoring the 404 case for misspelled nicknames. Epic display names are case-sensitive in some lookups and easy to typo. A silent failure here just looks like “no data,” not an error.
- Forgetting that this API sits in a legacy, low-maintenance state. Build retries and logging in from the start rather than treating errors as rare edge cases.
- Storing everything in one giant script. Splitting the client, database, and alerting logic into separate files (as this guide does) makes debugging one failing piece far faster.
Troubleshooting Guide
If something breaks, work through this table before assuming the API itself is down. Most failures trace back to a local environment issue rather than the Fortnite Tracker service, and the fix usually takes less than a minute once you spot the right row.
| Symptom | Likely Cause | Fix |
|---|---|---|
KeyError: 'FN_TRACKER_KEY' | Environment variable not exported in current shell | Run source ~/.bashrc or re-export before running the script |
| 404 on every request | Wrong platform code or misspelled nickname | Double-check platform is exactly pc, xbl, or psn |
| 429 responses even at low frequency | Multiple scripts sharing one API key | Check cron isn’t double-scheduled, or that a leftover process is still running |
Empty stats object | Player has no ranked matches this season yet | Play at least one ranked match, stats populate after your first match |
sqlite3.OperationalError: no such table | Forgot to run init_db() first | Run python3 db.py before anything else touches the database |
| Discord alert never arrives | Webhook URL expired or channel deleted | Generate a new webhook and update the environment variable |
| Cron job runs but nothing happens | Cron uses a different PATH than your interactive shell | Use absolute paths to the venv’s Python binary, as shown in Step 11 |
| Chart shows a flat line | Too few snapshots collected yet | Let the tracker run for a few days before expecting a meaningful trend |
| Script hangs indefinitely | No timeout set on the request | Confirm every requests.get() call includes timeout=10 |
Advanced Tips for Power Users
Once the basic version runs reliably, a few extensions make it more useful. Track multiple accounts by looping the run() function over a list of (platform, nickname) tuples instead of a single hardcoded pair, adding a squad or duo group of friends is just a few more lines. If you want to pull in cosmetic data alongside stats, the free fortnite-api.com documentation covers item shop and cosmetics endpoints you can join against your snapshot history for context (“what was in the shop the day I hit Champion”).
For longer-term storage, export your SQLite table to CSV monthly and archive it, keeping the live database small speeds up every query. If you’d rather not manage Discord webhooks, swap send_alert() for an email send via Python’s built-in smtplib, or pipe alerts into a service like ntfy.sh for phone push notifications. And if you’re comfortable with basic web development, wrapping plot_progress() in a tiny Flask route turns your local chart into a dashboard you can check from your phone without SSHing in.
One more idea worth trying: log your K/D alongside your estimated tier and look for the point where the two diverge. A rising K/D with a flat tier usually means your matches are getting harder (you’re facing stronger lobbies), which is a more honest signal of improvement than the tier label alone.
If you’re tracking a full squad, consider building a small leaderboard view that ranks your friend group by estimated tier and recent trend, not just current score. A teammate who climbed three divisions this week is a more interesting data point than whoever happens to sit highest today. You could also add a “session” concept on top of the raw snapshots: group entries captured within a few hours of each other into a single play session, then compare score change per session instead of per snapshot, which smooths out the noise from checking the tracker at odd intervals.
Google Sheets is another useful export target if you’d rather share progress with friends than build a dashboard. The gspread package can push a row from your SQLite database into a shared spreadsheet on every cron run, which turns your personal tracker into something a whole squad can glance at without installing anything. Just remember to add the Google service account credentials to the same secrets-handling routine described in the next section, since a spreadsheet API key deserves the same caution as your Discord webhook.
Frequently Asked Questions
Does Epic Games offer an official public stats API?
No. Epic doesn’t publish a self-serve API for pulling individual player stats, which is why every tracker, including this one, relies on the Fortnite Tracker Network’s community-run endpoint instead.
Is the Fortnite Tracker API free to use?
Yes, the free tier requires no email to get started and is rate-limited to roughly one request every two seconds per key, which is plenty for a personal tracker checked a few times a day.
Why doesn’t the API return my exact rank label directly?
The Profile endpoint returns raw stats and percentiles, not a “Bronze” or “Diamond” string. This guide’s estimate_tier() function bridges that gap by mapping your percentile against the published 2026 season distribution.
How many divisions does the current Fortnite ranked ladder have?
22, as of the July 2026 v40.20 update. Bronze, Silver, Gold, Platinum, and Diamond each have three divisions, Elite and Champion were expanded to three divisions each in that update, and Unreal remains a single leaderboard-based tier.
Can I track more than one Epic account with this setup?
Yes. The database schema already keys snapshots by epic_handle, so looping run() over a list of accounts requires no schema changes, just repeated calls with different nicknames.
What happens if I stop playing ranked for a while?
The tracker will keep saving snapshots showing your same score and tier since the underlying stats simply won’t change until you play another ranked match. No errors, just flat data until you queue up again. That’s actually useful data in its own right: a long flat stretch in your chart is an easy visual marker of an off-season break if you ever want to correlate rank progress with how consistently you played.
Can I run this on a Raspberry Pi or a cheap VPS instead of my own machine?
Yes, and it’s a good fit since the whole project is lightweight (a handful of Python files and a SQLite database). Just make sure the environment variables for your API key and webhook URL are set on that machine too.
Why did my rank-up alert fire twice for the same change?
This usually means cron is scheduled to run the script more than once in the same window, often from a leftover crontab entry from testing. Check crontab -l for duplicate lines pointing at the same script.
Does this project work for console players, not just PC?
Yes. The Fortnite Tracker Profile API accepts xbl for Xbox Live and psn for PlayStation Network as platform values alongside pc, so console players can use the exact same client code from Step 4 with no changes beyond the platform argument.
That’s the full build: a data source, a database, a change detector, an alert channel, a schedule, and a chart. None of it depends on paid infrastructure, and the whole project fits comfortably in a handful of small Python files you can read end to end in a few minutes. Start with the twelve steps as written, get one account tracking reliably, then layer in the advanced ideas above once you trust the base pipeline.
Related Coverage
- Fortnite vs Valorant Ranks: 38% Bronze, 25 Tiers [2026]
- Marvel Rivals Rank Tracker Setup: 12 Steps, 30 Min [2026]
- Rainbow Six Siege Stats Tracker Setup: 10 Steps, 30 Min [2026]
- How to Climb Valorant Ranks: 25 Tiers, 12 Steps [2026]
- Deadlock Tier List Tracker: 38 Heroes, 12 Steps [2026]
- More esports coverage on Shattered.io




