massoutage

MassOutage API

A free, open-CORS JSON API for US power outages: live county status, municipalities, incidents and threshold alerts, plus a decade of history.

Generated from the OpenAPI 3.1.0 document (version 1.x) served at /api/v1/openapi.json. Import it into Postman, Insomnia or a code generator. All responses include X-Request-Id, X-API-Version and X-Data-Notice headers.

Counties

County profile, history and causes.

GET/api/v1/counties/{fips}

County profile: live snapshot + yearly history

County metadata, a live customers-out snapshot, yearly reliability history and serving utilities. `?format=csv` downloads the yearly series.

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
formatqueryjson | csvResponse format; csv returns the yearly series.
200: application/json, text/csv404: Unknown county.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (33)
FieldTypeDescription
dataobject
data.fipsstring5-digit county FIPS.
data.namestringCounty name.
data.statestringState name.
data.state_abbrstringUSPS state abbreviation.
data.urlstringCanonical MassOutage county page.
data.liveobject
data.live.customers_outintegerCustomers out right now.
data.live.incident_countintegerActive incidents.
data.latest_yearobject | nullMost recent EAGLE-I year (latest year of any source when none); null without history.
data.latest_year.yearintegerCalendar year.
data.latest_year.sourcestringDataset slug (eaglei = DOE/ORNL EAGLE-I, odin = live).
data.latest_year.customer_minutesnumberTotal customer-minutes of outage.
data.latest_year.peak_customers_outintegerPeak simultaneous customers out.
data.latest_year.event_countintegerOutage episodes.
data.latest_year.customers_trackedinteger | nullCustomers in the county (denominator).
data.latest_year.avg_minutes_per_customernumber | nullSAIDI-like average minutes without power per customer.
data.yearlyarrayYearly reliability history.
data.yearly[].yearintegerCalendar year.
data.yearly[].sourcestringDataset slug (eaglei = DOE/ORNL EAGLE-I, odin = live).
data.yearly[].customer_minutesnumberTotal customer-minutes of outage.
data.yearly[].peak_customers_outintegerPeak simultaneous customers out.
data.yearly[].event_countintegerOutage episodes.
data.yearly[].customers_trackedinteger | nullCustomers in the county (denominator).
data.yearly[].avg_minutes_per_customernumber | nullSAIDI-like average minutes without power per customer.
data.utilitiesarrayUtilities serving the county.
data.utilities[].idstringUtility key.
data.utilities[].namestringUtility name.
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
Example response (illustrative values)
{
  "data": {
    "fips": "34025",
    "name": "Monmouth County",
    "state": "New Jersey",
    "state_abbr": "NJ",
    "url": "https://massoutage.com/new-jersey/monmouth-county",
    "live": {
      "customers_out": 1840,
      "incident_count": 27
    },
    "latest_year": {
      "year": 2025,
      "source": "eaglei",
      "customer_minutes": 10000000,
      "peak_customers_out": 40000,
      "event_count": 30,
      "customers_tracked": 350000,
      "avg_minutes_per_customer": 28.5
    },
    "yearly": [
      {
        "year": 2025,
        "source": "eaglei",
        "customer_minutes": 10000000,
        "peak_customers_out": 40000,
        "event_count": 30,
        "customers_tracked": 350000,
        "avg_minutes_per_customer": 28.5
      }
    ],
    "utilities": [
      {
        "id": "9726",
        "name": "Jersey Central Power & Lt Co"
      }
    ]
  },
  "meta": {
    "sources": [
      {
        "name": "ODIN (Outage Data Initiative Nationwide)",
        "url": "https://odin.ornl.gov/"
      }
    ],
    "last_modified": "2026-09-14T13:50:00.000Z"
  }
}
GET/api/v1/counties/{fips}/history

County monthly history and worst events

Monthly outage series, worst recorded episodes and yearly history. `?format=csv` downloads the monthly series.

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
formatqueryjson | csvResponse format; csv returns the monthly series.
200: application/json, text/csv404: Unknown county.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (32)
FieldTypeDescription
dataobject
data.fipsstringCounty FIPS.
data.namestringCounty name.
data.statestringState name.
data.monthlyarray
data.monthly[].yearintegerYear.
data.monthly[].monthintegerMonth 1-12.
data.monthly[].sourcestringDataset slug.
data.monthly[].customer_minutesnumberCustomer-minutes of outage.
data.monthly[].peak_customers_outintegerPeak customers out.
data.monthly[].event_countintegerOutage episodes.
data.monthly[].minutes_any_outagenumberMinutes with any customer out.
data.worst_eventsarray
data.worst_events[].started_atstringEpisode start.
data.worst_events[].ended_atstringEpisode end.
data.worst_events[].duration_minnumberDuration in minutes.
data.worst_events[].peak_customers_outintegerPeak customers out.
data.worst_events[].customer_minutesnumberCustomer-minutes.
data.worst_events[].sourcestringDataset slug.
data.yearlyarray
data.yearly[].yearintegerCalendar year.
data.yearly[].sourcestringDataset slug (eaglei = DOE/ORNL EAGLE-I, odin = live).
data.yearly[].customer_minutesnumberTotal customer-minutes of outage.
data.yearly[].peak_customers_outintegerPeak simultaneous customers out.
data.yearly[].event_countintegerOutage episodes.
data.yearly[].customers_trackedinteger | nullCustomers in the county (denominator).
data.yearly[].avg_minutes_per_customernumber | nullSAIDI-like average minutes without power per customer.
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
GET/api/v1/counties/{fips}/causes

County outage-cause mix

Accumulated historical cause mix and the live cause mix. `?format=csv` downloads the historical mix (live mix when there is no history).

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
formatqueryjson | csvResponse format.
200: application/json, text/csv404: Unknown county.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (29)
FieldTypeDescription
dataobject
data.countyobject
data.county.fipsstringCounty FIPS.
data.county.namestringCounty name.
data.county.statestring | nullState abbreviation.
data.historicalobject
data.historical.rankedarrayClassified categories, largest first.
data.historical.ranked[].categorystring enumCause category. (weather, equipment, vegetation, wildlife, vehicle, planned, publicSafety, vandalism, unknown)
data.historical.ranked[].labelstringDisplay label.
data.historical.ranked[].hexstringChart color.
data.historical.ranked[].weightnumbercustomer_minutes (historical) or customers_out (live).
data.historical.ranked[].share_pctnumberShare of the classified total.
data.historical.attributedPctnumberPercent of the total with a reported cause.
data.historical.totalWeightnumberClassified + unknown weight.
data.historical.unknownWeightnumberWeight with no reported cause.
data.historical.monthsCoveredintegerDistinct months of history (0 for live).
data.historical.metricstring enumUnit of weight. (customer_minutes, customers_out)
data.liveobject
data.live.rankedarrayClassified categories, largest first.
data.live.ranked[].categorystring enumCause category. (weather, equipment, vegetation, wildlife, vehicle, planned, publicSafety, vandalism, unknown)
data.live.ranked[].labelstringDisplay label.
data.live.ranked[].hexstringChart color.
data.live.ranked[].weightnumbercustomer_minutes (historical) or customers_out (live).
data.live.ranked[].share_pctnumberShare of the classified total.
data.live.attributedPctnumberPercent of the total with a reported cause.
data.live.totalWeightnumberClassified + unknown weight.
data.live.unknownWeightnumberWeight with no reported cause.
data.live.monthsCoveredintegerDistinct months of history (0 for live).
data.live.metricstring enumUnit of weight. (customer_minutes, customers_out)

Live

Live outage status, refreshed about every 10 minutes.

GET/api/v1/live/summary

National and per-state live totals

National live totals, coverage split and states ranked by customers out. Suitable for polling every 5 minutes.

No parameters.

200: application/json500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (32)
FieldTypeDescription
dataobject
data.nationalobject
data.national.customers_outintegerNational customers out (county-level + not localized).
data.national.incident_countintegerActive incidents.
data.national.county_countintegerCounties with customers out.
data.national.state_countintegerStates with live data.
data.national.unmapped_outintegerCustomers out known only at utility/state level.
data.national.updated_atstring | nullTime of the latest live poll.
data.national.as_ofstring | nullOldest region's data time; older than updated_at means some region is carried forward.
data.national.stalebooleanTrue when any region's data is being carried forward.
data.national.stale_outintegerCustomers out whose source is being carried forward (stale).
data.coverageobject
data.coverage.sourcesintegerConfigured live sources reporting this poll.
data.coverage.sources_okintegerSources that fetched successfully this poll.
data.coverage.customers_outintegerNational total.
data.coverage.county_level_outintegerCustomers out localized to a county.
data.coverage.not_localized_outintegerCustomers out known only at utility/state level.
data.coverage.tracked_customersintegerMeasured floor of customers under live watch.
data.statesarrayStates ranked by customers out.
data.states[].state_fipsstring2-digit state FIPS.
data.states[].namestringState name.
data.states[].abbrstringUSPS abbreviation.
data.states[].slugstringURL slug.
data.states[].urlstringRelative MassOutage state page URL.
data.states[].customers_outintegerCustomers out in the state.
data.states[].county_countintegerCounties with customers out.
data.states[].unmapped_outintegerCustomers out not placed on a county.
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
Example response (illustrative values)
{
  "data": {
    "national": {
      "customers_out": 184203,
      "incident_count": 9412,
      "county_count": 611,
      "state_count": 49,
      "unmapped_out": 12050,
      "updated_at": "2026-09-14T13:50:00.000Z",
      "as_of": "2026-09-14T13:50:00.000Z",
      "stale": false,
      "stale_out": 0
    },
    "coverage": {
      "sources": 64,
      "sources_ok": 62,
      "customers_out": 184203,
      "county_level_out": 172153,
      "not_localized_out": 12050,
      "tracked_customers": 98000000
    },
    "states": [
      {
        "state_fips": "34",
        "name": "New Jersey",
        "abbr": "NJ",
        "slug": "new-jersey",
        "customers_out": 5210,
        "county_count": 12,
        "unmapped_out": 0,
        "url": "/new-jersey"
      }
    ]
  },
  "meta": {
    "sources": [
      {
        "name": "ODIN (Outage Data Initiative Nationwide)",
        "url": "https://odin.ornl.gov/"
      }
    ],
    "last_modified": "2026-09-14T13:50:00.000Z"
  }
}
GET/api/v1/live/map

Compact severity map of every county and state out

Keyed arrays for choropleths: `[customers_out, pct_out, level, band, magnitude]`. `band` is the 0-5 share-of-customers ramp the map colours by (1/3/10/25/50%), or -1 where no denominator can be trusted; `magnitude` is a 0-5 headcount ramp. `level` is the pre-2026-09 blend, superseded by `band`; its formula is unchanged, though it moves where a clamped percentage was corrected.

No parameters.

200: application/json500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (4)
FieldTypeDescription
dataobject
data.generated_atstringWhen this response was generated.
data.countiesobjectCounty FIPS → `[customers_out, pct_out, level, band, magnitude]`. `band` is the 0-5 share-of-customers ramp the map colours by (1/3/10/25/50%), or -1 where no denominator can be trusted; `magnitude` is a 0-5 headcount ramp. `level` is the pre-2026-09 blend, superseded by `band`; its formula is unchanged, though it moves where a clamped percentage was corrected. Only counties with customers out appear.
data.statesobjectState FIPS → `[customers_out, pct_out, level, band, magnitude]`. `band` is the 0-5 share-of-customers ramp the map colours by (1/3/10/25/50%), or -1 where no denominator can be trusted; `magnitude` is a 0-5 headcount ramp. `level` is the pre-2026-09 blend, superseded by `band`; its formula is unchanged, though it moves where a clamped percentage was corrected.
Example response (illustrative values)
{
  "data": {
    "generated_at": "2026-09-14T13:50:00.000Z",
    "counties": {
      "34025": [
        1840,
        0.53,
        2
      ]
    },
    "states": {
      "34": [
        5210,
        0.13,
        2
      ]
    }
  }
}
GET/api/v1/live/top

Ranked live leaders

Top 8 states (with top live cause), counties and providers by customers out.

No parameters.

200: application/json500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (21)
FieldTypeDescription
dataobject
data.statesarray
data.states[].state_fipsstringState FIPS.
data.states[].namestringName.
data.states[].abbrstringAbbreviation.
data.states[].slugstringSlug.
data.states[].customers_outintegerCustomers out.
data.states[].county_countintegerCounties out.
data.states[].top_causestring | nullLeading live cause label.
data.countiesarray
data.counties[].fipsstringCounty FIPS.
data.counties[].namestringName.
data.counties[].slugstringSlug.
data.counties[].state_slugstringState slug.
data.counties[].state_abbrstringState abbreviation.
data.counties[].customers_outintegerCustomers out.
data.providersarray
data.providers[].eia_idstring | nullEIA utility id.
data.providers[].namestringName.
data.providers[].slugstringSlug.
data.providers[].customers_outintegerCustomers out.
GET/api/v1/live/timeline

State severity timeline

State-level 0-5 severity resampled to 48 evenly spaced frames over the window.

ParameterInTypeDescription
hoursquerynumber, default 24Window length in hours (clamped 1-48).
200: application/json500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (5)
FieldTypeDescription
dataobject
data.hoursnumberApplied window.
data.framesarray
data.frames[].tstringFrame time.
data.frames[].statesobjectState FIPS → level 0-5 (absent = 0).
GET/api/v1/live/sources

Feed health and coverage (status endpoint)

How the live feeds did on their last poll in aggregate, the county-level vs not-localized split, and connector coverage of US customers. Use it as the status endpoint: check `feeds.down` and `coverage.sources_ok` before trusting a number. Per-feed rows are not published; which utility we read at what resolution is not part of the contract.

No parameters.

200: application/json500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (27)
FieldTypeDescription
dataobject
data.coverageobject
data.coverage.sourcesintegerConfigured live sources reporting this poll.
data.coverage.sources_okintegerSources that fetched successfully this poll.
data.coverage.customers_outintegerNational total.
data.coverage.county_level_outintegerCustomers out localized to a county.
data.coverage.not_localized_outintegerCustomers out known only at utility/state level.
data.coverage.tracked_customersintegerMeasured floor of customers under live watch.
data.feedsobject
data.feeds.totalintegerLive feeds configured.
data.feeds.liveintegerFeeds that answered on the last poll and are within their freshness window.
data.feeds.staleintegerFeeds serving carried-forward last-good data.
data.feeds.downintegerFeeds that failed with no recent data (their areas show a gap).
data.feeds.customers_out_before_dedupeintegerCustomers out summed across feeds, before overlapping feeds are merged. Higher than the national total.
data.feeds.oldest_last_ok_tsstring | nullOldest successful fetch across all feeds.
data.connector_coverageobject
data.connector_coverage.total_customersintegerCustomers on the wires of every utility with an EIA-861 service territory, counted once each (bundled + delivery). Retail-only suppliers are excluded; see Denominator.
data.connector_coverage.county_customersintegerCustomers of utilities reported by county every poll (0 out is a real all-clear).
data.connector_coverage.utility_customersintegerCustomers of utilities reported as a utility total every poll, not by county.
data.connector_coverage.outage_only_customersintegerCustomers of utilities that appear in feeds only while they have outages.
data.connector_coverage.blind_customersintegerCustomers of utilities with no live feed.
data.connector_coverage.eia_yearinteger | nullEIA-861 year of the customer counts.
data.denominatorobject
data.denominator.wires_customersintegerCustomers on the wires of a utility with an EIA-861 service territory, the coverage denominator.
data.denominator.delivery_customersintegerOf those, the ones buying energy from a competitive supplier. Their wires utility restores their outage, so they are counted there, once.
data.denominator.supplier_duplicatesintegerCustomers retail suppliers file for themselves, already counted on their wires utility. Excluded; reported so the exclusion is visible.
data.denominator.eia_yearinteger | nullEIA-861 year of the customer counts.
Example response (illustrative values)
{
  "data": {
    "coverage": {
      "sources": 64,
      "sources_ok": 62,
      "customers_out": 184203,
      "county_level_out": 172153,
      "not_localized_out": 12050,
      "tracked_customers": 98000000
    },
    "feeds": {
      "total": 64,
      "live": 62,
      "stale": 1,
      "down": 1,
      "customers_out_before_dedupe": 196430,
      "oldest_last_ok_ts": "2026-09-14T13:50:00.000Z"
    },
    "connector_coverage": {
      "total_customers": 162000000,
      "county_customers": 70000000,
      "utility_customers": 20000000,
      "outage_only_customers": 50000000,
      "blind_customers": 22000000,
      "eia_year": 2024
    },
    "denominator": {
      "wires_customers": 162000000,
      "delivery_customers": 30000000,
      "supplier_duplicates": 29000000,
      "eia_year": 2024
    }
  }
}
GET/api/v1/live/counties/{fips}

Live status for one county

Customers out, incidents, per-utility breakdown with ETRs, freshness and momentum. Returns zeros (not 404) when nothing is out. `?format=csv` downloads the per-utility breakdown.

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
formatqueryjson | csvResponse format; csv returns the per-utility rows.
200: application/json, text/csv500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (22)
FieldTypeDescription
dataobject
data.fipsstring5-digit county FIPS.
data.customers_outintegerCustomers without power in the county right now (all live sources).
data.customers_trackedinteger | nullCustomers served by sources that publish a denominator.
data.pctnumber | nullcustomers_out / customers_tracked × 100, when a denominator exists.
data.incident_countintegerActive outage incidents.
data.utilitiesarrayPer-utility breakdown of customers out.
data.utilities[].idstringUtility identifier as reported by the live feed.
data.utilities[].namestringUtility name.
data.utilities[].customers_outintegerCustomers out for this utility in the county.
data.utilities[].etrstring | nullUtility's estimated time of restoration (ISO 8601), the utility's estimate; it changes.
data.worst_etrstring | nullLatest utility ETR among active outages (ISO 8601).
data.updated_atstring | nullTime of the poll that produced this rollup. Null when nothing is out.
data.as_ofstring | nullOldest contributing source's fetch time. Older than updated_at means data is carried forward (stale).
data.trendobject | nullOutage momentum; null when nothing is out.
data.trend.directionstring enumMomentum of the current outage. (rising, steady, improving)
data.trend.duration_minintegerMinutes the current contiguous outage run has lasted.
data.trend.peak_outintegerPeak customers out during the current run.
data.trend.seriesarrayRecent samples of the current run, oldest to newest.
data.trend.span_minintegerMinutes covered by `series`.
data.trend.normalcystring enumHow this outage compares with the county's history. (routine, elevated, major)
data.trend.vs_record_pctnumber | nullCurrent customers out as % of this county's worst episode on record.
Example response (illustrative values)
{
  "data": {
    "fips": "34025",
    "customers_out": 1840,
    "customers_tracked": 347000,
    "pct": 0.53,
    "incident_count": 27,
    "utilities": [
      {
        "id": "9726",
        "name": "JCP&L",
        "customers_out": 1620,
        "etr": "2026-09-14T18:00:00.000Z"
      }
    ],
    "worst_etr": "2026-09-14T18:00:00.000Z",
    "updated_at": "2026-09-14T13:50:00.000Z",
    "as_of": "2026-09-14T13:50:00.000Z",
    "trend": {
      "direction": "improving",
      "duration_min": 140,
      "peak_out": 3100,
      "series": [
        3100,
        2800,
        2200,
        1840
      ],
      "span_min": 30,
      "normalcy": "elevated",
      "vs_record_pct": 4.6
    }
  }
}
GET/api/v1/live/counties/{fips}/areas

Live municipalities/townships in a county

Every municipality each utility reports in the county, most affected first, with masked ("<N") outages and ETRs. Only feeds that report areas appear (e.g. NJ PSE&G and JCP&L).

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
formatqueryjson | csv | geojsonResponse format. geojson emits bbox polygons.
utilityquerystringOnly rows for this utility: an exact `utility_key` or a case-insensitive substring of the utility name (e.g. `jcp`, `pse&g`).
activequeryboolean, default false`true` keeps only areas with customers_out > 0 or masked_outages > 0.
min_customersquerynumberOnly areas with at least this many customers out. Negative or non-numeric values are ignored.
200: application/json, text/csv, application/geo+json400: Invalid FIPS or format.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (17)
FieldTypeDescription
dataarray
data[].utility_keystringUtility key (use with ?utility=).
data[].utility_namestringUtility name.
data[].area_typestringArea type as the utility reports it (e.g. Township, Borough, Municipality).
data[].namestringMunicipality/township name as the utility reports it.
data[].customers_servedinteger | nullCustomers the utility serves in this area.
data[].customers_outintegerCustomers out in this area. 0 may still hide masked outages.
data[].masked_outagesintegerOutages the utility reports as "<N" (count hidden for privacy). Unknown size, not zero.
data[].incident_countinteger | nullIncidents in this area.
data[].pct_outnumber | nullcustomers_out / customers_served × 100.
data[].etrstring | nullUtility's estimated restoration for the area (ISO 8601).
data[].bboxarray | null[minLon, minLat, maxLon, maxLat].
data[].as_ofstringWhen the utility's feed was fetched. Rows older than 1 hour are excluded.
as_ofstring | nullLatest feed time across the county's areas (before filters).
metaobjectNotice and echo of applied filters on county detail endpoints.
meta.noticestringTerms of use for the data; same text as the X-Data-Notice header.
meta.filtersobjectThe filters that were applied (null/false when not set).
Example response (illustrative values)
{
  "data": [
    {
      "utility_key": "9726",
      "utility_name": "JCP&L",
      "area_type": "Borough",
      "name": "BRIELLE BOROUGH",
      "customers_served": 2678,
      "customers_out": 412,
      "masked_outages": 0,
      "incident_count": 3,
      "pct_out": 15.38,
      "etr": "2026-09-14T18:00:00.000Z",
      "bbox": [
        -74.08,
        40.09,
        -74.04,
        40.12
      ],
      "as_of": "2026-09-14T13:50:00.000Z"
    }
  ],
  "as_of": "2026-09-14T13:50:00.000Z",
  "meta": {
    "notice": "Unofficial aggregate of public utility and government data; confirm with the utility or your OEM before acting. Emergencies: call 911.",
    "filters": {
      "utility": "jcp",
      "active": true,
      "min_customers": null
    }
  }
}
GET/api/v1/live/counties/{fips}/incidents

Live outage incidents in a county

Individual outages with location, customers out, utility ETR, crew status and cause.

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
formatqueryjson | csv | geojsonResponse format. geojson emits points.
utilityquerystringOnly rows for this utility: an exact `utility_key` or a case-insensitive substring of the utility name (e.g. `jcp`, `pse&g`).
min_customersquerynumberOnly incidents (unknown counts count as 0) with at least this many customers out. Negative or non-numeric values are ignored.
200: application/json, text/csv, application/geo+json400: Invalid FIPS or format.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (16)
FieldTypeDescription
dataarray
data[].utility_keystringUtility key.
data[].utility_namestringUtility name.
data[].latnumberLatitude of the outage marker (utility-provided, often approximate).
data[].lonnumberLongitude.
data[].customers_outinteger | nullCustomers out; null when the utility masks the count.
data[].customers_belowinteger | nullUpper bound when the utility reports "<N".
data[].etrstring | nullUtility's estimated time of restoration (ISO 8601).
data[].crew_statusstring | nullCrew status text as the utility reports it.
data[].causestring | nullCause text as the utility reports it.
data[].started_atstring | nullWhen the outage started.
data[].as_ofstringWhen the utility's feed was fetched.
as_ofstring | nullLatest feed time across the county's incidents (before filters).
metaobjectNotice and echo of applied filters on county detail endpoints.
meta.noticestringTerms of use for the data; same text as the X-Data-Notice header.
meta.filtersobjectThe filters that were applied (null/false when not set).
Example response (illustrative values)
{
  "data": [
    {
      "utility_key": "9726",
      "utility_name": "JCP&L",
      "lat": 40.108,
      "lon": -74.057,
      "customers_out": 212,
      "customers_below": null,
      "etr": "2026-09-14T18:00:00.000Z",
      "crew_status": "Crew assigned",
      "cause": "Tree contact",
      "started_at": "2026-09-14T11:32:00.000Z",
      "as_of": "2026-09-14T13:50:00.000Z"
    }
  ],
  "as_of": "2026-09-14T13:50:00.000Z",
  "meta": {
    "notice": "Unofficial aggregate of public utility and government data; confirm with the utility or your OEM before acting. Emergencies: call 911.",
    "filters": {
      "utility": null,
      "min_customers": 100
    }
  }
}
GET/api/v1/live/counties/{fips}/alerts

Threshold alert status for a county

Evaluates your thresholds for the county, each utility (with the 10% major-event line) and each municipality. Utilities without a live feed are listed with `visible: false` and must not be treated as clear.

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
min_customersquerynumberCustomers-out threshold.
min_pctquerynumberPercent-out threshold (0-100). When both are set, both must be crossed.
utilityquerystringOnly rows for this utility: an exact `utility_key` or a case-insensitive substring of the utility name (e.g. `jcp`, `pse&g`). Narrows utilities and areas; the county figure stays county-wide.
200: application/json400: Invalid FIPS.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (31)
FieldTypeDescription
dataobject
data.fipsstring5-digit county FIPS.
data.thresholdsobject
data.thresholds.min_customersnumber | nullApplied min_customers.
data.thresholds.min_pctnumber | nullApplied min_pct.
data.thresholds.major_event_pctnumberMajor-event percentage line (10).
data.countyobject
data.county.customers_outintegerCounty customers out (county-wide, not narrowed by ?utility).
data.county.pct_outnumber | nullPercent of county customers out.
data.county.above_thresholdbooleanCrosses every threshold that was set; false when none is set.
data.utilitiesarray
data.utilities[].utility_keystringUtility key.
data.utilities[].namestringUtility name.
data.utilities[].connector_statusstring enumHow MassOutage sees this utility: county = reported by county every poll (0 out is a real all-clear); utility = total only; outage_only = appears only while it has outages; none = no feed. (county, utility, outage_only, none)
data.utilities[].customers_outinteger | nullCustomers out in the county; null when not reported.
data.utilities[].customers_servedinteger | nullCustomers served in the county.
data.utilities[].pct_outnumber | nullPercent of the utility's county customers out.
data.utilities[].above_thresholdbooleanCrosses every threshold that was set.
data.utilities[].major_eventbooleanAt least 10% of the utility's county customers out (N.J.A.C. 14:5-8.9 line).
data.utilities[].visiblebooleanTrue only for connector_status county or utility. A non-visible utility can never be reported as clear.
data.areas_above_thresholdarrayMunicipalities crossing the thresholds, largest first (area rows also carry the other LiveArea fields).
data.areas_above_threshold[].utility_keystringUtility key.
data.areas_above_threshold[].utility_namestringUtility name.
data.areas_above_threshold[].namestringMunicipality.
data.areas_above_threshold[].customers_outintegerCustomers out.
data.areas_above_threshold[].customers_servedinteger | nullCustomers served.
data.areas_above_threshold[].pct_outnumber | nullPercent out.
data.as_ofstring | nullCounty live data time.
metaobjectNotice and echo of applied filters on county detail endpoints.
meta.noticestringTerms of use for the data; same text as the X-Data-Notice header.
meta.filtersobjectThe filters that were applied (null/false when not set).
Example response (illustrative values)
{
  "data": {
    "fips": "34025",
    "thresholds": {
      "min_customers": 1000,
      "min_pct": null,
      "major_event_pct": 10
    },
    "county": {
      "customers_out": 1840,
      "pct_out": 0.53,
      "above_threshold": true
    },
    "utilities": [
      {
        "utility_key": "9726",
        "name": "Jersey Central Power & Lt Co",
        "connector_status": "county",
        "customers_out": 1620,
        "customers_served": 297055,
        "pct_out": 0.55,
        "above_threshold": true,
        "major_event": false,
        "visible": true
      },
      {
        "utility_key": "15477",
        "name": "Public Service Elec & Gas Co",
        "connector_status": "county",
        "customers_out": 220,
        "customers_served": 1565,
        "pct_out": 14.06,
        "above_threshold": false,
        "major_event": true,
        "visible": true
      }
    ],
    "areas_above_threshold": [],
    "as_of": "2026-09-14T13:50:00.000Z"
  },
  "meta": {
    "notice": "Unofficial aggregate of public utility and government data; confirm with the utility or your OEM before acting. Emergencies: call 911.",
    "filters": {
      "utility": null,
      "min_customers": 1000,
      "min_pct": null
    }
  }
}

History

Historical series and federal disturbance reports.

GET/api/v1/disturbances

DOE OE-417 major electric disturbances

Summary stats and the 100 most recent OE-417 major disturbance reports.

No parameters.

200: application/json500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (25)
FieldTypeDescription
dataobject
data.statsobject
data.stats.totalintegerReports on file.
data.stats.yearsobject
data.stats.years.mininteger | nullFirst year.
data.stats.years.maxinteger | nullLast year.
data.stats.total_customersintegerCustomers affected across all reports.
data.stats.by_typearray
data.stats.by_type[].event_typestringEvent type.
data.stats.by_type[].nintegerCount.
data.recentarray
data.recent[].idintegerRow id.
data.recent[].began_datestring | nullEvent start.
data.recent[].restored_datestring | nullRestoration.
data.recent[].event_typestring | nullOE-417 event type.
data.recent[].nerc_regionstring | nullNERC region.
data.recent[].area_affectedstring | nullArea affected.
data.recent[].demand_loss_mwnumber | nullDemand loss (MW).
data.recent[].customers_affectedinteger | nullCustomers affected.
data.recent[].yearinteger | nullYear.
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
GET/api/v1/trend/data

Customers-out trend series

Time series for national, state, county or utility scope. `out` is null where the poller had a gap.

ParameterInTypeDescription
fipsquerystringCounty scope (5-digit FIPS). Omit all scopes for national.
stateFipsquerystringState scope (2-digit FIPS).
utilityIdquerystringUtility scope (utility key).
rangequery24h | 7d | 30d | 90d | 12moTime window.
regionquerystring, default United StatesDisplay label echoed back and used in filenames.
formatqueryjson | csvResponse format.
200: application/json, text/csv500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (10)
FieldTypeDescription
dataobject
data.regionstringEchoed region label.
data.rangestringApplied range.
data.scopeobject
data.scope.fipsstring | nullCounty.
data.scope.stateFipsstring | nullState.
data.scope.utilityIdstring | nullUtility.
data.seriesarray
data.series[].tstringSample time.
data.series[].outinteger | nullCustomers out; null where the poller had a gap.
GET/api/v1/reports/data

Annual report hub tables

Year-over-year national EAGLE-I totals and every state-year. `national` lists every report year from 2015 through the last complete year; years whose EAGLE-I data is not loaded yet have `loaded: false` and null values (a gap, not zero). `coverage_pct` is EAGLE-I's share of customers tracked, weighted by state customers.

ParameterInTypeDescription
tablequerynational | statesWhich report table to return.
formatqueryjson | csvResponse format; csv downloads the table.
200: application/json, text/csv400: Unknown table or format.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (11)
FieldTypeDescription
dataobject
data.scopestringAlways US.
data.loaded_yearsarray
data.tablestringTable returned.
data.columnsarrayColumn order (same as the CSV header).
data.rowsarray
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
GET/api/v1/reports/{year}/data

National annual report tables

Tables behind /reports/{year}: every county ranked by average hours without power per customer (`counties`, with customer-hours for absolute ranking), the 25 biggest outage episodes (`events`), states with prior-year change and coverage (`states`), month-by-month totals (`months`), reported causes (`causes`, from utility cause reports; shares exclude unreported) and DOE OE-417 filings (`disturbances`).

ParameterInTypeDescription
year*pathintegerReport year.
tablequerycounties | events | states | months | causes | disturbancesWhich report table to return.
formatqueryjson | csvResponse format; csv downloads the table.
200: application/json, text/csv400: Unknown table or format.404: Year not loaded or not a report year.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (11)
FieldTypeDescription
dataobject
data.scopestringAlways US.
data.yearintegerReport year.
data.tablestringTable returned.
data.columnsarrayColumn order (same as the CSV header).
data.rowsarray
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
GET/api/v1/reports/{year}/{state}/data

State annual report tables

Tables behind /reports/{year}/{state}: all counties in the state ranked, the state's 25 biggest outage episodes, monthly totals and reported causes.

ParameterInTypeDescription
year*pathintegerReport year.
state*pathstringState slug.
tablequerycounties | events | months | causesWhich report table to return.
formatqueryjson | csvResponse format; csv downloads the table.
200: application/json, text/csv400: Unknown table or format.404: Unknown state, or year not loaded.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (12)
FieldTypeDescription
dataobject
data.scopestringState abbreviation.
data.statestringState name.
data.yearintegerReport year.
data.tablestringTable returned.
data.columnsarrayColumn order (same as the CSV header).
data.rowsarray
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).
GET/api/v1/reports/{year}/{state}/{county}/data

County annual report tables

Tables behind the county annual report: a one-row `summary` (customer-hours, average hours per customer, peak, outage episodes at 100/1,000/10,000+ customers, longest episode, state and national averages, state and national rank, state EAGLE-I coverage), the county's `years` (gaps for years not loaded), `months`, its 10 biggest `events`, reported `causes`, and EIA-861 SAIDI/SAIFI of the `utilities` serving it (report year or nearest earlier filing).

ParameterInTypeDescription
year*pathintegerReport year.
state*pathstringState slug.
county*pathstringCounty slug.
tablequerysummary | years | months | events | causes | utilitiesWhich report table to return.
formatqueryjson | csvResponse format; csv downloads the table.
200: application/json, text/csv400: Unknown table or format.404: Unknown place, year not loaded, or no EAGLE-I data for the county that year.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (13)
FieldTypeDescription
dataobject
data.scopestringCounty FIPS.
data.countystringCounty name.
data.statestringState abbreviation.
data.yearintegerReport year.
data.tablestringTable returned.
data.columnsarrayColumn order (same as the CSV header).
data.rowsarray
metaobjectDataset-level provenance. Omitted when there is nothing to report.
meta.sourcesarrayDatasets this response draws on, for citation.
meta.sources[].namestringUpstream dataset name.
meta.sources[].urlstringResolvable URL of the upstream dataset.
meta.last_modifiedstring | nullWhen the underlying data last changed (live poll time on live endpoints).

Events

Outage change events: account-less API and RSS/Atom/JSON feeds, plus the signed webhook contract.

GET/api/v1/events

Outage event stream

Every detected change as an event: outage began, grew (1.5x and at least +250 customers; +50 for municipalities), restoration estimate changed, improving (down 40%), restored (after two polls below half the opening threshold), major event (a utility with 10% or more of its customers out statewide or in a county, the NJ BPU N.J.A.C. 14:5-8.9 line) and data feed stale/recovered. Outages open at the larger of a floor and a share of customers served: counties 500 or 1%, municipalities 50 or 5%, utilities statewide 1,000 or 0.5%. While a subject's feed is stale it is held open and no restored event is emitted. No account needed. Poll with `?after=<next_cursor>` (oldest-first) every 30-60 seconds; without `after` results are newest-first. Events are kept 90 days. The same events drive signed webhooks and Slack/Teams/Discord/email alerts from a free account; see `webhooks`.

ParameterInTypeDescription
afterqueryintegerCursor: events with id greater than this, oldest-first.
fipsquerystringComma-separated 5-digit county FIPS codes.
statequerystringComma-separated 2-digit state FIPS codes.
utilityquerystringComma-separated utility keys (exact).
kindquerystringComma-separated event kinds: outage_began, outage_grew, etr_changed, outage_improving, outage_restored, major_event, feed_stale, feed_recovered.
min_customersquerynumberOnly events with at least this many customers out.
sincequerystringOnly events at or after this ISO 8601 time.
limitqueryinteger, default 100Page size (1-1000).
200: application/json400: Invalid filter.500: Unexpected server error. `request_id` matches the X-Request-Id header.
Response fields (23)
FieldTypeDescription
dataarray
data[].idintegerEvent id; increases monotonically. Use it as the `after` cursor.
data[].tsstringWhen the change was detected (poll time).
data[].kindstring enumWhat changed. (outage_began, outage_grew, etr_changed, outage_improving, outage_restored, major_event, feed_stale, feed_recovered)
data[].scopestring enumWhat the event is about: a county, a municipality as a utility reports it, a utility (statewide, or in one county when `fips` is set), or a data source. (county, municipality, utility, source)
data[].fipsstring | null5-digit county FIPS.
data[].state_fipsstring | null2-digit state FIPS.
data[].utility_keystring | nullUtility key (EIA id where known).
data[].utility_namestring | nullUtility name.
data[].area_namestring | nullMunicipality name as the utility reports it (scope municipality).
data[].sourcestring | nullLive source id (scope source, or the single source behind the reading).
data[].customers_outinteger | nullCustomers out at the time of the event.
data[].customers_servedinteger | nullCustomers served by the subject (denominator), when known.
data[].pctnumber | nullPercent of customers served that are out.
data[].etrstring | nullEarliest utility estimated restoration time. The utility's estimate; it changes.
data[].prevobject | nullPreviously notified values (`customers_out`, `etr`) on change events.
data[].dataobject | nullContext: `county_name`, `state_abbr`, `url`, `opened_at`, `duration_min` and `peak_out` (restored), `major_event`, `utilities` (county scope), `source_name` and `last_ok_ts` (feed events).
data[].titlestringHuman-readable title, e.g. `Outage began · Brielle Borough, Monmouth County NJ`.
data[].summarystringFacts line: utility, customers out, ETR or duration.
data[].urlstringPage for the place on massoutage.com.
next_cursorintegerPass as `after` on the next request to receive only newer events.
has_morebooleanWith `after`: true when the page was full and more events are waiting.
latest_idinteger | nullHighest event id overall (ignores filters).
Example response (illustrative values)
{
  "data": [
    {
      "id": 48213,
      "ts": "2026-09-14T13:50:00.000Z",
      "kind": "outage_began",
      "scope": "municipality",
      "fips": "34025",
      "state_fips": "34",
      "utility_key": "9726",
      "utility_name": "JCP&L",
      "area_name": "BRIELLE BOROUGH",
      "source": "jcpl-34",
      "customers_out": 2000,
      "customers_served": 2678,
      "pct": 74.68,
      "etr": "2026-09-14T22:00:00.000Z",
      "prev": null,
      "data": {
        "subject": "area:9726:34025:BRIELLE BOROUGH",
        "county_name": "Monmouth County",
        "state_abbr": "NJ",
        "url": "/new-jersey/monmouth-county",
        "opened_at": "2026-09-14T13:50:00.000Z",
        "major_event": false
      },
      "title": "Outage began · Brielle Borough, Monmouth County NJ",
      "summary": "JCP&L · 2,000 out (74.7%) · ETR 6:00 PM ET",
      "url": "https://massoutage.com/new-jersey/monmouth-county"
    }
  ],
  "next_cursor": 48213,
  "has_more": false,
  "latest_id": 48213
}
GET/feeds/events.json

Outage events as JSON Feed 1.1

The event stream as a JSON Feed 1.1 feed (newest 50 by default) for feed readers, Slack/Teams RSS apps and EOC tools. Same filters as /api/v1/events except `after`; cached for 60 seconds. Item titles are human-readable and link to the county page. One outage is one item: the county entry carries the utility, town and statewide entries for the same outage in its text and, in the JSON feed, in `_massoutage.related` (each is still its own event in /api/v1/events).

ParameterInTypeDescription
fipsquerystringComma-separated 5-digit county FIPS codes.
statequerystringComma-separated 2-digit state FIPS codes.
utilityquerystringComma-separated utility keys (exact).
kindquerystringComma-separated event kinds: outage_began, outage_grew, etr_changed, outage_improving, outage_restored, major_event, feed_stale, feed_recovered.
min_customersquerynumberOnly events with at least this many customers out.
sincequerystringOnly events at or after this ISO 8601 time.
limitqueryinteger, default 50Items (1-1000).
200: application/feed+json400: Invalid filter.
GET/feeds/events.rss

Outage events as RSS 2.0

The event stream as a RSS 2.0 feed (newest 50 by default) for feed readers, Slack/Teams RSS apps and EOC tools. Same filters as /api/v1/events except `after`; cached for 60 seconds. Item titles are human-readable and link to the county page. One outage is one item: the county entry carries the utility, town and statewide entries for the same outage in its text and, in the JSON feed, in `_massoutage.related` (each is still its own event in /api/v1/events).

ParameterInTypeDescription
fipsquerystringComma-separated 5-digit county FIPS codes.
statequerystringComma-separated 2-digit state FIPS codes.
utilityquerystringComma-separated utility keys (exact).
kindquerystringComma-separated event kinds: outage_began, outage_grew, etr_changed, outage_improving, outage_restored, major_event, feed_stale, feed_recovered.
min_customersquerynumberOnly events with at least this many customers out.
sincequerystringOnly events at or after this ISO 8601 time.
limitqueryinteger, default 50Items (1-1000).
200: application/rss+xml400: Invalid filter.
GET/feeds/events.atom

Outage events as Atom

The event stream as a Atom feed (newest 50 by default) for feed readers, Slack/Teams RSS apps and EOC tools. Same filters as /api/v1/events except `after`; cached for 60 seconds. Item titles are human-readable and link to the county page. One outage is one item: the county entry carries the utility, town and statewide entries for the same outage in its text and, in the JSON feed, in `_massoutage.related` (each is still its own event in /api/v1/events).

ParameterInTypeDescription
fipsquerystringComma-separated 5-digit county FIPS codes.
statequerystringComma-separated 2-digit state FIPS codes.
utilityquerystringComma-separated utility keys (exact).
kindquerystringComma-separated event kinds: outage_began, outage_grew, etr_changed, outage_improving, outage_restored, major_event, feed_stale, feed_recovered.
min_customersquerynumberOnly events with at least this many customers out.
sincequerystringOnly events at or after this ISO 8601 time.
limitqueryinteger, default 50Items (1-1000).
200: application/atom+xml400: Invalid filter.

Charts

Branded PNG charts.

GET/api/v1/counties/{fips}/chart/{type}

County chart PNG

Branded chart image for embedding or download (1200×630, rendered before responding, cached 5 minutes).

ParameterInTypeDescription
fips*pathstring5-digit county FIPS code (state + county), e.g. 34025 for Monmouth County, NJ.
type*pathdecade | monthly | live | causesChart type.
themequerydark | lightChart color scheme.
formatquerypng | csv | jsonpng renders the chart; csv and json return exactly the plotted series.
200: image/png, text/csv404: Unknown county or chart type (plain text).
GET/api/v1/trend/chart

Trend chart PNG

Branded PNG of the trend series (peak per interval, gaps left empty), stamped with its data-as-of time.

ParameterInTypeDescription
fipsquerystringCounty scope (5-digit FIPS). Omit all scopes for national.
stateFipsquerystringState scope (2-digit FIPS).
utilityIdquerystringUtility scope (utility key).
rangequery24h | 7d | 30d | 90d | 12moTime window.
regionquerystring, default United StatesDisplay label echoed back and used in filenames.
themequerydark | lightChart color scheme.
formatquerypng | csv | jsonpng renders the chart; csv and json return exactly the plotted series.
200: image/png, text/csv
GET/api/v1/reports/{year}/chart

National annual report chart PNG

Decade trend of average hours without power per customer, the year highlighted.

ParameterInTypeDescription
year*pathintegerReport year.
themequerydark | lightChart color scheme.
formatquerypng | csv | jsonpng renders the chart; csv and json return exactly the plotted series.
highlightquerystringBar to highlight: a year, max (worst year) or none. Defaults to the report year.
200: image/png, text/csv404: No report for that year (plain text).
GET/api/v1/reports/{year}/{state}/chart

State annual report chart PNG

Decade trend for one state, the year highlighted.

ParameterInTypeDescription
year*pathintegerReport year.
state*pathstringState slug.
themequerydark | lightChart color scheme.
formatquerypng | csv | jsonpng renders the chart; csv and json return exactly the plotted series.
highlightquerystringBar to highlight: a year, max (worst year) or none. Defaults to the report year.
200: image/png, text/csv404: Unknown state or year (plain text).

Meta

API description.

GET/api/v1/openapi.json

This OpenAPI document

Machine-readable description of the v1 API.

No parameters.

200: application/json