Type “fortnite update today time” into Google and you get a wall of contradictory answers: a Reddit thread from three seasons ago, a status-tracker site that hasn’t refreshed in hours, and an official account that posted once and went quiet. None of those single sources is wrong exactly, they’re just incomplete. Epic Games can mark a service “operational” on its status page while players are still stuck on a login screen, and a third-party tracker can show green long after matchmaking actually dropped.
This tutorial builds something different: a small Python tool that checks three independent sources at once, Epic’s own status feed, the Fortnite-API.com build data, and the official X account, then reconciles them into one verdict instead of asking you to interpret three browser tabs yourself. By the end you’ll have a working CLI, a cache layer that keeps you from getting rate-limited, and a scheduled job that can ping Discord or Slack the moment the picture actually changes. The result is a reusable, scriptable answer to the fortnite update today time question, rather than a manual check you repeat every patch day. Every endpoint used here was live and returning data as of October 7, 2026.
The reason a fortnite update today time search rarely settles the question in one click is that “down” isn’t a single boolean. Login can be fine while matchmaking queues time out. The item shop can load while party formation fails. A verdict that collapses all of that into “up” or “down” is lying to you by omission, and a verdict that just repeats “check back later” isn’t much better. What you actually want is a tool that reads the same structured data Epic’s own engineers look at, cross-checks it against independent signals, and tells you which specific piece is broken and since when. That’s the bar this build is aiming for, and it’s achievable with three public endpoints and under 200 lines of Python.
What’s Live Right Now: Chapter 7 Season 4 and Its Update Cadence
As of this writing, Fortnite is running Chapter 7, Season 4, “Override,” which launched August 20, 2026 as version v42.00. Epic has kept a roughly biweekly cadence since: v42.10 on September 3, v42.20 on September 17, and v42.30 on October 1, the patch that shipped the Fortnitemares 2026 event. Knowing that cadence matters for the tool we’re building, because it tells you when it’s worth polling aggressively (around the two-week mark) versus when you can safely back off and save API calls.
Epic’s own status page for the v42.30 rollout stated: “Scheduled downtime for v42.30 begins at 4 AM ET (8 AM UTC),” and added that “Matchmaking will be disabled 30 minutes prior,” according to the Epic Games Public Status page. That’s a useful pattern: Epic consistently telegraphs maintenance in Eastern Time, then matchmaking goes dark first, then login and other services follow. The official Fortnite Status account mirrored similar language for the prior update, posting that “Downtime for v42.20 starts at 4 AM ET with matchmaking ending shortly beforehand,” per a post on the Fortnite Status X account.
| Version | Date | Context |
|---|---|---|
| v40.00 | March 19, 2026 | Chapter 7 Season 2 launch |
| v41.30 | July 30, 2026 | Extended maintenance, roughly 7 hours |
| v42.00 | August 20, 2026 | Chapter 7 Season 4 “Override” launch |
| v42.10 | September 3, 2026 | Mid-season content update |
| v42.20 | September 17, 2026 | Mid-season content update |
| v42.30 | October 1, 2026 | Fortnitemares 2026 event |
Routine mid-season patches have run about 2.5 to 3 hours lately, while the July 2026 v41.30 maintenance window stretched closer to seven hours. That variance is exactly why a single scheduled-time estimate isn’t enough. You need to check whether a window is still scheduled, already in progress, or quietly extended, and that’s the gap this project fills.
It’s also worth noting what the biweekly cadence does and doesn’t tell you. A roughly two-week gap between patches is a pattern observed across the last several Chapter 7 Season 4 releases, not a contractual promise from Epic. Major season transitions, like the jump from v40.00 at the start of Season 2 in March, have historically disrupted that rhythm with longer gaps beforehand and longer downtime during the transition itself. If you’re extending this project, it’s reasonable to build in a simple heuristic that treats the days immediately before and after an expected two-week mark as a higher-alert window, and poll a little more aggressively during those stretches, while backing off the rest of the time.
How Epic’s Status Page Actually Works Under the Hood
Before writing the fetch functions, it’s worth understanding the data model you’re pulling from, since it shapes every decision later in the build. Epic’s status page runs on a Statuspage-style backend, the same category of system used by thousands of SaaS companies to publish uptime. That means the data isn’t bespoke to Fortnite, it follows a predictable schema: a page object, a flat list of components, a list of incidents, and a separate list of scheduled maintenances.
Components are organized as a shallow tree. The top-level “Fortnite” entry is itself a group that rolls up several child components, things like login, matchmaking, the website, and cloud saves. Each child reports its own status independently: operational, degraded_performance, partial_outage, or major_outage. A parent group typically reflects the worst status among its children, which is why checking the group alone can mask which specific service is actually the problem. If you want granular alerts, “matchmaking is degraded” versus “login is fully down,” you’ll eventually want to walk the component list in get_epic_summary() rather than just reading the top-level group, something worth adding once the base version from this tutorial is working.
Incidents follow their own lifecycle: investigating, identified, monitoring, then resolved. Scheduled maintenances move through scheduled, in_progress, verifying, and completed. Both lifecycles matter for a verifier like the one in this tutorial, because a maintenance window that’s sitting in verifying often means the update has technically finished but Epic hasn’t yet confirmed full stability, a state worth surfacing separately from a flat “it’s done” message if you extend the reconciliation logic later.
Prerequisites: What You Need Before You Start
You don’t need a game studio’s infrastructure for this, just a terminal and about 40 minutes. Here’s the exact toolchain this tutorial assumes:
- Python 3.11 or newer (we use the standard-library
zoneinfomodule introduced in 3.9, and f-string improvements from 3.11) pip23 or newerrequests2.31 or newerclick8.1 or newer for the CLI layertenacity8.2 or newer for retry handling- A free-tier X developer account if you want to pull live posts from
@FortniteStatusprogrammatically (search-tier access, not the full firehose) - A Linux, macOS, or WSL environment with
cronorsystemdavailable for scheduling - On Windows without WSL, install the
tzdatapackage alongsidezoneinfo, since Windows doesn’t ship an IANA time zone database
Nothing here requires an Epic Games developer account. The status endpoint we’re using is public and unauthenticated, which is part of why it’s the most reliable leg of this three-source approach.
Architecture: Three Sources, One Verdict
Before writing code, it helps to know what each source is actually good for, because none of them is complete on its own.
| Source | Authoritative for | Typical latency | Auth required |
|---|---|---|---|
| Epic Games Public Status | Official uptime, incidents, scheduled maintenance | Seconds to a few minutes | None, public JSON API |
| Fortnite-API.com | Build/version context, in-game news, shop rotation | Minutes | None for basic read endpoints |
| X (@FortniteStatus) | Human-readable context, extensions, workaround advice | Real-time once posted | Bearer token, developer account |
The Epic status feed is the one that actually knows whether login and matchmaking are up. Fortnite-API.com won’t tell you uptime at all, its news endpoint just confirms what season or event is currently active, which is useful for sanity-checking that you’re reading status for the right build. The X account fills the gap both of the others miss: plain-language notes like “we’re extending downtime” that don’t show up as a structured status field. Combining all three, with the official status feed weighted highest, is what makes the final verdict trustworthy enough to alert on.
Why Build This Instead of Using a Browser Extension or Tracker Site
It’s a fair question, since plenty of third-party tracker sites already exist for exactly this purpose. The trouble is what happens under the hood on most of them: they scrape a rendered status page with a headless browser, cache the result for longer than they advertise, and present it with no indication of which upstream source it actually came from. When StatusGator’s Epic Games page says a service is operational, that’s a reasonable secondary data point, but it’s still one step removed from Epic’s own structured feed, and it inherits whatever polling interval StatusGator itself uses, not yours.
Going straight to the /api/v2/ JSON endpoints skips that layer entirely. There’s no HTML to parse, no risk of a front-end redesign silently breaking your scraper, and no dependency on a third party’s own uptime. The tradeoff is that you own the polling schedule and the alerting logic yourself, which is exactly the point: a script you control can be tuned to your own risk tolerance, whether that’s a three-minute interval for a competitive Discord server or a thirty-minute interval if you just want a daily digest. A hosted tracker can’t offer that kind of control, and it can’t tell you, the way this project’s reconciliation function can, when a signal is unconfirmed versus independently corroborated by a second source.
There’s also a practical reliability argument. A site you don’t control can change its markup, get rate-limited itself, or simply go down during the exact event you’re trying to monitor, Epic’s own status incidents have, on occasion, coincided with spikes in traffic to third-party trackers that then struggled to keep up. Hitting the same structured API Epic’s own status dashboard reads from removes that extra point of failure.
Step 1-4: Scaffold the Project and Read Epic’s Official Status Feed
Step 1: Create the project structure
Start with a clean folder and a virtual environment. Keeping sources, normalization, and reconciliation logic in separate modules makes the reconciliation step easy to test independently of network calls.
mkdir fn-verify && cd fn-verify
python3 -m venv .venv
source .venv/bin/activate
mkdir fn_verify
touch fn_verify/__init__.py fn_verify/sources.py fn_verify/normalize.py \
fn_verify/reconcile.py fn_verify/cache.py fn_verify/cli.py requirements.txt
Step 2: Install dependencies
# requirements.txt
requests>=2.31
click>=8.1
tenacity>=8.2
tzdata>=2024.1; sys_platform == "win32"
Install with pip install -r requirements.txt. The tzdata line only installs on Windows, since macOS and most Linux distributions already ship the IANA database that zoneinfo reads from.
Step 3: Read Epic’s official status feed
Epic’s status page at status.epicgames.com runs on a Statuspage-style backend, which exposes a public JSON API. No key, no login. The summary endpoint returns every component’s current state, including the top-level Fortnite group.
# fn_verify/sources.py
import requests
EPIC_STATUS_BASE = "https://status.epicgames.com/api/v2"
FORTNITE_API_BASE = "https://fortnite-api.com/v2"
def get_epic_summary(timeout=10):
resp = requests.get(f"{EPIC_STATUS_BASE}/summary.json", timeout=timeout)
resp.raise_for_status()
return resp.json()
def get_fortnite_component(summary):
for component in summary.get("components", []):
if component.get("name") == "Fortnite":
return component
return None
Run python -c "from fn_verify.sources import get_epic_summary; print(get_epic_summary()['page'])" and you should see a page object confirming the feed is live and timestamped in UTC. That timestamp matters, it tells you how fresh the data is before you even look at component status.
Step 4: Pull scheduled maintenance and open incidents
The summary endpoint tells you current state, but it won’t tell you about a maintenance window that’s been announced but hasn’t started yet. For that you need two more endpoints from the same API.
# fn_verify/sources.py (continued)
def get_epic_scheduled_maintenance(timeout=10):
resp = requests.get(
f"{EPIC_STATUS_BASE}/scheduled-maintenances/upcoming.json", timeout=timeout
)
resp.raise_for_status()
return resp.json().get("scheduled_maintenances", [])
def get_epic_unresolved_incidents(timeout=10):
resp = requests.get(f"{EPIC_STATUS_BASE}/incidents/unresolved.json", timeout=timeout)
resp.raise_for_status()
return resp.json().get("incidents", [])
An empty scheduled_maintenances array doesn’t mean nothing is happening soon, it means Epic hasn’t posted an announcement yet. Epic typically announces a window a few hours ahead, not days, so an empty array at 9 PM the night before an update is completely normal and not a bug in your code.
Step 5-7: Add Build Context and Watch X Without Getting Rate-Limited
Step 5: Add build context from Fortnite-API.com
This source won’t tell you uptime, but it confirms which season and content update is currently active, which is useful context to attach to any alert you send. If you’re fielding “is Fortnite down” questions from a Discord server, knowing it’s the Fortnitemares event build, not some unrelated incident, changes how you answer.
# fn_verify/sources.py (continued)
def get_fortnite_news(timeout=10):
resp = requests.get(f"{FORTNITE_API_BASE}/news", timeout=timeout)
resp.raise_for_status()
payload = resp.json()
return payload.get("data", {})
Fortnite-API.com is a third-party project, not an Epic Games product, so treat its uptime claims (if it makes any) as informational, never authoritative. Its real value here is contextual labeling, not status verification.
Step 6: Watch the official X account
The @FortniteStatus account posts plain-language notes that structured APIs don’t capture, extensions, workarounds, DNS fixes. Pulling it programmatically means authenticating against the X API v2 recent-search endpoint with a bearer token tied to a developer app.
# fn_verify/sources.py (continued)
import os
X_API_BASE = "https://api.x.com/2"
def get_recent_status_posts(max_results=10, timeout=10):
bearer_token = os.environ["X_BEARER_TOKEN"]
headers = {"Authorization": f"Bearer {bearer_token}"}
params = {
"query": "from:FortniteStatus -is:retweet",
"max_results": max_results,
"tweet.fields": "created_at",
}
resp = requests.get(
f"{X_API_BASE}/tweets/search/recent", headers=headers, params=params, timeout=timeout
)
resp.raise_for_status()
return resp.json().get("data", [])
Set the token with export X_BEARER_TOKEN="your-token-here" before running anything that calls this function. Never hardcode it in sources.py, since that file is the one you’re most likely to paste into a bug report or a gist later.
Step 7: Normalize timestamps across ET, UTC, and DST
Epic posts times in Eastern Time, which shifts between EST (UTC-5) and EDT (UTC-4) depending on the calendar. Hardcoding an offset works fine until the clocks change and your “4 AM ET” conversion is suddenly an hour off. Use the IANA time zone database through zoneinfo instead of a fixed offset.
# fn_verify/normalize.py
from datetime import datetime
from zoneinfo import ZoneInfo
EASTERN = ZoneInfo("America/New_York")
UTC = ZoneInfo("UTC")
def eastern_clock_time_to_utc(date_str, hour, minute=0):
"""date_str like '2026-10-07'; returns a timezone-aware UTC datetime."""
naive = datetime.strptime(date_str, "%Y-%m-%d").replace(hour=hour, minute=minute)
eastern_dt = naive.replace(tzinfo=EASTERN)
return eastern_dt.astimezone(UTC)
def parse_iso(timestamp):
return datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
Because ZoneInfo("America/New_York") carries the full DST ruleset, this function returns the correct UTC time whether you run it in January or July, with no manual offset math anywhere in the codebase.
Step 8-11: Reconcile, Cache, Expose a CLI, and Schedule It
Step 8: Build the three-source reconciliation engine
This is the core logic: weigh the official status feed highest, treat an open incident or scheduled window as corroborating evidence, and use X posts as a tiebreaker when the structured data is ambiguous.
# fn_verify/reconcile.py
from enum import Enum
class Verdict(Enum):
OPERATIONAL = "operational"
SCHEDULED = "scheduled_downtime"
IN_PROGRESS = "downtime_in_progress"
UNCONFIRMED = "unconfirmed"
def reconcile(epic_component, scheduled_maintenance, unresolved_incidents, x_posts):
epic_says_down = bool(epic_component) and epic_component["status"] != "operational"
has_scheduled = len(scheduled_maintenance) > 0
has_incident = len(unresolved_incidents) > 0
x_mentions_downtime = any(
"downtime" in post.get("text", "").lower()
or "maintenance" in post.get("text", "").lower()
for post in x_posts
)
corroborating_signals = sum([has_incident, x_mentions_downtime])
if epic_says_down or has_incident:
return Verdict.IN_PROGRESS if corroborating_signals >= 1 else Verdict.UNCONFIRMED
if has_scheduled:
return Verdict.SCHEDULED
return Verdict.OPERATIONAL
Notice that a single X post mentioning “maintenance” never flips the verdict to IN_PROGRESS on its own, it only counts as corroboration once the status feed already disagrees with “operational.” That ordering keeps a stray or sarcastic tweet from triggering a false alert.
Step 9: Cache responses so you don’t get blocked
Polling every few seconds across three services is how you get your IP or token throttled. A simple file-based cache with a short TTL solves this without adding infrastructure.
# fn_verify/cache.py
import json
import time
from pathlib import Path
CACHE_DIR = Path.home() / ".cache" / "fn-verify"
CACHE_DIR.mkdir(parents=True, exist_ok=True)
def cached(key, ttl_seconds, fetch_fn):
path = CACHE_DIR / f"{key}.json"
if path.exists():
age = time.time() - path.stat().st_mtime
if age < ttl_seconds:
return json.loads(path.read_text())
data = fetch_fn()
path.write_text(json.dumps(data, default=str))
return data
A 60 to 120 second TTL on the Epic status feed is generous enough to stay well inside reasonable use, while still catching a status change within two minutes, fast enough for a personal alert bot.
Step 10: Wire up the command-line interface
# fn_verify/cli.py
import click
from fn_verify.sources import (
get_epic_summary,
get_fortnite_component,
get_epic_scheduled_maintenance,
get_epic_unresolved_incidents,
)
from fn_verify.reconcile import reconcile
from fn_verify.cache import cached
@click.command()
@click.option("--ttl", default=120, help="Cache TTL in seconds")
def status(ttl):
summary = cached("epic_summary", ttl, get_epic_summary)
component = get_fortnite_component(summary)
maintenance = cached("epic_maint", ttl, get_epic_scheduled_maintenance)
incidents = cached("epic_incidents", ttl, get_epic_unresolved_incidents)
verdict = reconcile(component, maintenance, incidents, [])
click.echo(f"Verdict: {verdict.value}")
click.echo(f"Fortnite component status: {component['status']}")
click.echo(f"Scheduled maintenance windows: {len(maintenance)}")
click.echo(f"Unresolved incidents: {len(incidents)}")
if __name__ == "__main__":
status()
Run it with python -m fn_verify.cli. On a quiet day you’ll see operational with zero maintenance windows and zero incidents, which is the baseline you want to confirm before relying on this for alerts.
Step 11: Schedule it and add alerts
A systemd timer is more reliable than cron for anything you want retried cleanly on failure, but either works. Here’s the systemd version.
# /etc/systemd/system/fn-verify.timer
[Unit]
Description=Run the Fortnite status verifier every 3 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=3min
[Install]
WantedBy=timers.target
Pair it with a matching fn-verify.service unit that runs python -m fn_verify.cli, then enable both with systemctl enable --now fn-verify.timer. For alerting, pipe the verdict into a webhook call only when it changes from the previous run, store the last verdict in the same cache directory, compare it on each run, and fire a POST request to your Discord or Slack webhook URL only on a transition. That single comparison is what keeps you from getting pinged every three minutes during a seven-hour maintenance window.
Respecting Rate Limits: How Often Is Too Often
There’s no published rate limit for the public Epic status endpoints the way there is for the X API, but “no published limit” isn’t the same as “no limit.” Treat it the way you’d treat any public API you don’t have a contract with: poll only as often as you genuinely need to, cache aggressively, and set a descriptive User-Agent header on your requests so that if something does go wrong on Epic’s side, whoever’s looking at server logs can see a sensible identifier rather than a generic Python requests string.
# fn_verify/sources.py (add near the top)
HEADERS = {"User-Agent": "fn-verify/1.0 (personal status checker; contact: [email protected])"}
# then pass headers=HEADERS to every requests.get() call
A three-minute polling interval, which is what the systemd timer in this tutorial uses, works out to 480 requests per day against the Epic status endpoint alone. That’s a trivial load for a public JSON API serving a game with hundreds of millions of accounts, but it would add up quickly if you ran dozens of instances of this tool from the same IP, say, across a fleet of community mod bots. If you’re building something that scales beyond personal or single-community use, centralize the polling in one process and fan the result out to multiple consumers internally, rather than having each consumer hit the upstream API independently. The X API is the one leg of this project where limits are explicit and tight on the free tier, which is the main reason the cache layer in Step 9 defaults to a TTL measured in minutes rather than seconds.
Testing the Reconciliation Logic Without Hitting Live Endpoints
The reconcile() function is the part of this project worth testing properly, since it’s pure logic with no network calls, exactly the kind of code that’s cheap to cover and expensive to get wrong in production. Writing fixtures that mimic Epic’s real JSON shape means you can verify the consensus rules without waiting for an actual outage to happen, which, given the biweekly patch cadence, could mean waiting days for a real test case.
# tests/test_reconcile.py
from fn_verify.reconcile import reconcile, Verdict
OPERATIONAL = {"name": "Fortnite", "status": "operational"}
MAJOR_OUTAGE = {"name": "Fortnite", "status": "major_outage"}
def test_quiet_day_is_operational():
verdict = reconcile(OPERATIONAL, [], [], [])
assert verdict == Verdict.OPERATIONAL
def test_scheduled_window_with_no_incident_yet():
verdict = reconcile(OPERATIONAL, [{"id": "abc123"}], [], [])
assert verdict == Verdict.SCHEDULED
def test_outage_corroborated_by_incident():
verdict = reconcile(MAJOR_OUTAGE, [], [{"id": "inc_1"}], [])
assert verdict == Verdict.IN_PROGRESS
def test_outage_without_corroboration_is_unconfirmed():
verdict = reconcile(MAJOR_OUTAGE, [], [], [])
assert verdict == Verdict.UNCONFIRMED
Run these with pytest tests/. That last case, a component status that flipped but with zero corroborating incidents or posts, is deliberately treated as UNCONFIRMED rather than a hard outage. Status pages occasionally show a brief, self-correcting blip that resolves in under a minute, and you don’t want a webhook firing for every one of those. Requiring at least one corroborating signal before declaring IN_PROGRESS is a cheap way to cut down on noise without hiding genuine incidents, since a real outage almost always produces an incident entry within a few minutes anyway.
Output Examples
A normal, quiet run looks like this:
$ python -m fn_verify.cli
Verdict: operational
Fortnite component status: operational
Scheduled maintenance windows: 0
Unresolved incidents: 0
During an active update window, the same command reflects the change immediately, since the cache TTL has expired and a fresh pull picks up the new component status:
$ python -m fn_verify.cli
Verdict: downtime_in_progress
Fortnite component status: major_outage
Scheduled maintenance windows: 0
Unresolved incidents: 1
If you prefer machine-readable output for piping into another tool, swap the click.echo calls for a single json.dumps call. That’s the more useful form if you’re feeding this into a webhook dispatcher, a status page of your own, or a simple cron job that appends each check to a log file for later analysis. A typical JSON payload would look like this:
{
"checked_at_utc": "2026-10-07T18:22:00Z",
"verdict": "operational",
"epic_component_status": "operational",
"scheduled_maintenance_count": 0,
"unresolved_incident_count": 0
}
Common Pitfalls
Most of the mistakes here aren’t exotic, they’re the same handful of assumptions that trip up anyone building their first status-checking tool.
- Treating Fortnite-API.com as an uptime source. It’s a game-data project, not Epic’s status system. Use it for season and build context only, never as evidence that servers are up or down.
- Hardcoding a UTC offset for Eastern Time. “ET is UTC-5” is only true for part of the year. Use
zoneinfowith theAmerica/New_Yorkkey so daylight saving transitions resolve automatically. - Confusing matchmaking shutdown with full downtime. Epic disables matchmaking roughly 30 minutes before the broader maintenance window starts. If your tool only watches for “fully down,” you’ll miss the early warning players actually want.
- Polling without caching. Hitting any of these three APIs every few seconds invites rate limiting, and the X API’s free search tier is the one most likely to cut you off first.
- Assuming an empty scheduled-maintenance array means nothing is planned. Epic typically announces a window only a few hours ahead. An empty list the night before an update is expected behavior, not a bug.
- Firing an alert on every poll instead of on state changes. Without a “has the verdict actually changed since last run” check, a three-minute polling interval during a seven-hour maintenance window produces well over a hundred identical pings, which trains everyone to mute the channel.
Troubleshooting
Here are the issues you’re most likely to hit, and what each one actually means. Most of these look like bugs in your code on first glance but are actually expected behavior from one of the three upstream services, which is worth knowing before you start adding defensive code that papers over something that was never actually broken.
| Symptom | Likely cause | Fix |
|---|---|---|
| HTTP 429 from X API | Search-tier rate limit hit | Increase cache TTL, reduce polling frequency, or batch fewer queries per window |
| HTTP 403 from X API | Invalid or expired bearer token | Regenerate the token in your developer app dashboard and re-export the env var |
| JSONDecodeError on status.epicgames.com | You hit an HTML error page instead of the JSON API, often a typo’d path | Double-check the /api/v2/ prefix and the exact endpoint name |
| zoneinfo raises ZoneInfoNotFoundError | Missing IANA tz database, common on bare Windows installs | Install the tzdata PyPI package |
| Stale verdict after a known status change | Cache TTL hasn’t expired yet | Lower the TTL during active update windows, or clear ~/.cache/fn-verify manually |
| SSL certificate verify failed | Corporate proxy intercepting TLS | Point requests at your organization’s CA bundle via the REQUESTS_CA_BUNDLE env var, don’t disable verification |
| Duplicate alerts for the same incident | Incident ID changes mid-event on the status page | Compare verdict state, not incident ID, before firing a webhook |
| Fortnite-API.com returns 503 | Third-party service has its own outage, unrelated to actual Fortnite status | Treat it as optional context, don’t let its failure block your core verdict |
One pattern worth calling out separately: if get_fortnite_news() fails, don’t let that exception bubble up and kill the whole run. Wrap optional sources in their own try/except blocks so a single third-party outage, which has nothing to do with whether Fortnite itself is actually reachable, never prevents the core verdict from being computed and posted.
Advanced Tips
Once the basic verifier is running reliably, a few additions make it genuinely useful rather than just a novelty script. First, wrap every network call in tenacity‘s retry decorator with exponential backoff, a single dropped connection shouldn’t produce a false “down” verdict. Second, log every verdict change with a timestamp to a local SQLite file, after a month you’ll have real historical data on how long updates actually take versus Epic’s initial estimate, which is more useful than any single data point. Third, if you’re running this for a community rather than yourself, separate the polling service from the alert dispatcher, that way you can silence alerts during a known maintenance window without stopping the underlying checks. Finally, if you outgrow the free X API search tier, consider dropping that source entirely rather than paying for higher access, the Epic status feed alone already catches the vast majority of real incidents, and the X source mainly adds color commentary.
Once you’ve logged a few weeks of verdict transitions, computing your own median downtime is a short query, and it’s often more accurate for your specific region and connection than any single news article’s estimate.
# rough median duration from logged transitions
import sqlite3
import statistics
conn = sqlite3.connect("verdict_log.db")
rows = conn.execute(
"""
SELECT started_at, ended_at FROM downtime_windows
WHERE ended_at IS NOT NULL
"""
).fetchall()
durations_minutes = [
(ended - started).total_seconds() / 60 for started, ended in rows
]
print(f"Median downtime: {statistics.median(durations_minutes):.0f} minutes")
print(f"Windows logged: {len(durations_minutes)}")
Containerizing the whole thing is optional but worthwhile if you’re deploying it alongside other small services. A minimal Dockerfile based on python:3.12-slim, with the cache directory mounted as a volume so state survives container restarts, is enough to run this on any machine with Docker installed, no systemd required. If you go that route, mount ~/.cache/fn-verify as a named volume rather than letting it live inside the container’s writable layer, otherwise every redeploy wipes your cache and historical log along with it.
The Complete Working Project
Put together, the project is six small files plus a tests folder. None of it needs a database, a web server, or a paid hosting plan, a single machine with a systemd timer or a cron job is enough to keep it running continuously.
fn-verify/
├── fn_verify/
│ ├── __init__.py
│ ├── sources.py # Epic status, Fortnite-API, X fetch functions
│ ├── normalize.py # timezone-safe ET/UTC conversion
│ ├── reconcile.py # three-source consensus logic
│ ├── cache.py # TTL-based file cache
│ └── cli.py # click entry point
├── tests/
│ └── test_reconcile.py
├── requirements.txt
└── fn-verify.timer # systemd schedule (copy to /etc/systemd/system/)
The entire thing fetches three public endpoints, applies one reconciliation function, and prints or posts a verdict, which is intentionally simple: the goal was never to out-engineer Epic’s own status page, just to stop treating any single source as gospel when three are available for free, and to finally give a reliable, automatable answer to fortnite update today time instead of a guess. From here, the most natural next steps are the ones covered in the advanced tips above, a SQLite log for historical medians, a webhook dispatcher that only fires on state transitions, and per-component alerts instead of one flat Fortnite-wide verdict.
Frequently Asked Questions
What time does Fortnite usually go down for updates?
Recent Chapter 7 Season 4 maintenance windows, including v42.20 and v42.30, have started at 4 AM Eastern Time (8 AM UTC), with matchmaking disabled roughly 30 minutes earlier. That’s a pattern, not a guarantee, Epic can and does shift the start time for specific releases.
How long does Fortnite update today time downtime typically last?
Routine mid-season patches in recent months have run about 2.5 to 3 hours. Larger releases can run much longer, the July 2026 v41.30 maintenance stretched to roughly seven hours. There’s no fixed duration, which is the core reason a static countdown isn’t enough on its own.
Is Fortnite-API.com an official Epic Games source?
No. It’s a third-party project that surfaces game data like news, cosmetics, and shop rotation. It’s genuinely useful for build context, but it isn’t built or operated by Epic Games and shouldn’t be treated as an uptime authority.
Why does Epic’s status page sometimes show “operational” during an update?
Status pages reflect individual components, and not every component goes down at once. Matchmaking can be disabled while login and the website remain operational, which is why this tutorial checks the specific Fortnite component rather than relying on one page-wide status.
Do I need a paid X API plan to monitor @FortniteStatus programmatically?
A free-tier developer account with search access is enough for the recent-search endpoint used in this tutorial, though X’s free tiers come with tight request caps. If those caps become a problem, the Epic status feed alone still covers the core use case.
How do I convert Epic’s “4 AM ET” into my local time safely?
Use Python’s zoneinfo module with the America/New_York key, then convert to your own local zone with astimezone(). Avoid fixed offsets like UTC-5, they break every year when clocks change for daylight saving.
Can I run this without a server?
Yes. A systemd timer or cron job on a personal machine, a Raspberry Pi, or a low-cost VPS is enough. The project makes three lightweight HTTP calls per run and stores small JSON files locally, there’s no database or persistent process required.
What’s the difference between matchmaking shutdown and full downtime?
Epic typically disables matchmaking about 30 minutes before broader services go into maintenance, so players already in a match can often finish it while new matches stop forming. Full downtime, when login and core services also go offline, follows shortly after.
Does this approach work for games other than Fortnite?
The pattern generalizes well. Many large publishers run their status pages on the same Statuspage-style backend, so the sources.py functions in this tutorial work for other titles with only the base URL changed. The reconciliation logic and timezone handling need no changes at all, since none of that is Fortnite-specific.



