# FMCSA Carrier Safety - Inspections, Violations & Authority (`j0401/fmcsa-carrier-safety`) Actor

US truck & bus carrier records (FMCSA open data, 4.5M carriers): registry details, inspection history, violations, SMS BASIC safety categories, operating authority, out-of-service orders and revocations - all keyed to the DOT number. Filter by DOT/MC, state or fleet size.

- **URL**: https://apify.com/j0401/fmcsa-carrier-safety.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.10 / 1,000 fmcsa carrier records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## FMCSA Carrier Safety - Inspections, Violations, Authority & Out-of-Service

The US Federal Motor Carrier Safety Administration publishes the country's **entire motor-carrier record** - every registered truck and bus company, every roadside inspection, every violation those inspections wrote - as open data. This actor turns that record into a **charged-per-record lookup, filter and aggregate tool** keyed to the one identifier the whole system turns on: the **DOT number**.

**Built for:** insurers and underwriters pricing carrier risk, brokers and shippers vetting a carrier before they tender a load, fleet and compliance teams benchmarking their own safety record, litigation and claims researchers, and anyone doing diligence on a trucking company.

### What it covers

Seven interlocking datasets, **37 million rows of public record**, all tied to the same carrier identity:

| Dataset | Records | What it is |
|---|---|---|
| **carrier** | **4,501,050** | the company census: name, address, fleet, operation, commodities, safety rating |
| **inspections** | **8,304,136** | every roadside inspection, **~8,300 new per day** |
| **violations** | **13,558,886** | the violations cited in those inspections, with the regulation section |
| **smsViolations** | **6,869,475** | the same violations carrying their SMS **BASIC** safety category and severity weights |
| **authority** | **1,860,604** | operating authority (common / contract / broker), insurance, bonds |
| **oos** | **399,280** | out-of-service orders served against carriers |
| **revocations** | **1,529,083** | authority revocations and their effective dates |

Each dataset carries the depth the source actually publishes:

- **carrier** - legal and DBA name, interstate vs intrastate status, the for-hire vs private distinction, the MC docket number and its status, the MCS-150 mileage filing, physical and mailing address, phone and email, power / truck / bus units, driver counts, the hazmat flag, the **30 commodity flags** folded into one readable `cargoTypes` list, and the **FMCSA safety rating** (Satisfactory / Conditional / Unsatisfactory) with its review date.
- **inspections** - date and start/end time, the inspecting state, the **inspection level** (full, walk-around, driver-only, vehicle-only), the location text, the carrier's name and address **as captured on the day**, and the violation and out-of-service counts split out by driver, vehicle and hazmat.
- **violations** - the regulatory part and section, the violation code and its full description, the unit it was written against, the **out-of-service indicator**, and any defect-verification or citation number.
- **smsViolations** - the BASIC category (Vehicle Maintenance / Unsafe Driving / Hours-of-Service / Driver Fitness / Hazardous Materials / Controlled Substances & Alcohol), the out-of-service indicator and the **severity and time weights** FMCSA uses to score carriers.
- **authority** - which authority the carrier holds and whether it is **active, inactive or none**, pending applications and revocations, the property / passenger / household-goods endorsements, and the **minimum insurance coverage amount** with any bond or cargo requirements.
- **oos** - the order date, the **reason** (imminent hazard, failure to pay, new-entrant audit failures, unfit) and the rescission date once lifted.
- **revocations** - the license type, the order service date, **voluntary vs involuntary**, and the effective date.

### The fine print that matters

The **DOT number is number-typed in some datasets and text in others**, and in the authority and revocation files it is **zero-padded to eight digits** - so the same carrier is `3568188` in one table and `"03568188"` in the next. This actor normalizes both directions, so you query a carrier once, by its real DOT number, and get its records from every dataset.

The **DOT number and the MC (docket) number are different namespaces that can collide numerically.** A DOT of 1215404 and an MC of 1215404 are unrelated carriers. The two are kept as separate inputs and never conflated.

**Null columns vanish from this source entirely**, so a naive reader sees a different set of keys on every row. Every record here carries the **full union of keys with `''` for absent values** - 157 keys, identical on every row of every dataset.

Dates arrive **four different ways across these tables** - `YYYYMMDD`, `YYYYMMDD HHMM`, `MM/DD/YYYY` and `DD-MON-YY` - and are normalized to `YYYY-MM-DD` on output. In the revocation file the source stores order dates as MM/DD/YYYY **text**, which sorts by month rather than by year, so that dataset filters by **year** rather than offering a date range that would silently return the wrong rows.

The **safety rating is populated on only about 5% of the census** - those FMCSA has actually rated. `hasSafetyRating` narrows to exactly those ~236,000 carriers instead of implying the whole register is rated.

The **violations table joins on the DOT number through the inspections it belongs to** (it carries no DOT column of its own), and that join names the carrier's inspection ids in the query, so it covers up to **1,000 inspections per carrier**. A carrier with a longer history returns a clear error telling you to narrow by date or query one inspection by id - never a silently truncated violation history.

Dates are **exact in every dataset**: the SMS feed's dates are `DD-MON-YY` text that does not sort chronologically (its own `ORDER BY` returns April 2025 before August 2024), so a date window there is matched as exact day values rather than a range comparison that would quietly return the wrong months.

The authority dataset's insurance amount and the SMS **BASIC category containing an ampersand** are both published in forms a plain text match gets wrong: the coverage amount is a **zero-padded string** (`"00750"` is 750 thousand dollars, and `"00000"` means none on file), and the Controlled Substances BASIC is stored **HTML-escaped**, so an exact match on the readable name returns nothing - that category is matched on its readable prefix instead. The coverage amount is passed through exactly as the agency publishes it, zero padding and all.

### Typical questions

- "Every **inspection and violation** for DOT 1215404 - with the out-of-service findings."
- "Which carriers in **Texas** run **50+ power units**?"
- "**Hazmat** carriers, and who reports hauling **passengers** or **refrigerated food**."
- "Carriers with a **Conditional or Unsatisfactory** safety rating."
- "**Out-of-service orders still in force** for a carrier."
- "Who has had their **operating authority revoked** - voluntary or involuntary."
- "Aggregate inspections **by state** or **by level**; violations **by regulation part**."
- "The **BASIC breakdown** for a carrier - is its problem Unsafe Driving or Vehicle Maintenance?"

### Inputs

| Input | What it does |
|---|---|
| `corpus` | which dataset - `carrier` (default) / `inspections` / `violations` / `smsViolations` / `authority` / `oos` / `revocations` |
| `mode` | `rows` (default) / `aggregate` |
| `dotNumber` / `mcNumber` | the carrier (DOT is the join key across every dataset) |
| `name` / `state` / `city` / `zip` | who and where |
| `statusCode` / `carrierOperation` / `classification` | interstate/intrastate, for-hire/private, business class |
| `cargo` / `hazmatOnly` | what they haul |
| `safetyRating` / `hasSafetyRating` / `reviewed` | the FMCSA rating and review record |
| `minPowerUnits` / `minTruckUnits` / `minBusUnits` / `minDrivers` | fleet-size ranges |
| `level` / `interstate` / `oosOnly` / `hazmatOosOnly` / `reportNumber` | inspection detail |
| `basic` / `violCode` / `partNo` / `section` | the violation and its BASIC category |
| `authorityType` / `authorityStatus` / `activeOnly` / `hasBond` / `hasCargo` | operating authority |
| `reason` / `status` / `year` | out-of-service orders and revocations |
| `dateFrom` / `dateTo` | date window (YYYY-MM-DD) |
| `groupBy` | aggregate over any dimension of the chosen dataset |
| `maxResults` | cap records (default 200, up to 20000) |

**Default run = the 200 carriers with the lowest DOT numbers** - fast for the daily auto-test. For a targeted query add a filter or a DOT number; for a broad view use `aggregate`.

### Low cost

**From $0.0001 per record** - billed only for the rows you use, at the platform floor. Cost scales with what you pull, not with the size of the register, and because each record is metered individually there's no per-run charge cap to hit on a big pull.

### Why this is hard to do properly

The FMCSA record is not one dataset - it is **seven tables whose only shared key is typed differently in each**, and the columns that make a carrier's file meaningful are not in one place. Reconstructing one carrier's history means knowing that **the DOT number is an integer in the census and inspection files but an eight-digit zero-padded string in the authority and revocation files**, that the violations table **has no DOT column at all** and can only be reached through the inspections it belongs to, and that the SMS safety categories live in a fifth file that refreshes on a different cycle than the rest. Layer on **four incompatible date encodings**, columns that **disappear entirely when null** (so a thin reader sees a shifting schema row to row), an **HTML-escaped BASIC category** and a **zero-padded insurance amount** that both defeat exact matching, and a **plain-text `LIKE` that is case-sensitive** - and "look up this carrier" becomes a normalization problem before it is a data problem. Every pull here is a server-side push-down - the million-row tables are filtered, sorted and aggregated at the source, never scanned client-side. (The one exception is the violations-by-DOT join, which has to name the carrier's inspection ids, so it resolves those first and caps the carrier at 1,000 inspections.) A broad pull is integrity-checked against the corpus's known shape (row-count anchor and key-field coverage), so a degraded source fails loudly instead of returning an empty or wrong result.

### Source

- [US DOT / FMCSA open data (data.transportation.gov)](https://data.transportation.gov/) - the FMCSA carrier census, inspection, violation, authority and enforcement files, live. Public open data, no login and no API key. Records are the agency's own published fields; not an endorsement of any carrier.

# Actor input Schema

## `corpus` (type: `string`):

carrier = the company census register (4.5M: name, address, fleet, operation, commodities, safety rating). inspections = roadside inspection history (8.3M). violations = the violations cited in each inspection (13.6M). smsViolations = the same violations with their SMS BASIC safety category and severity weights (6.9M). authority = operating authority + insurance (1.9M). oos = out-of-service orders (399k). revocations = authority revocations (1.5M).

## `mode` (type: `string`):

rows = records matching your filters (default). aggregate = one count row per group (see groupBy).

## `groupBy` (type: `string`):

Dimension to aggregate over (only used when mode=aggregate). Blank = the corpus's primary dimension. Must belong to the selected corpus: carrier -> statusCode / carrierOperation / safetyRating / state / classification / businessOrgDesc / fleetSize / hazmat / reviewType / country. inspections -> state / level / interstate / hazmatPlacardReq / region. violations -> partNo / outOfService / violCode. smsViolations -> basic / section / group / outOfService. authority -> commonStat / contractStat / brokerStat / mxType. oos -> status / reason. revocations -> typeLicense / orderType.

## `dotNumber` (type: `string`):

USDOT number (the carrier's federal ID), e.g. '1215404'. This is the key that ties every dataset to the same carrier. NOT the same as the MC / docket number. Blank = any.

## `mcNumber` (type: `string`):

The carrier's MC (or FF / MX) docket number, e.g. '1215404'. Matched against the census docket columns with or without the MC prefix. This is a DIFFERENT namespace from the DOT number - they can share digits and still be different carriers. Blank = any.

## `name` (type: `string`):

Legal or DBA name, substring match, e.g. 'SWIFT'. Blank = any.

## `state` (type: `string`):

Two-letter state. For carrier / authority this is the physical or business address state; for inspections it is the state that conducted the inspection. Blank = any.

## `city` (type: `string`):

Physical city, substring match (carrier dataset only). Blank = any.

## `zip` (type: `string`):

Physical ZIP code, exact match (carrier dataset only). Blank = any.

## `statusCode` (type: `string`):

Carrier dataset: I = interstate, A = intrastate, P = pending. Blank = any.

## `carrierOperation` (type: `string`):

Carrier dataset: A = a for-hire motor carrier (the main commercial class, 2.47M), B = a private carrier with both passenger and property operations, C = private carrier (1.84M). Blank = any.

## `classification` (type: `string`):

Carrier dataset, substring match on the source's classification field: 'AUTHORIZED FOR HIRE', 'PRIVATE PROPERTY', 'PRIVATE PASSENGER, BUSINESS', 'EXEMPT FOR HIRE'. Values are semicolon-combined, so a substring match may match a combined value. Blank = any.

## `cargo` (type: `string`):

Carrier dataset: filter to carriers that report hauling this commodity (the census carries 30 commodity flags). Blank = any.

## `hazmatOnly` (type: `boolean`):

Carrier dataset: only carriers that report carrying hazardous materials (284k of 4.5M).

## `safetyRating` (type: `string`):

Carrier dataset: S = Satisfactory, C = Conditional, U = Unsatisfactory. Only about 5% of the register carries a rating (those FMCSA has rated). Blank = any.

## `hasSafetyRating` (type: `boolean`):

Carrier dataset: only carriers that have a recorded safety rating (~236k rows).

## `reviewed` (type: `boolean`):

Carrier dataset: only carriers with an FMCSA review on record (review type / date).

## `hasEmail` (type: `boolean`):

Carrier dataset: only carriers with an email address on file (~2.97M).

## `minPowerUnits` (type: `integer`):

Carrier dataset: only carriers with at least this many power units (trucks/tractors) - the numeric fleet-size filter, e.g. 50 for mid-size fleets and up.

## `minTruckUnits` (type: `integer`):

Carrier dataset: only carriers with at least this many truck units.

## `minBusUnits` (type: `integer`):

Carrier dataset: only carriers with at least this many bus units.

## `minDrivers` (type: `integer`):

Carrier dataset: only carriers with at least this many drivers.

## `level` (type: `string`):

Inspections dataset: the inspection level. 1 = full (driver + vehicle, most thorough), 2 = walk-around, 3 = driver-only, 4 = special study, 5 = vehicle-only, 6 = radio, 9 = terminal. Blank = any.

## `interstate` (type: `boolean`):

Inspections dataset: true = interstate inspections only, false or unchecked = any.

## `oosOnly` (type: `boolean`):

inspections / violations / smsViolations: only records with an out-of-service result (the serious findings that pull a driver or vehicle off the road).

## `hazmatOosOnly` (type: `boolean`):

Inspections dataset: only inspections with a hazmat out-of-service finding.

## `reportNumber` (type: `string`):

Inspections dataset: exact inspection report number. Blank = any.

## `inspectionId` (type: `string`):

Violations dataset: exact internal inspection id, when you already have it. Blank = any.

## `basic` (type: `string`):

smsViolations dataset: the FMCSA safety-measurement category the violation counts toward. Vehicle Maintenance alone is 5.1M of the 6.9M. Blank = any.

## `violCode` (type: `string`):

violations / smsViolations: the violation code or a prefix, e.g. '393.75' for tire violations or '392.4' for drug/alcohol. Blank = any.

## `partNo` (type: `string`):

Violations dataset: exact regulation part number - 393 = vehicle equipment, 392 = driving rules, 395 = hours of service, 391 = driver qualifications, 396 = inspection/repair, 390 = general. Blank = any.

## `section` (type: `string`):

smsViolations dataset: section description substring, e.g. 'Driving', 'Vehicle'. Blank = any.

## `docketNumber` (type: `string`):

authority / revocations: the operating authority docket number with or without its prefix, e.g. 'MC1215404' or '1215404'. Blank = any.

## `authorityType` (type: `string`):

authority dataset: common = general for-hire authority, contract = contract carrier, broker = brokerage authority. Picking a type on its own returns carriers that ACTIVELY hold it (combine with authorityStatus to see inactive ones). Blank = any.

## `authorityStatus` (type: `string`):

authority dataset (used with authorityType): A = active, I = inactive, N = none. Blank = any.

## `activeOnly` (type: `boolean`):

authority: only carriers with at least one active authority. oos: only out-of-service orders still in force (not rescinded).

## `hasBond` (type: `boolean`):

Authority dataset: only carriers whose authority carries a bond requirement,

## `hasCargo` (type: `boolean`):

Authority dataset: only carriers whose authority carries a cargo-insurance requirement.

## `reason` (type: `string`):

oos dataset: reason substring, e.g. 'Imminent Hazard', 'failure to pay', 'New Entrant'. Blank = any.

## `status` (type: `string`):

oos dataset: ACTIVE = the order is still in force, INACTIVE = it has been lifted. Blank = any.

## `typeLicense` (type: `string`):

revocations dataset: which authority the revocation applies to. Blank = any.

## `orderType` (type: `string`):

revocations dataset: revocation type substring - 'VOLUNTARY', 'INVOLUNTARY', 'ADMINISTRATIVE'. Blank = any.

## `year` (type: `string`):

Revocations dataset: the year the order was served, four digits (the source stores these dates as MM/DD/YYYY text, so a year is the exact date filter it supports). Blank = any.

## `dateFrom` (type: `string`):

Start of the date window, YYYY-MM-DD (inclusive). Each dataset filters its own date field: inspections = the inspection date; oos = the order date; smsViolations = the inspection date; violations = the record change date (when the violation was last updated, which trails the inspection). Blank = no lower bound.

## `dateTo` (type: `string`):

End of the date window, YYYY-MM-DD (exclusive). Blank = no upper bound.

## `maxResults` (type: `integer`):

How many records to deliver (or groups to return in aggregate mode). Default 50. Capped at 20000 per run; a broad query over a multi-million-row dataset is bounded here rather than paging the whole corpus.

## Actor input object example

```json
{
  "corpus": "carrier",
  "mode": "rows",
  "groupBy": "",
  "dotNumber": "",
  "mcNumber": "",
  "name": "",
  "state": "",
  "city": "",
  "zip": "",
  "statusCode": "",
  "carrierOperation": "",
  "classification": "",
  "cargo": "",
  "hazmatOnly": false,
  "safetyRating": "",
  "hasSafetyRating": false,
  "reviewed": false,
  "hasEmail": false,
  "minPowerUnits": 0,
  "minTruckUnits": 0,
  "minBusUnits": 0,
  "minDrivers": 0,
  "level": "",
  "interstate": false,
  "oosOnly": false,
  "hazmatOosOnly": false,
  "reportNumber": "",
  "inspectionId": "",
  "basic": "",
  "violCode": "",
  "partNo": "",
  "section": "",
  "docketNumber": "",
  "authorityType": "",
  "authorityStatus": "",
  "activeOnly": false,
  "hasBond": false,
  "hasCargo": false,
  "reason": "",
  "status": "",
  "typeLicense": "",
  "orderType": "",
  "year": "",
  "dateFrom": "",
  "dateTo": "",
  "maxResults": 50
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

FMCSA carrier, inspection, violation, authority, out-of-service or revocation records - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/fmcsa-carrier-safety").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/fmcsa-carrier-safety").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call j0401/fmcsa-carrier-safety --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,j0401/fmcsa-carrier-safety"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/iCb75wJbhNvt6g2Ib/builds/cnbyzcfKVkK6htKg6/openapi.json
