Fortnite’s item shop rotates every single day, crossover skins drop with almost no warning, and by the time a headline hits your feed the limited-time back bling is already gone. That churn is exactly why fortnite chapter 7 season 4 content has become one of the most searched topics in the Battle Royale community this month. Chapter 7, Season 4, nicknamed “Override,” landed on August 20, 2026, and it has been stacking crossovers, weapon patches, and shop refreshes ever since. Refreshing the in-game shop by hand is tedious and you will still miss things. This tutorial walks through building your own Python loot tracker, using the free Fortnite-API, so you get pinged the moment new Sonic, Mega Man, Kingdom Hearts, or Persona 5 Royal content hits the game, in 12 steps you can finish in about an hour. You will end up with a script that runs unattended, remembers what it already checked, and posts alerts to Discord instead of a browser tab you have to babysit.
Fortnite Chapter 7 Season 4 Override: What Actually Changed
Chapter 7, Season 4 launched with patch v42.00 on August 20, 2026, and outlets including TimeSaver and GamesBible confirmed the launch date the same day. The season swapped several named locations, replacing Calamari Canyon with the Sonic-themed Green Hill Zone, and added an arcade-flavored cluster of smaller landmarks: Mega Maze, Geno’s Machine, Pac-Man Island, Blinky Island, Pinky Island, Barncade, and Pixel Polys. A follow-up patch, v42.10, shipped September 3, 2026, with three new guns reported by IGN: the Wrecker Revolver, Deadeye Assault Rifle, and Holo Twister Assault Rifle. As of today, September 26, 2026, Fortnite-API’s public build feed lists the live client as Fortnite Release-42.20-CL-58011042, up from the prior 42.10-CL-57819926 build, which confirms the season is already three patches deep.
Crossover content has been the headline story. Live data pulled directly from Fortnite-API on September 26, 2026 confirms Sonic the Hedgehog, Mega Man (Dual Override), Mega Man X, and Zero all shipped as full outfits tagged “Introduced in Chapter 7, Season 4,” alongside a Rush sidekick and a Mega Buster Flex emote. More recently, Kingdom Hearts joined the roster with Sora, Riku, Kairi, and Roxas outfits, and Persona 5 Royal added Joker and Panther skins with matching Shujin Academy and Phantom Thieves back blings. Cyberpunk: Edgerunners contributed a Lucyna Kushinada outfit. Epic also ran creator collabs for streamer Ironmouse and singer Madison Beer through the Icon Series. The season is reported to run through roughly November 1, 2026, though Epic has not published an official closing date.
The named-location list is worth a second look because outlets do not fully agree on it. TimeSaver’s early breakdown named Green Hill Zone, Mega Maze, and Geno’s Machine as the headline additions, while a couple of other write-ups referred to a fourth zone by a different name depending on when they published relative to the season’s rollout. For a tracker, that ambiguity does not matter much, since you are pulling structured cosmetic and shop data directly from the API rather than scraping map screenshots, but it is a good reminder to treat any single gaming outlet’s map breakdown as a snapshot rather than a permanent record.
| Detail | Value |
|---|---|
| Chapter / Season | Chapter 7, Season 4 (“Override”) |
| Launch patch / date | v42.00, August 20, 2026 |
| Latest confirmed patch | v42.10, September 3, 2026 |
| Live build (checked Sept 26, 2026) | Fortnite Release-42.20-CL-58011042 |
| Reported season end | Around November 1, 2026 |
| Confirmed crossovers | Sonic, Mega Man, Kingdom Hearts, Persona 5 Royal, Cyberpunk: Edgerunners |
Why Build Your Own Season 4 Loot Tracker
Manually checking the shop works until you miss a one-day-only item, and Fortnite’s crossover skins have a habit of leaving the rotation before most players notice they arrived. A script solves three problems at once. First, it never forgets to check, since a scheduler runs it on a fixed interval. Second, it gives you a searchable history of every item that has appeared this season, which is useful if you are trying to confirm whether a Kingdom Hearts skin already ran once before. Third, it can alert you the second something new shows up, instead of you finding out from a screenshot on social media an hour later.
There is also a cost angle worth mentioning. If you are the kind of player who checks the shop daily hoping a specific crossover skin reappears, a tracker removes the temptation to buy something similar out of fear of missing it, since you know you will get pinged the moment the exact item you want comes back. That is a small thing, but across a season it adds up to fewer impulse V-Bucks purchases driven by shop anxiety rather than an actual decision to buy.
This also happens to be a solid beginner project for learning API polling patterns you will reuse elsewhere: pagination, rate limiting, state diffing, and webhook notifications all show up in production systems far outside gaming. The pattern is close to what you would use for tracking rank changes in another title, just pointed at a different API and a different set of fields.
How This Ties Into Competitive Fortnite
Cosmetics do not affect gameplay, so a competitive player has no real reason to care whether Sora’s outfit is in today’s shop. Weapon and item patches are a different story, and that split is exactly why this tracker separates cosmetic tracking from patch tracking instead of treating everything as one undifferentiated feed.
Why Weapon Patches Matter More Than Cosmetics for Ranked Players
The Wrecker Revolver, Deadeye Assault Rifle, and Holo Twister Assault Rifle that shipped in v42.10 on September 3, 2026 changed loadout decisions in ranked and tournament play the moment they landed, since a new weapon with strong stats can push older guns out of competitive rotation within days. Build tracking, which Step 9 covers, gives you a heads-up the moment a new patch drops, before official patch notes are even fully translated and published. That lead time is small, often just a few hours, but it matters if you stream, coach, or write about the competitive scene and want to be first to flag a loadout shift.
Where to Check Official FNCS Schedules
This tutorial’s tracker does not pull tournament schedules, since Fortnite-API’s public endpoints focus on cosmetics, shop, and build data rather than competitive event calendars. For FNCS brackets, qualification windows, and prize-pool details tied to Chapter 7, Season 4, check Epic’s official competitive announcements directly rather than relying on a third-party API, since tournament formats change between seasons and a stale hardcoded schedule in your own script would be worse than no schedule at all.
Understanding Rarity, Series, and Set: The Fields That Make Filtering Work
Before writing the filtering logic in Steps 4 and 5, it helps to know what each Fortnite-API field actually represents, since the three that look similar, rarity, series, and set, mean different things and mixing them up is a common source of bugs.
- rarity describes an item’s tier, such as Common, Rare, Epic, or Legendary, and exists independently of any crossover status.
- series groups items into marketing collections like Gaming Legends Series, Icon Series, or Marvel Series. Every confirmed crossover outfit checked for this guide, including Sonic, Mega Man, Kingdom Hearts, and Persona 5 Royal, carries the Gaming Legends Series tag.
- set is the narrowest grouping, tying together every cosmetic tied to one specific character or theme, such as everything belonging to the “Sonic the Hedgehog” set: the outfit, its back bling, and matching pickaxe.
Filtering on series rather than rarity is what keeps the tracker from also flagging every unrelated Epic-rarity item added that week, and filtering on set rather than a name substring is what keeps a search for “Sonic” from also matching unrelated items that happen to share a word in their description.
One more field worth checking before you finalize your filters is backendValue, nested inside both rarity and introduction. It is a stable numeric or string identifier that does not change even if Epic later renames a display label, which makes it a safer long-term key to store in your database than the human-readable text fields if you plan to keep this tracker running across multiple future seasons.
Prerequisites: Tools, Accounts, and Versions
- Python 3.11 or newer (3.12 works fine too)
- pip and venv, both bundled with standard Python installs
- The
requestslibrary, version 2.32.3 or newer, for HTTP calls - A free Fortnite-API account is optional. The cosmetics and shop endpoints used in this guide do not require an API key, only the player-stats endpoints do
- A Discord server where you can create a webhook, if you want push alerts (skip this if you prefer console output only)
- A code editor (VS Code, PyCharm, or anything with syntax highlighting)
- Roughly 60 minutes and a stable internet connection
You do not need a Fortnite account, an Epic developer key, or admin access to anything. Fortnite-API is a free, community-run REST service that mirrors the game’s cosmetic, shop, and build data, and the endpoints this tutorial uses are open to anonymous requests.
Confirming Your Versions Before You Start
Two minutes of version checking now saves a confusing debugging session later, especially if you have more than one Python install on your machine. Run both of these before creating the virtual environment in Step 1.
python3 --version
pip3 --version
You want to see 3.11 or higher on the first line. If your system defaults to an older Python 3.x release, install a current version from python.org or through your OS package manager before continuing, since the f-string and pathlib syntax used later in this guide assumes a reasonably modern interpreter.
Step 1: Set Up Your Python Project
Start with a clean virtual environment so the tracker’s dependencies stay isolated from anything else on your machine. Create a project folder, activate a venv, and install the one external library you need.
mkdir fortnite-loot-tracker
cd fortnite-loot-tracker
python3 -m venv venv
source venv/bin/activate
pip install requests==2.32.3
On Windows, replace the activation line with venv\Scripts\activate. Once the environment is active, your terminal prompt should show (venv) at the start of the line, confirming pip installs are scoped to this project only.
Step 2: Explore the Fortnite-API Endpoints
Before writing any Python, poke at the raw API with curl so you know what the JSON actually looks like. The two endpoints doing most of the work here are /v2/cosmetics/new, which returns everything added in the most recent build, and /v2/shop, which returns today’s item shop rotation.
curl "https://fortnite-api.com/v2/cosmetics/new"
A trimmed real response, captured September 26, 2026, looks like this:
{
"data": {
"build": "++Fortnite+Release-42.20-CL-58011042",
"previousBuild": "++Fortnite+Release-42.10-CL-57819926",
"lastAdditions": { "br": "2026-09-25T13:00:42Z" },
"items": {
"br": [
{
"name": "Sora",
"type": { "displayValue": "Outfit" },
"series": { "value": "Gaming Legends Series" },
"introduction": { "chapter": "7", "season": "4" }
}
]
}
}
}
Notice the series.value field. Crossover items from other franchises are consistently tagged “Gaming Legends Series” in the live data, which gives you a clean way to filter for exactly the content you care about without hardcoding a list of character names.
It is worth spending ten minutes running a few more curl commands against different query parameters before you write a single line of Python. Try the shop endpoint, try a search with matchMethod=full versus contains, and look at how a bundle entry differs from a single-item entry in the shop response. Understanding the shape of the data up front means the functions you write in the coming steps will handle edge cases correctly the first time, instead of needing a rewrite after the tracker crashes on a bundle entry that does not have the field you assumed every entry would have.
Step 3: Build a Reusable API Client
Wrap the raw requests in a small client class. This keeps your later code readable and gives you one place to handle timeouts and errors instead of repeating that logic in every function.
import requests
class FortniteAPIClient:
BASE_URL = "https://fortnite-api.com"
def __init__(self, timeout=10):
self.timeout = timeout
self.session = requests.Session()
def _get(self, path, params=None):
url = f"{self.BASE_URL}{path}"
response = self.session.get(url, params=params, timeout=self.timeout)
response.raise_for_status()
return response.json()["data"]
def get_new_cosmetics(self):
return self._get("/v2/cosmetics/new")
def get_shop(self):
return self._get("/v2/shop")
def search_cosmetics(self, name, match_method="contains"):
params = {"name": name, "matchMethod": match_method}
return self._get("/v2/cosmetics/br/search/all", params=params)
The raise_for_status() call matters more than it looks. Without it, a failed request silently returns an error page instead of the JSON you expect, and your tracker will crash a few lines later with a confusing KeyError instead of a clear network error.
Step 4: Pull This Season’s New Cosmetics
With the client in place, grab everything the game added recently and narrow it down to items tagged for the current chapter and season. The API’s introduction field makes this a simple comparison rather than fragile string matching.
def get_season_items(client, chapter="7", season="4"):
data = client.get_new_cosmetics()
items = data["items"].get("br", [])
return [
item for item in items
if item.get("introduction", {}).get("chapter") == chapter
and item.get("introduction", {}).get("season") == season
]
Running this today returns well over a hundred items for Chapter 7, Season 4, spanning outfits, back blings, emotes, and pickaxes. That volume is exactly why filtering matters in the next step.
Step 5: Filter for Crossover and Collab Items
Most players tracking this season only care about the crossover skins, not every recolored back bling. Filter on the series.value field to isolate Gaming Legends Series items, then group by the set field to see which franchise each item belongs to.
def get_crossovers(season_items):
crossovers = [
item for item in season_items
if item.get("series", {}).get("value") == "Gaming Legends Series"
]
by_set = {}
for item in crossovers:
set_name = item.get("set", {}).get("value", "Unknown")
by_set.setdefault(set_name, []).append(item["name"])
return by_set
Confirmed franchises pulled this way as of September 26, 2026 include Sonic the Hedgehog, Mega Man, Kingdom Hearts, Persona 5 Royal, and Cyberpunk: Edgerunners. Two creator collabs, Ironmouse and Madison Beer, sit outside Gaming Legends Series and use the Icon Series tag instead, so add a second check if you want those included too.
| Franchise / Series | Confirmed Item Example | Item Type |
|---|---|---|
| Sonic the Hedgehog | Sonic the Hedgehog | Outfit |
| Mega Man | Mega Man X, Zero, Rush | Outfit / Sidekick |
| Kingdom Hearts | Sora, Riku, Kairi, Roxas | Outfit |
| Persona 5 Royal | Joker (P5R), Panther (P5R) | Outfit |
| Cyberpunk: Edgerunners | Lucyna Kushinada | Outfit |
| Icon Series (creators) | Ironmouse, Madison Beer | Outfit |
Step 6: Snapshot and Diff the Daily Item Shop
Cosmetics get added to the game files before they hit the shop, sometimes weeks earlier through data mining. If you specifically want to know when something goes live for purchase, track the shop endpoint instead, and diff each day’s entries against the previous snapshot by their unique offerId.
def diff_shop(old_entries, new_entries):
old_ids = {e["offerId"] for e in old_entries}
new_ids = {e["offerId"] for e in new_entries}
added = [e for e in new_entries if e["offerId"] not in old_ids]
removed = [e for e in old_entries if e["offerId"] not in new_ids]
return added, removed
The live shop as of today carries a hash of 03acd4ec5ff0c2d4 and updates once every 24 hours, so checking every 30 to 60 minutes is frequent enough to catch a rotation without hammering the API.
Step 7: Persist State Between Runs
A diff is only useful if you remember what you saw last time. Save each run’s results to a local JSON file so the script has something to compare against the next time it wakes up, even after a reboot.
import json
from pathlib import Path
STATE_FILE = Path("shop_state.json")
def load_state():
if STATE_FILE.exists():
return json.loads(STATE_FILE.read_text())
return {"entries": []}
def save_state(entries):
STATE_FILE.write_text(json.dumps({"entries": entries}, indent=2))
Keep this file out of version control if you plan to push the project to GitHub. It changes every run and adds noise to your commit history for no benefit.
Step 8: Send Discord Alerts When Loot Changes
Console output is fine while you are testing, but you want a push notification once the tracker runs unattended. Discord webhooks are the simplest option since they need no bot setup, just a URL from your server’s integration settings, documented in Discord’s webhook API reference.
def send_discord_alert(webhook_url, added_items):
if not added_items:
return
lines = [f"- {item['devName']}" for item in added_items]
message = "New Fortnite shop items:\n" + "\n".join(lines)
requests.post(webhook_url, json={"content": message[:1900]}, timeout=10)
Example alert text your Discord channel would receive:
New Fortnite shop items:
- 1 x Poker Face for 500 MtxCurrency
- 1 x Sora Outfit for 1500 MtxCurrency
The message[:1900] slice is not decorative. Discord rejects webhook payloads over 2000 characters, and a shop refresh with a dozen new entries can blow past that limit on a busy day.
Step 9: Track Weapon and Patch History
Cosmetics are not the only thing worth logging this season. Compare the build field from get_new_cosmetics() against your last saved value, and log a new row whenever it changes, so you end up with a running patch history for Chapter 7, Season 4 without any manual note-taking.
def check_for_new_patch(client, last_known_build):
data = client.get_new_cosmetics()
current_build = data.get("build", "")
if current_build and current_build != last_known_build:
print(f"New patch detected: {current_build}")
return current_build
return last_known_build
Store the returned value in your state file next to the shop entries, and check it on every run alongside the shop diff. Because the build string embeds a changelist number (the CL-58011042 portion), you get a precise, sortable record of exactly when each patch went live, not just a rough date pulled from a news article.
| Patch | Date | Notable Addition |
|---|---|---|
| v42.00 | August 20, 2026 | Season launch, Green Hill Zone, Sonic and Mega Man crossover |
| v42.10 | September 3, 2026 | Wrecker Revolver, Deadeye Assault Rifle, Holo Twister Assault Rifle |
| v42.20 | Confirmed live Sept 26, 2026 | Kingdom Hearts and Persona 5 Royal crossover additions |
Step 10: Automate the Tracker With a Scheduler
A script you have to remember to run manually is not much better than checking the shop by hand. On Linux or macOS, cron handles the scheduling with one line. Test the syntax first at crontab.guru if you are not sure it will fire when you expect.
*/30 * * * * /path/to/fortnite-loot-tracker/venv/bin/python /path/to/fortnite-loot-tracker/tracker.py >> tracker.log 2>&1
On Windows, Task Scheduler does the same job through a GUI: point it at your venv’s python.exe and the script path, then set a 30-minute recurring trigger. Either way, redirect output to a log file so you have something to check when the alerts stop showing up.
Step 11: Export an HTML or CSV Report
Real-time alerts are great, but a standing report you can glance at is useful too, especially if you want to share a season summary with a Discord community or a friend group. A CSV export takes about five lines and opens cleanly in any spreadsheet app.
import csv
def export_csv(items, path="season_crossovers.csv"):
with open(path, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["name", "set", "type", "added"])
for item in items:
writer.writerow([
item["name"],
item.get("set", {}).get("value", ""),
item.get("type", {}).get("displayValue", ""),
item.get("added", ""),
])
Step 12: Test, Validate, and Handle API Failures
Before you trust the tracker to run unattended for weeks, force it to fail on purpose. Disconnect your network and run it, feed it a corrupted state file, and confirm the script logs a clear error instead of crashing silently or, worse, sending a false “everything is new” alert because it read an empty state file as a blank slate.
try:
data = client.get_new_cosmetics()
except requests.exceptions.RequestException as exc:
print(f"API request failed: {exc}")
raise SystemExit(1)
Wrapping the top-level call this way means a cron job that fails shows up clearly in your log file with a timestamp and an exit code, instead of a wall of a Python traceback that is easy to miss when you are skimming logs at 2 a.m.
It also helps to write a small manual test file with a handful of fake shop entries so you can verify diff_shop() catches additions and removals correctly without waiting for the real shop to refresh. Feed it two lists that differ by one offerId, confirm the function returns exactly that one entry, then feed it two identical lists and confirm it returns an empty list. Five minutes of this kind of manual testing catches off-by-one bugs in the diffing logic that would otherwise only show up days later when a real item quietly slips through untracked.
Common Pitfalls When Tracking Live Fortnite Data
Most of the bugs people hit building a tracker like this are not Python problems, they are assumptions about the data that turn out to be wrong once the season moves on. These six show up often enough to call out before you run into them yourself.
- Hardcoding character names. A list like
["Sonic", "Mega Man"]breaks the moment Epic adds a franchise you did not predict. Filter onseries.valueinstead. - Comparing items by name instead of ID. Fortnite reuses cosmetic names across seasons (“Sonic Splash” exists as both a back bling and a pickaxe). Always key your diffs off
idorofferId. - Polling too aggressively. The shop only refreshes once every 24 hours. Checking every minute wastes requests and adds no new information.
- Assuming the introduction field is always populated. A small number of older or bundled items omit chapter and season data entirely, so check with
.get()rather than direct dictionary access or you will hit aKeyErroreventually. - Treating third-party player counts as official. Independent trackers estimated roughly 1.7 million average concurrent Fortnite players in September 2026, down about 24.5% from August, but Epic Games does not publish these figures itself, so label any player-count claim in your own notes as an estimate.
- Forgetting time zones in your state file. Fortnite-API timestamps are UTC. If your diff logic silently converts to local time somewhere, items can appear to “add” or “remove” twice around midnight.
Troubleshooting Guide
If something in the tracker misbehaves, work through this list before assuming the API itself is broken. Nine times out of ten the cause is on the local side: a misconfigured path, a stale webhook, or a state file that never got written.
- Script returns a 429 error. You are being rate limited. Add a short delay between requests and avoid running the tracker more than once every 15 minutes.
- KeyError on “data”. The API returned an error payload instead of a normal response, usually because a query parameter was misspelled. Print the raw response body before parsing it.
- Discord webhook returns 401. The webhook URL was regenerated or deleted in Discord’s server settings. Create a new one and update your config.
- No new items ever show up. Double-check your chapter and season filter values are strings, not integers. The API returns “7” and “4” as text, not numbers.
- Cron job never runs. Cron uses a minimal environment that often lacks your shell’s PATH. Always use the full path to your venv’s Python interpreter, not just
python3. - CSV export has garbled characters. Open the file with UTF-8 encoding explicitly, since some cosmetic descriptions include non-ASCII punctuation that breaks default Windows encodings.
- State file grows without bound. If you are appending every run’s items without trimming, the JSON file will bloat over weeks. Store only the latest snapshot, not a full history, unless you specifically want an archive.
- Alerts fire for items you already saw. This almost always means
save_state()is not being called after a successful run, so the script keeps comparing against the same stale snapshot. - SSL certificate errors on older systems. Update your
certifipackage (pip install --upgrade certifi). Outdated root certificates are a common cause on machines that have not run pip in a while.
Advanced Tips for Scaling Your Tracker
Once the basic version works, a few upgrades make it genuinely useful long-term. Cache API responses locally with a short TTL (5 to 10 minutes) if you run the script from more than one machine, so you are not duplicating requests against the same endpoint. Add a lightweight SQLite database instead of a flat JSON file once your history grows past a few hundred entries, since querying “every Kingdom Hearts item added this season” becomes a one-line SQL statement in SQLite instead of a manual loop over a JSON blob. If you want stats tracking alongside cosmetics, note that player-stats endpoints require a free API key from Fortnite-API’s dashboard, tied to an Epic account login, unlike the cosmetics and shop endpoints used throughout this guide.
Rate limiting deserves a second mention here because it is the single most common way a hobby tracker gets itself blocked. Fortnite-API is free but community-funded, and hammering it with requests every few seconds from a tight loop is a fast way to get your IP throttled or banned, which then breaks the tracker for everyone else testing from the same network. Space requests at least a few minutes apart, add exponential backoff around the try/except block from Step 12 instead of retrying immediately on failure, and set a descriptive User-Agent header on your requests session so the API maintainers can identify traffic if something does go wrong on their end.
The diffing and webhook-alert pattern in this guide also transfers directly to trackers for other live-service games. Swap the base URL and the field names in FortniteAPIClient, and the same snapshot-compare-alert structure works against almost any public game-data API that returns JSON.
The Complete Project: Full Script
Putting every piece from the steps above together gives you a single runnable file. Save this as tracker.py in your project folder alongside the venv you created in Step 1.
import json
import requests
from pathlib import Path
STATE_FILE = Path("shop_state.json")
WEBHOOK_URL = "YOUR_DISCORD_WEBHOOK_URL"
class FortniteAPIClient:
BASE_URL = "https://fortnite-api.com"
def __init__(self, timeout=10):
self.timeout = timeout
self.session = requests.Session()
def _get(self, path, params=None):
r = self.session.get(f"{self.BASE_URL}{path}", params=params, timeout=self.timeout)
r.raise_for_status()
return r.json()["data"]
def get_shop(self):
return self._get("/v2/shop")
def load_state():
if STATE_FILE.exists():
return json.loads(STATE_FILE.read_text())
return {"entries": []}
def save_state(entries):
STATE_FILE.write_text(json.dumps({"entries": entries}, indent=2))
def diff_shop(old_entries, new_entries):
old_ids = {e["offerId"] for e in old_entries}
return [e for e in new_entries if e["offerId"] not in old_ids]
def send_discord_alert(added):
if not added or WEBHOOK_URL.startswith("YOUR_"):
return
lines = [f"- {item['devName']}" for item in added]
message = "New Fortnite shop items:\n" + "\n".join(lines)
requests.post(WEBHOOK_URL, json={"content": message[:1900]}, timeout=10)
def main():
client = FortniteAPIClient()
try:
shop = client.get_shop()
except requests.exceptions.RequestException as exc:
print(f"API request failed: {exc}")
raise SystemExit(1)
state = load_state()
added = diff_shop(state["entries"], shop["entries"])
if added:
print(f"{len(added)} new item(s) found:")
for item in added:
print(f" - {item['devName']}")
send_discord_alert(added)
else:
print("No new items since last check.")
save_state(shop["entries"])
if __name__ == "__main__":
main()
Run it once to establish a baseline (python tracker.py), then let the cron job from Step 10 take over. The first run always reports every current shop entry as “new,” since there is no prior state to compare against, so do not be alarmed by a long list on that first execution.
From here, the natural next additions are the crossover-filtering and CSV-export functions from Steps 4, 5, and 11, wired into the same main() function so one run of the script updates your shop history, flags new crossover skins, and refreshes your CSV report in a single pass. Keep the pieces as separate functions rather than one long block, since that separation is what makes it easy to test each piece independently and swap out one part, like the Discord alert, for something else, like a Slack webhook or a simple log file, without touching the rest of the script.
Frequently Asked Questions
Do I need an Epic Games account to use Fortnite-API?
No. The cosmetics, shop, and new-items endpoints used throughout this guide are open to anonymous requests. An account and API key are only required for player-stats lookups, which this tutorial does not use.
How often does the Fortnite item shop actually refresh?
Once every 24 hours, based on the shop hash and date fields returned by the API. Polling every 30 to 60 minutes is frequent enough to catch a refresh without wasting requests.
Is Kingdom Hearts confirmed as a Fortnite Chapter 7 Season 4 crossover?
Yes. Live Fortnite-API data checked on September 26, 2026 lists Sora, Riku, Kairi, and Roxas as outfits tagged “Introduced in Chapter 7, Season 4” under the Gaming Legends Series.
Can this tracker run on a Raspberry Pi or a cheap VPS?
Yes. The script has one dependency (requests) and uses almost no memory or CPU, so it runs comfortably on the smallest available Raspberry Pi models or a low-cost VPS.
Why use Discord webhooks instead of email alerts?
Webhooks require no authentication beyond a single URL and post instantly, while email needs SMTP credentials and is slower to reach you. Discord’s webhook API is also free with no message-sending limits for this kind of low-volume use.
What happens if Epic changes the API response format?
Fortnite-API is community-maintained and independent of Epic, so it can lag behind or adjust field names after a major patch. Wrapping every field access in .get() with a default value, as shown throughout this guide, keeps small schema changes from crashing your tracker outright.
Does this tracker work for chapters or seasons other than the current one?
Yes. Change the chapter and season arguments passed to get_season_items() to any past values, and the same filtering logic works against Fortnite-API’s historical cosmetic data.
Is using Fortnite-API against Epic Games’ terms of service?
Fortnite-API is an independent, community-run project, not an official Epic Games service, and it only exposes read-only data that mirrors what any player can already see in-game or in the shop. This tutorial’s script performs simple, low-frequency GET requests for personal tracking, similar in spirit to checking a public webpage, but you should review Epic’s current terms yourself before deploying anything beyond personal use.




