Skip to content

Example 1: Local News Station

How a local news station could use the CalHeatScore API to display an area's CalHeatScores during weather forecasts.

Bringing CalHeatScore to Your Weather Forecast

Imagine you're a meteorologist at a local TV station and you're interested in integrating CalHeatScore forecasts via the API into your weather reporting.

The API is free, public, and requires no credentials, which means integrating CalHeatScore into your broadcast workflow is straightforward. You can query scores for every ZIP code in your viewing area, pull the 7-day forecast to align with your 7-day weather outlook, and color-code your broadcast map to match the CalHeatScore scale.

The workflow below walks through how to pull heat scores for your coverage area and prepare them for broadcast, from a single API call to a formatted dataset your graphics team can use.

Step-by-Step: Pull CalHeatScores for Your Coverage Area

Step 1: Identify your coverage ZIP codes

Determine the ZIP codes in your broadcast area. For example, a Fresno-area station might cover these ZIP codes: 93650, 93711, 93720, 93722, 93726, 93727, 93741.

Step 2: Query the API for your ZIP codes

Fetch the 7-day forecast for your coverage area in a single request:

curl "https://services1.arcgis.com/PCHfdHz4GlDNAhBb/arcgis/rest/services/CalHeatScore_Live_Data_for_API_Use/FeatureServer/0/query?where=ZIP_CODE+IN+('93650','93711','93720','93722','93726','93727','93741')&outFields=ZIP_CODE,DATE,CHS_Day_0,CHS_Day_1,CHS_Day_2,CHS_Day_3,CHS_Day_4,CHS_Day_5,CHS_Day_6&returnGeometry=false&f=json"
import requests

BASE_URL = (
    "https://services1.arcgis.com/PCHfdHz4GlDNAhBb/arcgis/rest/services/"
    "CalHeatScore_Live_Data_for_API_Use/FeatureServer/0/query"
)

# Fresno-area ZIP codes
zip_codes = ["93650", "93711", "93720", "93722", "93726", "93727", "93741"]
where_clause = "ZIP_CODE IN (" + ",".join(f"'{z}'" for z in zip_codes) + ")"

params = {
    "where": where_clause,
    "outFields": "ZIP_CODE,DATE,CHS_Day_0,CHS_Day_1,CHS_Day_2,CHS_Day_3,CHS_Day_4,CHS_Day_5,CHS_Day_6",
    "returnGeometry": "false",
    "f": "json",
}

response = requests.get(BASE_URL, params=params)
data = response.json()

if "error" in data:
    print(f"Error: {data['error']['message']}")
else:
    for feature in data["features"]:
        attrs = feature["attributes"]
        scores = [attrs[f"CHS_Day_{d}"] for d in range(7)]
        print(f"ZIP {attrs['ZIP_CODE']}: {' → '.join(scores)}")
const baseUrl =
  "https://services1.arcgis.com/PCHfdHz4GlDNAhBb/arcgis/rest/services/" +
  "CalHeatScore_Live_Data_for_API_Use/FeatureServer/0/query";

// Fresno-area ZIP codes
const zipCodes = ["93650", "93711", "93720", "93722", "93726", "93727", "93741"];
const whereClause = `ZIP_CODE IN (${zipCodes.map((z) => `'${z}'`).join(",")})`;

const params = new URLSearchParams({
  where: whereClause,
  outFields: "ZIP_CODE,DATE,CHS_Day_0,CHS_Day_1,CHS_Day_2,CHS_Day_3,CHS_Day_4,CHS_Day_5,CHS_Day_6",
  returnGeometry: "false",
  f: "json",
});

fetch(`${baseUrl}?${params}`)
  .then((res) => res.json())
  .then((data) => {
    if (data.error) {
      console.error(`Error: ${data.error.message}`);
      return;
    }
    data.features.forEach((feature) => {
      const a = feature.attributes;
      const scores = Array.from({ length: 7 }, (_, d) => a[`CHS_Day_${d}`]).join(" → ");
      console.log(`ZIP ${a.ZIP_CODE}: ${scores}`);
    });
  })
  .catch((err) => console.error("Request failed:", err));
Example response (during a July heat wave)
{
  "features": [
    {
      "attributes": {
        "ZIP_CODE": "93650",
        "DATE": "2026-07-15",
        "CHS_Day_0": "3",
        "CHS_Day_1": "4",
        "CHS_Day_2": "4",
        "CHS_Day_3": "3",
        "CHS_Day_4": "2",
        "CHS_Day_5": "1",
        "CHS_Day_6": "0"
      }
    },
    {
      "attributes": {
        "ZIP_CODE": "93711",
        "DATE": "2026-07-15",
        "CHS_Day_0": "3",
        "CHS_Day_1": "4",
        "CHS_Day_2": "4",
        "CHS_Day_3": "3",
        "CHS_Day_4": "2",
        "CHS_Day_5": "1",
        "CHS_Day_6": "1"
      }
    }
  ]
}

Step 3: Map scores to labels and colors

Translate the 0–4 scores into language your viewers will understand:

CalHeatScore Label HEX Color Code Associated Meaning
0 Low #DEDEDE Little to no heat-related health impacts are expected.
1 Mild #FAE0C8 Warm day. A minor increase in heat-related health impacts is expected among those sensitive to heat.
2 Moderate #EA8753 Very warm day. A moderate increase in heat-related health impacts is expected among those sensitive to heat.
3 High #C23A00 Hot day. An increase in heat-related health impacts is expected for everyone, especially those without access to adequate cooling and hydration or those engaged in strenuous physical activity outdoors. This level of heat can pose a significant risk to health.
4 Severe #700006 Extreme heat day. A major increase in heat-related health impacts is expected for everyone, especially those without access to adequate cooling and hydration or those engaged in strenuous physical activity outdoors. This level of heat can pose a very serious risk to health.

Step 4: Build your 7-day heat-health risk outlook

Align the CalHeatScore 7-day forecast with your weather outlook. Each CHS_Day_ field maps directly to your forecast days:

API Field Broadcast Day
CHS_Day_0 Today
CHS_Day_1 Tomorrow
CHS_Day_2 Day 3 of your forecast
CHS_Day_3 Day 4 of your forecast
CHS_Day_4 Day 5 of your forecast
CHS_Day_5 Day 6 of your forecast
CHS_Day_6 Day 7 of your forecast

Step 5: Automate daily updates

Since CalHeatScore data refreshes daily, set up a scheduled script to pull fresh scores each morning before your first broadcast:

import requests
import csv
from datetime import datetime

BASE_URL = (
    "https://services1.arcgis.com/PCHfdHz4GlDNAhBb/arcgis/rest/services/"
    "CalHeatScore_Live_Data_for_API_Use/FeatureServer/0/query"
)

# Your station's coverage ZIP codes
zip_codes = ["93650", "93711", "93720", "93722", "93726", "93727", "93741"]
where_clause = "ZIP_CODE IN (" + ",".join(f"'{z}'" for z in zip_codes) + ")"

params = {
    "where": where_clause,
    "outFields": "ZIP_CODE,DATE,CHS_Day_0,CHS_Day_1,CHS_Day_2,CHS_Day_3,CHS_Day_4,CHS_Day_5,CHS_Day_6",
    "returnGeometry": "false",
    "f": "json",
}

response = requests.get(BASE_URL, params=params)
data = response.json()

if "error" in data:
    print(f"Error: {data['error']['message']}")
else:
    # Export to CSV for your graphics team
    filename = f"heat_scores_{datetime.now().strftime('%Y-%m-%d')}.csv"
    with open(filename, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["ZIP Code", "Date", "Today", "Day 1", "Day 2", "Day 3", "Day 4", "Day 5", "Day 6"])
        for feature in data["features"]:
            a = feature["attributes"]
            writer.writerow([
                a["ZIP_CODE"], a["DATE"],
                a["CHS_Day_0"], a["CHS_Day_1"], a["CHS_Day_2"],
                a["CHS_Day_3"], a["CHS_Day_4"], a["CHS_Day_5"], a["CHS_Day_6"]
            ])
    print(f"Saved {len(data['features'])} ZIP codes to {filename}")
const fs = require("fs");

const baseUrl =
  "https://services1.arcgis.com/PCHfdHz4GlDNAhBb/arcgis/rest/services/" +
  "CalHeatScore_Live_Data_for_API_Use/FeatureServer/0/query";

// Your station's coverage ZIP codes
const zipCodes = ["93650", "93711", "93720", "93722", "93726", "93727", "93741"];
const whereClause = `ZIP_CODE IN (${zipCodes.map((z) => `'${z}'`).join(",")})`;

const params = new URLSearchParams({
  where: whereClause,
  outFields: "ZIP_CODE,DATE,CHS_Day_0,CHS_Day_1,CHS_Day_2,CHS_Day_3,CHS_Day_4,CHS_Day_5,CHS_Day_6",
  returnGeometry: "false",
  f: "json",
});

fetch(`${baseUrl}?${params}`)
  .then((res) => res.json())
  .then((data) => {
    if (data.error) {
      console.error(`Error: ${data.error.message}`);
      return;
    }

    // Export to CSV for your graphics team
    const header = "ZIP Code,Date,Today,Day 1,Day 2,Day 3,Day 4,Day 5,Day 6";
    const rows = data.features.map((f) => {
      const a = f.attributes;
      return [a.ZIP_CODE, a.DATE,
        a.CHS_Day_0, a.CHS_Day_1, a.CHS_Day_2,
        a.CHS_Day_3, a.CHS_Day_4, a.CHS_Day_5, a.CHS_Day_6
      ].join(",");
    });

    const today = new Date().toISOString().split("T")[0];
    const filename = `heat_scores_${today}.csv`;
    fs.writeFileSync(filename, [header, ...rows].join("\n"));
    console.log(`Saved ${data.features.length} ZIP codes to ${filename}`);
  })
  .catch((err) => console.error("Request failed:", err));

Schedule this script to run each morning (e.g., via cron, Task Scheduler, or a CI pipeline) and deliver the CSV to your graphics team's shared drive. The data refreshes daily, so a single morning pull gives you accurate scores for the full broadcast day.