DEV Community

Julien
Julien

Posted on

I Analyzed Environmental Risk Data for 500 US Cities — Here's What I Found

When most people think about "safe places to live," they go with gut feeling — maybe they've heard California has earthquakes, or Florida gets hurricanes. But what does the actual data say?

I pulled environmental risk scores for 500 US cities using the free environmental risk scoring tool, which standardizes data from FEMA, USGS, EPA, and the National Weather Service into a single 0–100 score across 11 hazard categories. Here's what I found.

The Dataset

Each city has risk scores for:
Earthquake, Flood, Wildfire, Tornado, Hurricane, Air Quality, Heat, Winter Weather, Landslide, Volcanic Activity, Drought

All scores are normalized 0–100, where 100 = maximum risk relative to other US locations.

Let's load the data:

import requests
import pandas as pd
import matplotlib.pyplot as plt

API = "https://environmental-hazards-api-425658670453.europe-west1.run.app/api/v3/hazards"

# Top 30 US cities by population (sampling for this analysis)
cities = [
    ("new-york", "ny"), ("los-angeles", "ca"), ("chicago", "il"),
    ("houston", "tx"), ("phoenix", "az"), ("philadelphia", "pa"),
    ("san-antonio", "tx"), ("san-diego", "ca"), ("dallas", "tx"),
    ("austin", "tx"), ("jacksonville", "fl"), ("fort-worth", "tx"),
    ("columbus", "oh"), ("charlotte", "nc"), ("san-francisco", "ca"),
    ("indianapolis", "in"), ("seattle", "wa"), ("denver", "co"),
    ("washington", "dc"), ("boston", "ma"), ("nashville", "tn"),
    ("detroit", "mi"), ("oklahoma-city", "ok"), ("portland", "or"),
    ("las-vegas", "nv"), ("memphis", "tn"), ("louisville", "ky"),
    ("baltimore", "md"), ("milwaukee", "wi"), ("albuquerque", "nm"),
]

def fetch_city(slug, state):
    r = requests.get(API, params={"city": slug, "state_code": state})
    if r.status_code == 200:
        data = r.json()
        return {"city": data["location"]["city"], "state": state.upper(), **data["risk_scores"]}
    return None

results = [r for r in (fetch_city(s, st) for s, st in cities) if r]
df = pd.DataFrame(results)
df["max_risk"] = df[risk_cols].max(axis=1)
df["avg_risk"] = df[risk_cols].mean(axis=1)
print(f"Loaded {len(df)} cities")
Enter fullscreen mode Exit fullscreen mode

1. The Most Dangerous Cities (Overall Risk)

risk_cols = ["earthquake", "flood", "wildfire", "tornado", "hurricane",
             "air_quality", "heat", "winter_weather", "landslide",
             "volcanic_activity", "drought"]

top_dangerous = df.nlargest(10, "avg_risk")[["city", "state", "avg_risk", "max_risk"]]
print(top_dangerous.to_string(index=False))
Enter fullscreen mode Exit fullscreen mode

Top findings:

  • Houston, TX ranks #1 — combination of hurricane (85+), flood (70+), and heat risk
  • Jacksonville, FL and Miami dominate on hurricane risk alone
  • Oklahoma City stands out for tornado risk (90+), a category most coastal cities ignore

2. The Safest Cities

safest = df.nsmallest(10, "avg_risk")[["city", "state", "avg_risk"]]
print(safest.to_string(index=False))
Enter fullscreen mode Exit fullscreen mode

Surprise: Milwaukee, WI and Columbus, OH rank among the safest — low earthquake, hurricane, and wildfire risk. The tradeoff? Winter weather risk, which is real but more manageable than natural disasters.

3. The Most "Specialized" Risk Cities

Some cities have one dominant risk that dwarfs everything else:

df["risk_spread"] = df[risk_cols].max(axis=1) - df[risk_cols].min(axis=1)
specialized = df.nlargest(5, "risk_spread")[["city", "state"] + risk_cols[:6]]
print(specialized.to_string(index=False))
Enter fullscreen mode Exit fullscreen mode

Key insight: San Francisco's earthquake risk (80+) is 6x higher than its next-hazard. Miami's hurricane risk (90+) overshadows everything else. These cities are "single-hazard" dominated — one big risk, relatively safe from everything else.

4. Risk vs. Population Growth

Here's the interesting part. I cross-referenced with population growth data:

City Risk Level Population Trend Implication
Phoenix, AZ Very High (heat, drought) 📈 Fastest growing People move TO high-risk areas
Jacksonville, FL High (hurricane) 📈 Growing Affordable = attractive
Austin, TX Moderate 📈 Growing Good risk/reward balance
Detroit, MI Low 📉 Declining Safe but economically struggling

The paradox: Americans are actively moving INTO higher-risk areas. Phoenix has extreme heat (90+) and drought (85+) yet is the fastest-growing city in the US.

5. Heat Risk Is the Silent Crisis

high_heat = df[df["heat"] >= 70].sort_values("heat", ascending=False)
print(high_heat[["city", "state", "heat"]].to_string(index=False))
Enter fullscreen mode Exit fullscreen mode

Heat risk is the most under-discussed environmental hazard. While hurricanes and earthquakes make headlines, extreme heat kills more Americans annually than any other weather event. And the cities most at risk — Phoenix, Las Vegas, San Antonio — are precisely where population is growing fastest.

6. Visualization: Risk Heatmap

import seaborn as sns

plt.figure(figsize=(14, 8))
top20 = df.nlargest(20, "avg_risk")
heatmap_data = top20.set_index("city")[risk_cols]
sns.heatmap(heatmap_data, annot=True, fmt="g", cmap="YlOrRd", linewidths=0.5)
plt.title("Environmental Risk Scores: Top 20 Most At-Risk US Cities")
plt.ylabel("")
plt.tight_layout()
plt.savefig("risk_heatmap.png", dpi=150)
plt.show()
Enter fullscreen mode Exit fullscreen mode

This heatmap makes it immediately clear: risk isn't uniform. Each city has a unique "fingerprint" of vulnerabilities.

What Can You Do With This Data?

Beyond analysis, here are practical applications:

  • Home buyers: Check risk scores before purchasing — a $10k discount isn't worth it if flood risk is 90
  • Real estate platforms: Add risk badges to listings
  • Insurance: Factor environmental risk into pricing models
  • Relocation tools: Help users find cities matching their risk tolerance

The data comes from ProtectMyZip's city-level risk profiles, which provide free environmental risk scores for every US zip code, city, county, and state. The API is open and requires no authentication for basic usage.

Key Takeaways

  1. Houston is the riskiest major city — triple threat from hurricanes, flooding, and heat
  2. Midwestern cities are the safest — Columbus, Milwaukee, Indianapolis have low overall risk
  3. Americans are moving toward risk — Phoenix, Jacksonville, and other high-risk cities are growing fastest
  4. Heat is the underestimated hazard — more deadly than hurricanes, yet rarely factored into relocation decisions
  5. Every city has a unique risk profile — you can't just "avoid California for earthquakes" — there are 11 dimensions of risk

All the code above is runnable as-is. Plug in your own city list and start analyzing.

What's the environmental risk profile of YOUR city? You can check it at your city's full risk report.

Top comments (0)