MSHA Mine Data Scraper - Violations, Inspections, Accidents
parseforge/msha-mine-data-scraper
Other
Streams all 20 MSHA Open Government Data files, 17.8 million rows on US mines, violations, inspections, accidents, penalties, production and health samples.
- Total runs
- 23
- Bookmarked
- 0
- Last modified
- 8 days ago
This scraper was last updated on .
What does the MSHA Mine Data Scraper - Violations, Inspections, Accidents return?
MSHA Mine Data Scraper - Violations, Inspections & Accidents
Scrape MSHA mine data from all 20 Open Government Data files: 17,856,581 rows covering every US mine since 1970 and every violation, inspection and accident since 2000. Every row is a flat record with the source's own field names mapped to readable keys, dates normalised to ISO, and no empty columns. No login, no API key, no FOIA request. Export to CSV, JSON, Excel, or XML.
MSHA publishes this data as 582 MB of ZIP archives that you are expected to download whole, unzip, and load into a database before you can ask a single question. This Actor streams the archives and stops reading the moment it has your rows, so a 10-row request against the 1.4 GB violations file transfers a few kilobytes instead of 120 MB, and filters by mine, state, date, penalty or injury run while the file streams.
| Who uses it | What they scrape MSHA data for |
|---|---|
| Mine safety consultants | Violation and inspection history for a client's mine ID before an audit |
| Insurance underwriters | Injury rates, penalty history and employment size to price a mining risk |
| Industrial sales teams | Active mines by state, commodity and employee count with operator contacts |
| Journalists and researchers | Fatality records, narratives and enforcement outcomes as primary sources |
| Legal and compliance teams | Contested citations, FMSHRC dockets and civil penalty decisions |
What it does
This Actor reads MSHA's Open Government Data archives and returns each record as a flat row. Pick one dataset or all twenty in a single run:
- โ๏ธ Mines: 91,979 mines since 1970 with status, type, controller, operator, commodity, SIC, employee count and coordinates.
- ๐จ Enforcement: 3,096,400 violations, 3,029,093 assessed penalties, 451,718 contested citations, 481,753 civil penalty dockets, 162,065 conferences and 3,861 imminent danger orders.
- ๐ Inspections: 1,156,532 inspection events with activity, inspector count, on-site hours and the areas covered.
- ๐ฉน Accidents: 274,448 injury and illness reports from MSHA form 7000-1, including the full free-text narrative.
- ๐ Production: 5,062,796 employment and production rows for mine operators and contractors, quarterly and yearly.
- ๐งช Health samples: 3,784,443 coal dust, quartz, area, personal health and noise samples.
- ๐ Optional joins: add mine master details, operator contact addresses or the latest production summary to any row that carries a mine ID.
Results export to CSV, JSON, Excel, or XML, or stream from the API.
What you can do with MSHA data
๐ญ Build a targeted list of active mines. Filter 91,979 mines down to the ones that matter: active status, a given state, sand and gravel or stone, above a minimum employee count. Tick the operator contact block and every row carries the business name, contact title and street address MSHA has on file.
โ๏ธ Reconstruct a mine's enforcement history. Feed a mine ID and select violations, assessed violations, contested violations and penalty dockets in one run. You get the citation, its gravity and negligence findings, the penalty proposed, what was actually paid, whether it was contested and how the judge ruled.
๐ Study injuries and fatalities. The accidents dataset carries the degree of injury, the classification, the equipment involved, the miner's experience, days lost, and the inspector's narrative. Filter to fatalities only and you have all 1,210 fatal records with their stories.
๐งญ Track an operator across its mines. Controller and operator history maps 169,809 controller-to-operator associations with start and end dates, so you can follow who owned which mine and when, then join production and violations onto the same mine IDs.
Why choose this scraper
| What you get | |
|---|---|
| Full coverage | All 20 live MSHA data files, 17,856,581 rows, 562 source columns, nothing summarised away |
| Streamed, not downloaded | Reads the ZIP as it arrives and stops early, so small requests stay small |
| Filters that run upstream of your dataset | Mine ID, state, date, penalty, injury, negligence and 16 more, applied while the file streams |
| Clean rows | ISO dates, real numbers, Yes/No booleans, "N/A" where MSHA withheld a value, and zero always-empty columns |
| Joins MSHA makes you do yourself | Mine master, operator address and production summary attached by mine ID |
| Priced per row | You pay for rows written, not for the 582 MB the source makes you fetch |
How it compares
Four other MSHA Actors exist on the Apify Store, and each covers one slice: a mine lookup, an operator lead list, or the assessed violations file. None of them reads all twenty files, and none joins the mine master onto the enforcement rows. This Actor costs more per row than the cheapest of them, and what the difference buys is coverage and the joins: one run can return mines, their violations, their inspections, their accidents and their production, with state and status filters working even on the files that carry neither column.
| This Actor | Typical single-file MSHA Actor | Downloading the files yourself | |
|---|---|---|---|
| Datasets covered | 20 | 1 to 2 | 20 |
| Rows reachable | 17,856,581 | Under 3.1 million | 17,856,581 |
| Filters | 22, applied while streaming | Usually a mine ID | Whatever you write |
| Bytes moved for 10 rows | A few KB | Varies | 582 MB, then unzip |
| Mine master join | Built in | No | Write it yourself |
| Setup | None | None | Download, unzip, parse, load |
What an MSHA mine record looks like
One real row from a verified run, unedited:
{
"dataset": "mines",
"mineId": "0100003",
"mineName": "O'Neal Quarry & Mill",
"coalOrMetal": "M",
"mineType": "Surface",
"mineStatus": "Active",
"mineStatusDate": "1979-01-22",
"controllerId": "0041044",
"controllerName": "Lhoist Group",
"operatorId": "L13586",
"operatorName": "Lhoist North America of Alabama, LLC",
"state": "AL",
"bureauOfMinesStateCode": "01",
"countyFipsCode": "117",
"countyName": "Shelby",
"congressionalDistrict": "06",
"companyType": "Other",
"controllerBeginDate": "1989-07-01",
"district": "M3",
"officeCode": "M3661",
"officeName": "Birmingham AL Field Office (A)",
"assessmentControlNo": "000638053",
"primarySicCode": "327400",
"primarySicDescription": "Lime",
"primarySicCodeShort": "3274",
"primarySicSuffix": "00",
"secondarySicCode": "142200",
"secondarySicDescription": "Crushed, Broken Limestone NEC",
"secondarySicCodeShort": "1422",
"secondarySicSuffix": "00",
"primaryCommodityCode": "6",
"primaryCommodity": "Stone",
"secondaryCommodityCode": "6",
"secondaryCommodity": "Stone",
"status103i": "Never Had 103I Status",
"status103iDate": "N/A",
"portableOperation": "No",
"portableStateFipsCode": "N/A",
"daysPerWeek": 7,
"hoursPerShift": 8,
"productionShiftsPerDay": 3,
"maintenanceShiftsPerDay": 0,
"employeeCount": 108,
"part48Training": "Yes",
"longitude": -86.758889,
"latitude": 33.177778,
"avgMineHeightInches": "N/A",
"gasCategoryCode": "N/A",
"methaneLiberationCfd": "N/A",
"producingPitCount": "N/A",
"nonProducingPitCount": "N/A",
"tailingPondCount": 0,
"pillarRecoveryUsed": "No",
"highwallMinerUsed": "No",
"multiplePits": "No",
"minersRepresentative": "No",
"safetyCommittee": "No",
"milesFromOffice": 100,
"directionsToMine": "Approx 7 miles south of the city of Alabaster on Hwy 31.",
"nearestTown": "Calera",
"mineDataRetrievalUrl": "https://www.msha.gov/mine-data-retrieval-system?mineid=0100003",
"scrapedAt": "2026-08-27T16:20:57.704Z"
}
Rows from the other 19 datasets carry that dataset's own columns instead. Violations, inspections, accidents and the sample files each have their own shape, and the dataset key tells you which one you are looking at.
Configure the run
Pick datasets, set maxItems, and add filters. Filters run as each record is read, so only matches reach your dataset, and a filter naming a column the selected dataset does not have is ignored for that dataset. The Input tab lists every parameter.
Active sand and gravel mines in Texas, with the operator's mailing address:
{ "datasets": ["mines"], "states": ["TX"], "mineStatuses": ["Active"], "commodities": ["SandAndGravel"], "includeOperatorContact": true, "maxItems": 500 }
Everything MSHA has on one mine, across four enforcement datasets:
{ "datasets": ["violations", "assessedViolations", "contestedViolations", "penaltyDockets"], "mineIds": ["0100003"], "maxItems": 2000 }
The most recent fatal accidents in West Virginia, with the mine master joined on:
{ "datasets": ["accidents"], "states": ["WV"], "fatalitiesOnly": true, "sortBy": "newestFirst", "includeMineDetails": true, "maxItems": 200 }
Free users
Free-plan runs return up to 10 rows as a preview. Upgrade your Apify plan to collect up to 1,000,000 rows per run.
Run it
- Create a free Apify account with $5 in credit.
- Open the MSHA Mine Data Scraper.
- Pick one or more
datasets, setmaxItems, add any filters, and click Start. - Export the results as CSV, Excel, JSON, or XML from the Dataset tab.
Run it programmatically through the Apify API or the ApifyClient for JavaScript and Python.
Use with AI agents (MCP)
Give an AI agent live access to MSHA data through the Model Context Protocol. Add the Actor to Claude, Cursor, or any MCP client:
claude mcp add --transport http apify "https://mcp.apify.com?tools=parseforge/msha-mine-data-scraper"
Then prompt it in plain language:
- "List the active underground coal mines in West Virginia with more than 50 employees."
- "Pull every violation issued at mine ID 4600015 since 2024 and total the proposed penalties."
- "Find the most recent fatal mining accidents and summarise the narratives."
Troubleshooting
No results at all. Check that the filters you set exist on the dataset you picked. mineIds returns nothing on contractor production or conferences, because neither file carries a mine ID. degreesOfInjury only applies to accidents.
Fewer rows than I asked for. Either the filters matched fewer records than maxItems, or the run hit its time limit on a large file. Selecting several datasets splits maxItems between them unless you set maxItemsPerDataset. Free-plan runs stop at 10 rows.
A sorted run returned fewer rows than an unsorted one. Sorting has to read the whole file and hold the candidates, so it is capped by the run's memory and by a hard ceiling of 50,000 rows per dataset. The log says the cap it settled on. Narrow the filters, drop the optional joins, or use file order to get the full set.
A field is empty. MSHA leaves fields blank when it never recorded the value, and those arrive as "N/A". Coordinates are present on 51.8% of mines and sectionOfAct on 0.6% of violations; that is the source, not the scraper. Columns that are empty in every single row upstream are dropped rather than shipped.
The run is slow. File order is the fast path and stops early. A sort, or a filter that matches rarely, forces a full read of files that reach 1.4 GB uncompressed: a full pass over all 3,096,400 violations takes a couple of minutes. Narrow the date range or the mine list to cut the work.
FAQ
| Question | Answer |
|---|---|
| Do I need an MSHA account or API key? | No. These are public Open Government Data files served without any login or rate limit. |
| How fresh is the data? | MSHA refreshes the files every Friday afternoon. Each run reports the publication date it read. |
| Which datasets are included? | All 20 live ones: mines, violations, assessed and contested violations, inspections, accidents, penalty dockets, conferences, 107(a) orders, addresses, controller history, four production files and five sample files. |
| Can I get everything for one mine in a single run? | Yes. Select the datasets you want and set mineIds. Every dataset that carries a mine ID is filtered to it. |
| Can I filter violations by state? | Yes. The violations file has no state column, so the Actor resolves it through the mine master. That lookup is loaded once and is not billed. |
| What is the mine ID format? | Seven characters, zero padded, for example 0100003. Shorter IDs are padded for you. |
| How many rows per run? | Free plan: 10. Paid: up to 1,000,000, bounded by what your filters match and the run's time limit. |
| Are the accident narratives included? | Yes, the full free-text narrative from MSHA form 7000-1 is in the narrative field of every accident row. |
Why are some numeric fields "N/A"? |
Because MSHA left them blank. The Actor never emits a literal null, so a withheld number arrives as "N/A" and a real one as a number. |
| Is this an official MSHA product? | No. It is unofficial and reads only publicly published MSHA data files. |
๐ Need help? Email parseforge@protonmail.com with your run ID, your input, and what you expected.
โ ๏ธ Disclaimer. This Actor is unofficial and is not affiliated with, endorsed by, or sponsored by the Mine Safety and Health Administration or the US Department of Labor. It collects only publicly published MSHA Open Government Data. You are responsible for using the data in compliance with applicable laws, including GDPR, CCPA, and PIPL. Accident records describe injuries to identifiable workers; do not use them to identify, profile, or target individuals.
What input does the MSHA Mine Data Scraper - Violations, Inspections, Accidents accept?
| Field | Type | What it does | Default |
|---|---|---|---|
| datasets | array | Which MSHA Open Government Data files to read. Each selected dataset is downloaded and streamed in turn, and every dataset bills its own event. Row counts are from the 2026-08-21 publication. | ["mines"] |
| maxItems | integer | Free users: limited to 10 items (preview). Paid users: up to 1,000,000. This is the total across every selected dataset. | 10 |
| maxItemsPerDataset | integer | Cap for each selected dataset. Leave empty and the Max Items budget is split evenly across the datasets you selected. | not set |
| mineIds | array | Seven digit MSHA mine IDs, for example 0100003. Shorter IDs are zero padded for you. Applies to every dataset that carries a mine ID; contractor production and conferences do not, so they return nothing when this is set. | not set |
| companyIds | array | Controller, operator, contractor or violator IDs, for example 0041044 or L13586. A row matches when any of the dataset's company ID columns equals one of these. | not set |
| states | array | Two letter state or territory. Datasets without a state column are matched through the mine master, which is loaded once and not billed. | not set |
| coalOrMetal | string (3 options) | Restrict to coal mines or to metal and nonmetal mines. | any |
| searchText | string | Case insensitive substring matched against the dataset's name columns: mine, operator, controller, contractor and violator names, plus the accident narrative. Datasets with no name column match their identifier columns instead. | not set |
| dateFrom | string | Inclusive lower bound on the dataset's main date, in YYYY-MM-DD. On the four employment and production files, which only carry a calendar year, the year part is compared instead. | not set |
| dateTo | string | Inclusive upper bound on the dataset's main date, in YYYY-MM-DD. | not set |
| mineStatuses | array | Current mine status. Applies to the mines, addresses and controller history datasets directly, and to the rest through the mine master. | not set |
| mineTypes | array | Underground, surface or facility. | not set |
| commodities | array | Primary canvass commodity from the mine master. | not set |
| subunits | array | Location or operation within the mine. Applies to accidents and to the four employment and production datasets. | not set |
| documentTypes | array | Citation, order, safeguard or notice. Applies to the violations, assessed violations, contested violations and penalty docket datasets. | not set |
| significantAndSubstantial | string (3 options) | S&S designation on the citation or order. | any |
| negligenceLevels | array | Inspector's negligence finding. | not set |
| likelihoodLevels | array | Gravity likelihood recorded on the citation. | not set |
| injuryIllnessLevels | array | Gravity injury or illness that could reasonably be expected. | not set |
| minPenaltyUsd | number | Keep only rows whose proposed penalty is at least this much. Decimals are allowed. | not set |
| maxPenaltyUsd | number | Keep only rows whose proposed penalty is at most this much. Decimals are allowed. | not set |
| degreesOfInjury | array | Degree of injury recorded on MSHA form 7000-1. | not set |
| accidentClassifications | array | MSHA accident classification, the 29 values that appear in the file. | not set |
| fatalitiesOnly | boolean | Shortcut for degree of injury FATALITY. There are 1,210 fatal records in the file. | false |
| activityContains | string | Case insensitive substring of the inspection activity, for example "regular" or "103(i)". The file uses 86 activity spellings, so a substring beats an exact list. Applies to the inspections dataset only. | not set |
| sortBy | string (5 options) | File order streams and stops early, which is by far the fastest. Any other order has to read the whole file before it can answer, so it costs the full download of every selected dataset and is capped at 50,000 rows per dataset. | fileOrder |
| includeMineDetails | boolean | Join the mine master onto every row that carries a mine ID, adding mine name, type, status, state, county, commodity, SIC, controller, operator, employee count and coordinates. Billed per row actually enriched. Skipped on the mines dataset, which already has these columns. | false |
| includeOperatorContact | boolean | Join the mine addresses of record, adding business name, contact title, street, PO box, city, state, ZIP and country. Billed per row actually enriched. | false |
| includeProductionSummary | boolean | Join the latest reported calendar year from the yearly employment file, summed across subunits: hours, coal production in short tons and average employees. Billed per row actually enriched. | false |
| proxyConfiguration | object | MSHA serves these files without any bot protection, so no proxy is needed. Turn one on only if your own network cannot reach arlweb.msha.gov. | {"useApifyProxy":false} |
How much does the MSHA Mine Data Scraper - Violations, Inspections, Accidents cost?
from $2.67 per 1,000 results
| Charged for | What it is | Price each |
|---|---|---|
| Actor Start | Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). | $0.0178 to $0.02 |
| Dataset opened | One per MSHA data file opened and streamed, whatever its size. Select three datasets, pay three. | $0.0178 to $0.02 |
| Mine record | One row from the Mines master file: 60 fields including status, controller, operator, commodity, SIC and coordinates. | $0.00623 to $0.007 |
| Violation record | One citation, order or safeguard from the Violations file, with gravity, negligence, penalty and termination fields. | $0.00623 to $0.007 |
| Assessed violation record | One row from the Assessed Violations file, with the penalty points breakdown, amounts proposed, assessed and paid. | $0.00623 to $0.007 |
| Contested violation record | One contested citation before the FMSHRC, with the judge, the original attributes and the decision attributes side by side. | $0.00623 to $0.007 |
| Inspection record | One MSHA inspection event, with activity, inspector count, on-site hours and the areas covered. | $0.00623 to $0.007 |
| Accident record | One accident, injury or illness from MSHA form 7000-1, including the free-text narrative and the degree of injury. | $0.00623 to $0.007 |
| Penalty docket record | One civil penalty docket line, with the original and decided penalty amounts, the decision type and the appeal. | $0.00623 to $0.007 |
| Conference record | One completed safety and health conference, with who requested it and how the citation ended up. | $0.00623 to $0.007 |
| 107(a) order record | One imminent danger order, with the mine, operator, controller, contractor and the termination date. | $0.00623 to $0.007 |
| Mine address record | One mine address of record: business name, contact title, street, PO box, city, state, ZIP and country. | $0.00623 to $0.007 |
| Controller history record | One controller-to-operator association at a mine, with the start and end dates on both sides. | $0.00623 to $0.007 |
| Mine production record | One mine employment and production row, quarterly or yearly, by subunit: employees, hours and coal short tons. | $0.00623 to $0.007 |
| Contractor production record | One contractor employment and production row, quarterly or yearly, by subunit. | $0.00623 to $0.007 |
| Coal dust or quartz sample | One respirable coal dust or quartz sample: cassette, weights, concentration, occupation and void reason. | $0.00623 to $0.007 |
| Exposure or noise sample | One metal/nonmetal area sample, personal health sample or noise survey, with contaminant, concentration and action taken. | $0.00623 to $0.007 |
| Mine master details added | Optional. Charged only when the mine master actually resolved the row: name, type, status, state, county, commodity, SIC, controller, operator, employees, coordinates. | $0.00267 to $0.003 |
| Operator contact added | Optional. Charged only when the address of record resolved: business name, contact title, street, PO box, city, state, ZIP, country. | $0.00267 to $0.003 |
| Production summary added | Optional. Charged only when the yearly employment file resolved: latest reported year, hours, coal short tons, average employees. | $0.00267 to $0.003 |
Tiered: the lower figure is the price on a higher Apify plan. Billing and the free credit live on Apify.
How do I call the MSHA Mine Data Scraper - Violations, Inspections, Accidents API?
One POST returns the dataset directly. Same shape for every scraper in the library, so swapping the slug is the only change.
curl -X POST "https://api.apify.com/v2/acts/parseforge~msha-mine-data-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"datasets": [
"mines"
],
"maxItems": 10,
"coalOrMetal": "any",
"significantAndSubstantial": "any",
"fatalitiesOnly": false
}' What example inputs can I use?
Use these inputs to see how a run is configured.
{
"datasets": [
"mines"
],
"maxItems": 10,
"coalOrMetal": "any",
"significantAndSubstantial": "any",
"fatalitiesOnly": false
} What do users say about the MSHA Mine Data Scraper - Violations, Inspections, Accidents?
No reviews yet. Be the first.
How do I report an issue with the MSHA Mine Data Scraper - Violations, Inspections, Accidents?
We build and maintain this scraper, so a problem with it comes to us. Report it on the Apify listing and the thread stays attached to the scraper where the next person can find it: open an issue.
Broken and urgent, or you would rather not post in public? Write to parseforge@protonmail.com and it reaches the people who wrote it.
What related scrapers can I use?
Run MSHA Mine Data Scraper - Violations, Inspections, Accidents on Apify All scrapers
