--- name: usgs-earthquakes-scraper description: Read global seismic events from the United States Geological Survey as structured records via the Apify Actor arman-bd/usgs-earthquakes-scraper. Returns one record per event with the stable USGS event ID, magnitude and magnitude scale, place name, origin and revision times, epicentre coordinates, hypocentre depth, tsunami-zone flag, PAGER alert level, felt-report count and community intensity, ShakeMap intensity, the USGS significance score and the event page URL. Use for disaster-response triggers, catastrophe and insurance modelling, live public-safety maps, newsroom monitoring or reproducible regional seismicity extracts back to 1900. Not for aftershock forecasts, fault or tectonic models, ShakeMap or waveform products, or non-USGS regional networks. --- # USGS Earthquake Scraper Apify Actor `arman-bd/usgs-earthquakes-scraper`. With no input at all it returns every event of the past day. Set a date window and the same call reaches the full USGS catalogue back to 1900 instead, with an identical output shape either way. It runs without credentials and a summary-feed run finishes in well under a second, which makes it cheap to put on a five-minute schedule. ## When to use it - Watch for significant events on a schedule and fire on `alert` turning orange or red. - Pull decades of events above a magnitude floor inside a bounding box for exposure modelling. - Feed a live map with magnitude, depth and epicentre from the past hour. - Find the quakes people actually noticed rather than the largest ones, using `felt` and `cdi`. - Produce a reproducible regional seismicity extract with a stable schema. ## When not to use it - Aftershock probabilities, hazard curves or fault models. Only observed events are returned. - ShakeMap grids, waveforms, moment tensors or any other USGS product. You get the headline intensity numbers, not the underlying data. - Regional networks USGS does not aggregate. - Sub-event detail such as contributing stations, phases or origin uncertainty. ## Call it ```js import { ApifyClient } from 'apify-client'; const client = new ApifyClient({ token: process.env.APIFY_TOKEN }); const run = await client.actor('arman-bd/usgs-earthquakes-scraper').call({ fromDate: '2026-07-01', toDate: '2026-08-01', minMagnitude: '5', boundingBox: { minLatitude: 30, maxLatitude: 45, minLongitude: 125, maxLongitude: 150 }, maxResults: 0, }); const { items } = await client.dataset(run.defaultDatasetId).listItems(); const { value: summary } = await client .keyValueStore(run.defaultKeyValueStoreId) .getRecord('RUN_SUMMARY'); ``` One-shot over HTTP, when you want the rows back in the same request: ```bash curl -X POST "https://api.apify.com/v2/acts/arman-bd~usgs-earthquakes-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"feed":"significant_month","minMagnitude":"5"}' ``` The Actor is also exposed through Apify's MCP server as `arman-bd/usgs-earthquakes-scraper`, so an MCP-capable agent can call it with no extra wiring. ## Input | Field | Type | Required | Default | Notes | |---|---|---|---|---| | `feed` | string | no | `"all_day"` | One of 20 pre-baked summary feeds, named as a magnitude floor and a window joined by an underscore: `all`, `1.0`, `2.5`, `4.5` or `significant`, each with `hour`, `day`, `week` or `month`. Case-insensitive, and a full feed URL is reduced to the name. An unknown name throws. **Ignored entirely when a date window is set.** | | `minMagnitude` | string | no | `null` | Magnitude floor such as `"4.5"`. A string because Apify input schemas carry no decimal type; it is parsed to a number. Empty means no floor, which is not the same as a floor of zero. | | `boundingBox` | object | no | `null` | Any subset of `minLatitude`, `maxLatitude`, `minLongitude`, `maxLongitude`. Aliases are accepted: `minLat`, `maxLat`, `minLon`, `maxLon`, `minLng`, `maxLng`, `north`, `south`, `east`, `west`. Omitted edges are unbounded. **A bare array is rejected**, because its axis order is ambiguous, and an object with no recognisable numeric edge throws rather than silently returning the world. | | `fromDate` | string | no | `null` | `YYYY-MM-DD`, an ISO 8601 timestamp, or a millisecond epoch. **Setting this switches the run to the full catalogue.** An unparseable value throws. | | `toDate` | string | no | `null` | Same formats. Also switches to the catalogue, and defaults to now when only `fromDate` is set. Earlier than `fromDate` throws. | | `maxResults` | integer | no | `0` | Cap on events saved across the run. `0` or negative means no cap. Catalogue paging stops the moment the cap is met. | **Setting any date is the decision that changes everything else.** With no date window the run reads a static summary feed: one request, sub-second, covering an hour to a month, and `minMagnitude` and `boundingBox` are applied to the records after they arrive. With a date window the run queries the full catalogue instead, `feed` is not read at all, the same two filters are applied at the source, and paging runs 20,000 events at a time until a short page arrives. The output is identical; only the reach, the cost and the latency differ. Pick a feed whose own magnitude floor is already close to what you want, because in feed mode a `minMagnitude` of 5 on `all_hour` still transfers every event before discarding almost all of them. ## Output One record per event. | Field | Type | Notes | |---|---|---| | `eventId` | string \| null | USGS event ID such as `us6000ti8i`. Stable across revisions; de-duplicate on it. | | `magnitude` | number \| null | Null when the event carries no magnitude, which a magnitude filter then excludes. | | `magType` | string \| null | The scale it was measured on: `mww`, `ml`, `mb` and others. Not comparable between scales without care. | | `place` | string \| null | Human-readable location, for example `40 km SW of Sarangani, Philippines`. Free text, not a geocode. | | `time` | string \| null | Origin time, ISO 8601 UTC, converted from epoch milliseconds. | | `updated` | string \| null | Last revision. Changes as USGS refines a solution. | | `longitude` | number \| null | Epicentre. | | `latitude` | number \| null | Epicentre. | | `depth` | number \| null | Hypocentre depth in kilometres. Can be negative for events located above sea level. | | `tsunami` | boolean | True when the event lies in a tsunami-notification region. **Not a statement that a tsunami occurred.** | | `alert` | string \| null | PAGER impact level: `green`, `yellow`, `orange` or `red`. Null on smaller events, which is normal rather than an error. | | `felt` | number \| null | Count of community felt reports. Null when nobody filed one. | | `cdi` | number \| null | Community-reported intensity derived from those reports. | | `mmi` | number \| null | Maximum estimated Modified Mercalli intensity from ShakeMap. | | `significance` | number \| null | USGS significance score, 0 to 1000, blending magnitude, felt reports and estimated impact. The `significant` feeds select above 600. | | `url` | string \| null | USGS event page. | | `scrapedAt` | string | Run timestamp, ISO 8601. | A real record: ```json { "eventId": "us6000ti8i", "magnitude": 6.3, "magType": "mww", "place": "south of the Kermadec Islands", "time": "2026-08-05T11:43:27.317Z", "updated": "2026-08-06T11:51:38.507Z", "longitude": 179.4727, "latitude": -33.8063, "depth": 226.084, "tsunami": false, "alert": "green", "felt": 14, "cdi": 3.1, "mmi": 3.316, "significance": 615, "url": "https://earthquake.usgs.gov/earthquakes/eventpage/us6000ti8i", "scrapedAt": "2026-08-06T12:00:00.000Z" } ``` ## RUN_SUMMARY Written to the run's key-value store under the key `RUN_SUMMARY`. **Read it.** In catalogue mode it is the only place a truncated page walk is recorded. ```json { "mode": "catalogue", "feed": null, "requests": 1, "eventsSeen": 47, "eventsSaved": 47, "sourcesFailed": 0, "failures": [], "filters": { "minMagnitude": 5, "boundingBox": { "minLatitude": 30, "maxLatitude": 45, "minLongitude": 125, "maxLongitude": 150 }, "fromDate": "2026-07-01T00:00:00.000Z", "toDate": "2026-08-01T00:00:00.000Z", "maxResults": 0 }, "finishedAt": "2026-08-06T12:00:09.441Z" } ``` `mode` tells you which of the two paths ran, and `feed` is null whenever `mode` is `catalogue`. In feed mode `eventsSeen` is everything the feed listed and `eventsSaved` is what survived your filters, so the gap between them is the filters' work. In catalogue mode the source has already filtered, so the two normally agree and a gap means `maxResults` bit. `filters` echoes the **parsed** values: `minMagnitude` comes back as a number, the dates as full ISO timestamps, and `boundingBox` with all four edges present and `null` for the ones you omitted. A non-empty `failures` alongside a healthy `eventsSaved` is the important case: the catalogue walk stopped early and the dataset is a prefix of the window you asked for. ## Behaviour to plan around - **A date silently overrides `feed`.** Passing both is not an error and not a warning in the summary beyond `mode`. Check `mode` before assuming which window you got. - **A partial catalogue run does not throw.** A failing page breaks the walk, everything already collected is kept, and the run finishes successfully. Only a run that saved nothing *and* had a failure throws. Always compare `failures` against your expected window. - **`maxResults` truncates by time order.** The catalogue is walked newest first, so a cap gives you the most recent events in the window, never a sample of it. - **A magnitude floor excludes events with no magnitude.** That is deliberate: a null magnitude would otherwise pass a floor of 0. A run with `minMagnitude: "0"` therefore returns fewer events than one with no floor at all. - **A bounding box crossing the antimeridian does not work in feed mode.** Edges are compared with plain inequalities, so `minLongitude: 170` with `maxLongitude: -170` matches nothing. Split it into two runs. - **`tsunami` means "in a notification zone".** Do not report it as a tsunami having happened. - **`updated` moves after the fact.** Re-running a window legitimately returns revised copies of events you already have. De-duplicate on `eventId` and take the row with the later `updated`. - **Catalogue paging is discovered, not declared.** No total is returned, so pages of 20,000 are requested until a short one arrives. That makes the request count in `RUN_SUMMARY.requests` the only measure of how deep the walk went. - **Transient failures get four attempts** with exponential backoff starting at 0.5 seconds. A malformed query is rejected immediately rather than burning retries. ## Recipes **Five-minute watch loop.** Cheapest possible shape: one static feed, filtered locally. ```json { "feed": "all_hour", "minMagnitude": "2.5", "maxResults": 200 } ``` De-duplicate on `eventId` against your store, and alert when `alert` is `orange` or `red` or `significance` clears your own threshold. **Regional catastrophe extract.** A date window switches to the catalogue automatically. ```json { "fromDate": "2016-01-01", "toDate": "2026-01-01", "minMagnitude": "5", "boundingBox": { "minLatitude": 30, "maxLatitude": 45, "minLongitude": 125, "maxLongitude": 150 } } ``` Check `RUN_SUMMARY.failures` is empty before treating the decade as complete. **Newsroom sweep for events people felt.** Broad feed, no magnitude floor. ```json { "feed": "all_day" } ``` Sort on `felt` and `cdi` rather than `magnitude`: a shallow M4 in a city outranks a deep M6 offshore. **Notable events of the past month.** The feed does the selection for you. ```json { "feed": "significant_month", "maxResults": 100 } ``` `significance` above 600 is what put each event in this feed, so it is already the right sort key.