When a slice of your users suddenly disappears, the first triage question is whether the problem is yours or the Internet's. A broken deploy, a failing load balancer and a national connectivity shutdown can look identical in your dashboards: requests from one country or one mobile carrier drop by half. Answering that question in minutes instead of hours needs an outside view of the network, and Cloudflare Radar is one of the few freely available sources of one, built from traffic that crosses Cloudflare's global network and its public DNS resolver.
This article treats Radar as an engineering data source rather than a news site. It explains where Radar's data comes from and what that implies about bias, which datasets matter for operations, how the Radar API is called and paginated, how to build a small service that correlates Radar signals with your own telemetry, a worked triage example, and the misreadings and licensing limits that catch teams out. Endpoint paths and parameters below were checked against Cloudflare's API reference on 2026-10-04; check the reference again before you build, because the API keeps growing.
What Radar measures and what it cannot see
Radar publishes aggregated views of what Cloudflare observes: HTTP request trends, network-layer traffic, DNS queries to the 1.1.1.1 resolver, attack activity, BGP routing events and a URL scanner. Two features matter most for operations. The traffic anomaly feed flags sudden deviations from a location's or network's normal traffic, and the outage center records Internet disruptions with a cause, a scope and a time range. Cloudflare's documentation describes a manual review step: an anomaly found in several Cloudflare datasets or in third-party data, such as Georgia Tech's IODA platform, is marked verified.
Everything Radar shows is a sample of traffic that touches Cloudflare. That is a large share of the web, but it is not the Internet. A network whose users mostly reach services Cloudflare does not front will be under-represented, and a change in Cloudflare's own customer mix can shift a series. Use Radar as strong evidence, not ground truth, and prefer it for relative change over time rather than for comparing absolute sizes.
Architecture: Radar as an alert enrichment source
The design principle is that your own measurements decide whether something is wrong, and Radar helps decide why. A poller fetches Radar data on a schedule and caches the raw responses with the exact request parameters, so you can later show what was known at the time. When your monitoring detects a drop for a country or autonomous system number (ASN), the correlator looks for Radar events on the same entity in an overlapping window and labels the alert. The label shapes the response: an external outage calls for a status-page update and patience, while a drop visible only in your data calls for a rollback.
The API essentials
The Radar API lives under https://api.cloudflare.com/client/v4/radar/. Requests authenticate with a bearer token created in the Cloudflare dashboard with the Account, Radar, Read permission. Most endpoints accept a relative dateRange such as 7d or explicit dateStart and dateEnd values, plus format=json or format=csv. Responses wrap data in a result object, and time-series responses include result.meta describing the date range, aggregation interval and normalization.
| Endpoint | What it returns | Useful filters | Pagination |
|---|---|---|---|
/radar/traffic_anomalies | Automatically detected traffic drops, in result.trafficAnomalies | location, asn, status (VERIFIED or UNVERIFIED), type | limit, offset |
/radar/annotations/outages | Outage records with cause, type, scope and dates, in result.annotations | location, asn, outageCause, outageType | limit, offset |
/radar/bgp/hijacks/events | Suspected BGP hijacks with hijacker and victim ASNs, prefixes and a confidence score | involvedAsn, victimAsn, prefix, minConfidence | page, per_page |
/radar/netflows/timeseries | Normalized traffic time series, in result.serie_0 | location, asn, aggInterval, normalization | none |
Note the two pagination styles, and note that no rate limit was documented on the pages checked for this article. Poll gently, cache aggressively and back off on HTTP 429 responses rather than assuming a quota.
A minimal poller
A minimal poller in Python covers the four feeds. It keeps the token out of logs, handles both pagination styles and casts time-series values, which the API returns as strings:
import os, time, requests
BASE = "https://api.cloudflare.com/client/v4/radar"
S = requests.Session()
S.headers["Authorization"] = "Bearer " + os.environ["CF_RADAR_TOKEN"]
def get(path, **params):
for attempt in range(5):
r = S.get(f"{BASE}{path}", params={"format": "json", **params}, timeout=20)
if r.status_code == 429:
time.sleep(2 ** attempt)
continue
r.raise_for_status()
return r.json()["result"]
raise RuntimeError(f"Radar throttled on {path}")
def anomalies(location=None, asn=None, date_range="2d"):
out, offset = [], 0
while True:
res = get("/traffic_anomalies", location=location, asn=asn,
dateRange=date_range, limit=100, offset=offset)
batch = res.get("trafficAnomalies", [])
out += batch
if len(batch) < 100:
return out
offset += 100
def hijacks_involving(my_asn, min_conf=5, date_range="7d"):
out, page = [], 1
while True:
res = get("/bgp/hijacks/events", involvedAsn=my_asn, minConfidence=min_conf,
dateRange=date_range, page=page, per_page=100)
events = res.get("events", [])
out += events
if len(events) < 100:
return out
page += 1
def netflows(location, date_range="2d"):
res = get("/netflows/timeseries", location=location, dateRange=date_range,
aggInterval="15m", normalization="MIN0_MAX")
serie = res["serie_0"]
return list(zip(serie["timestamps"], map(float, serie["values"])))Two details deserve care. Hijack events arrive in result.events, and the response also carries a result_info block with page, per_page and total_count, which a stricter client can use instead of the short-page test. And requests drops parameters whose value is None, which is what makes the optional filters work; a hand-rolled URL builder that sends asn=None will get an error or, worse, an empty result that looks like good news.
Correlating Radar with your telemetry
The correlator is a join on entity and time. An anomaly or outage matches an alert when it names the same country or ASN and its time range overlaps the alert window, widened by a tolerance because detection on both sides lags the real event:
from datetime import timedelta
from dateutil.parser import isoparse
TOL = timedelta(minutes=30)
def overlaps(a_start, a_end, b_start, b_end):
still_open = b_end is None
return (still_open or a_start - TOL <= b_end) and b_start <= a_end + TOL
def label(alert): # alert: entity_type, entity, start, end
for an in anomalies(**{alert.entity_type: alert.entity}):
s, e = isoparse(an["startDate"]), an.get("endDate")
if overlaps(alert.start, alert.end, s, isoparse(e) if e else None):
return "external", an["status"], an["uuid"]
for o in get("/annotations/outages", **{alert.entity_type: alert.entity},
dateRange="7d", limit=100).get("annotations", []):
s, e = isoparse(o["startDate"]), o.get("endDate")
if overlaps(alert.start, alert.end, s, isoparse(e) if e else None):
return "external", "outage", o["id"]
return "likely ours", None, NoneOngoing events may have no end date, so the overlap test treats a missing end as still open from its start. Carry the anomaly's verification status into the label: an unverified anomaly is a hint, a verified one or an outage record is much stronger evidence.
A quick baseline from netflows
Anomaly and outage records are curated and can lag. The netflows series lets you make your own quick judgement while you wait. The poller requests MIN0_MAX normalization, which Cloudflare defines as each value divided by the maximum in the response, so zero stays zero and ratios between points are meaningful. Compare the series with itself: take the latest 15-minute value and compare it with the values at the same time of day on previous days, which removes the daily cycle that dominates Internet traffic.
from datetime import timedelta
from dateutil.parser import isoparse
def same_time_ratio(points, days=7):
"""Latest value divided by the median of the same slot on earlier days."""
by_ts = {isoparse(t): v for t, v in points}
latest = max(by_ts)
history = [by_ts[latest - timedelta(days=d)] for d in range(1, days + 1)
if latest - timedelta(days=d) in by_ts]
if len(history) < 3:
return None # not enough history to judge
history.sort()
median = history[len(history) // 2]
return by_ts[latest] / median if median > 0 else None
ratio = same_time_ratio(netflows("XX", date_range="8d"))A ratio near 1 says the country's traffic, as Cloudflare sees it, looks like a normal day; a ratio of 0.5 says half of it is missing. Treat the result as a hint with the same caveats as everything else from Radar: holidays, events and changes in Cloudflare's own traffic mix move the series too, so the ratio should support a label, never trigger a page by itself.
Worked example: a sudden drop in one country
At 14:05 UTC your real-user monitoring shows sessions from one country falling 45 percent below the same hour last week, concentrated in two mobile ASNs. Error rates from other countries are flat and there was no deploy in the last hour. The correlator queries Radar for the country and both ASNs.
Case one: Radar lists an unverified anomaly for one of the ASNs starting at 13:58, and the netflows series for the country shows a matching dip relative to its previous days. The alert is labelled external with medium confidence. The on-call engineer posts a status note, keeps watching their own error rate for divergence, and does not roll back. An outage record appears later naming a cause; it is attached to the incident for the review.
Case two: Radar shows nothing for the country or the ASNs, and the netflows series is flat. That is strong evidence that the Internet path is healthy and the problem is local to your service, for example a CDN configuration or certificate change that affects only some clients. The label switches to likely ours and the incident escalates. The absence of a Radar event is evidence too, provided you remember the coverage caveat.
Watching your own routes
Routing events can make your own prefixes unreachable or send traffic somewhere it should not go. Query hijack events filtered by your ASN as involvedAsn or victimAsn, or by your prefixes. Cloudflare's reference describes confidence scores of 1 to 4 as low, 5 to 7 as medium and 8 and above as high; start alerting at high and review medium events in business hours. Each event carries the suspected hijacker and victim ASNs, the prefixes, the number of peers that saw it and timestamps, which is enough to open a ticket with your upstream provider.
For people rather than services, the Radar dashboard also offers notifications: from the traffic, routing or outage center views you can subscribe to a country or ASN and receive email or webhook alerts. That is the quickest way to start, and the API-based poller is the way to integrate the same signals into your own tooling.
Failure modes and misreadings
- Reading normalized values as counts. Time-series values are normalized (for example as percentage change or scaled to a range), and the normalization is stated in result.meta. A value of 0.6 is not a request count. Compare shapes over time, not magnitudes between series.
- Treating unverified anomalies as fact. Automatic detection produces false positives. Weight verified anomalies and outage records above unverified ones.
- Over-reading silence. Small networks and places where Cloudflare sees little traffic may show nothing even during an outage.
- Detection lag. Anomalies appear after the event starts and outage records can take longer; re-run the correlation as the incident develops instead of labelling once.
- Paging on Radar alone. An Internet event that does not affect your users is not your incident.
- Licence violations. Cloudflare's documentation states that data from Radar API endpoints is made available under CC BY-NC 4.0, which requires attribution and does not allow commercial use. Internal triage is very different from republishing Radar data inside a paid product; get legal advice before the latter.
- Token leakage. Keep the token in a secret store and never log request headers.
Trade-offs and related reading
Radar is broad, free to query and quick to integrate, but its view is Cloudflare's view. Complement it with other public sources such as IODA, with active measurements from your own synthetic probes in the regions you serve, and with your providers' status feeds. Synthetic probes tell you whether your service is reachable from a place; Radar tells you whether that place can reach much of anything. Together they separate the two halves of the triage question better than either alone.
Related pages: DNS failover and traffic steering for what to do once an outage is confirmed, multi-region architecture for designing around regional failures, DDoS protection for attack-driven traffic shifts, and cloud networking for the routing basics behind BGP events.
What to do next
- Create a read-only Radar API token and store it in your secret manager.
- Break your own session and error metrics down by country and ASN, since that is how Radar indexes events.
- Deploy a poller for traffic anomalies, outages and netflows for the countries and networks that matter to you, caching raw responses.
- Add the correlator to your alert pipeline as an enrichment step, with verification status in the label.
- Subscribe to hijack events for your own ASN and prefixes, alerting on high confidence.
- Review the CC BY-NC 4.0 terms before showing Radar data to customers.