Skip to content

FMCSA QCMobile + SAFER + L&I Integration

OpenInsure integrates with three upstream FMCSA surfaces — the QCMobile JSON API, the public SAFER HTML snapshot, and the L&I out-of-service orders page — to build a complete carrier profile for underwriting and monitoring. This page documents the endpoints, authentication, response field mappings, and how the client handles rate limits, SAFER/L&I scrape failures, and retries.

SourceFormatAuthFields Unique to This Source
QCMobile APIJSONwebKeyLegal/DBA name, authority, BIPD insurance, BASICs, OOS rates, crashes, safety rating, power units, drivers
SAFER snapshotHTML scrapeNonePhone, DUNS, mailing address, HM shipper/carrier flags, MCS-150 form date, MCS-150 mileage
L&I OOS ordersHTML scrapeNoneActive OCO (Out-of-service Carrier Order) / OSO (Out-of-service Shipper Order) decisions — hard decline signal for UW

The server fans out all three in parallel on every DOT lookup, so the interactive UW panel and the server-side submission enrichment always see the same merged record. QCMobile is the source of truth where it has data; SAFER and L&I fill the gaps that QCMobile doesn’t cover at all.

The QCMobile API uses a webKey query parameter for authentication. Keys are issued at no cost through the FMCSA portal at mobile.fmcsa.dot.gov.

Terminal window
GET https://mobile.fmcsa.dot.gov/qc/services/carriers/1234567?webKey=<your_key>

The webKey is stored in Cloudflare Workers secrets as FMCSA_WEB_KEY and injected into every request by the API worker. It is never logged or exposed in error messages.

GET /carriers/:dot — Carrier Authority & Insurance

Section titled “GET /carriers/:dot — Carrier Authority & Insurance”

Retrieves the carrier’s operating authority, insurance on file, and contact information.

https://mobile.fmcsa.dot.gov/qc/services/carriers/:dotNumber?webKey=<key>

Key response fields:

FMCSA FieldInternal MappingDescription
dotNumberdotNumberUSDOT number
legalNamelegalNameLegal entity name
dbaNamedbaNameDBA name, if any
carrierOperationoperationTypeA (interstate), B (intrastate hazmat), C (intrastate non-hazmat)
authorityStatusauthorityStatusA (active), I (inactive), N (not authorized)
bipdInsuranceOnFileinsuranceOnFileY / N — BI/PD insurance currently on file
bipdInsuranceRequiredinsuranceRequiredRequired insurance amount (dollars)
phyCity, phyStateaddress.city/statePhysical location
totalDriverstotalDriversNumber of drivers on file
totalPowerUnitstotalPowerUnitsNumber of power units on file

GET /carriers/:dot/basics — CSA BASIC Scores

Section titled “GET /carriers/:dot/basics — CSA BASIC Scores”

Retrieves the carrier’s Compliance, Safety, Accountability (CSA) BASIC percentile scores and investigation history.

https://mobile.fmcsa.dot.gov/qc/services/carriers/:dotNumber/basics?webKey=<key>

Seven BASIC categories:

BASIC NameInternal KeyIntervention Threshold
Unsafe DrivingUNSAFE_DRIVING65th percentile
Hours-of-ServiceHOS_COMPLIANCE65th percentile
Driver FitnessDRIVER_FITNESS80th percentile
Controlled Substances/AlcoholCONTROLLED_SUBSTANCES80th percentile
Vehicle MaintenanceVEHICLE_MAINT80th percentile
Hazardous MaterialsHAZMAT60th percentile
Crash IndicatorCRASH_INDICATOR65th percentile

The fetchBasics() function (in apps/api/src/lib/fmcsa.ts) maps the raw FMCSA basics array — which is keyed by numeric BASIC category codes — to the named keys above, and attaches whether each score has crossed the intervention threshold.

// Simplified output of fetchBasics()
{
basics: [
{
basic: 'HOS_COMPLIANCE',
percentile: 78,
interventionThreshold: 65,
aboveThreshold: true,
measureValue: '4.12',
inspections: 24,
violations: 9
},
// ...
],
investigations: [
{
type: 'COMPLIANCE_REVIEW',
date: '2024-11-15',
rating: 'CONDITIONAL'
}
]
}

GET /carriers/:dot/oos — Out-of-Service Rates

Section titled “GET /carriers/:dot/oos — Out-of-Service Rates”

Retrieves the carrier’s out-of-service rates for vehicles, drivers, and hazmat, compared to national averages.

https://mobile.fmcsa.dot.gov/qc/services/carriers/:dotNumber/oos?webKey=<key>

The fetchOosRates() function extracts and normalizes the three OOS rate categories:

// Simplified output of fetchOosRates()
{
vehicle: {
inspections: 48,
outOfService: 9,
rate: 18.75, // percentage
nationalAverage: 21.4
},
driver: {
inspections: 48,
outOfService: 3,
rate: 6.25,
nationalAverage: 5.5
},
hazmat: {
inspections: 12,
outOfService: 0,
rate: 0.0,
nationalAverage: 4.2
}
}

fetchSaferSnapshot(dotNumber) in apps/api/src/lib/fmcsa.ts scrapes the public SAFER page at https://safer.fmcsa.dot.gov/query.asp and extracts the fields that QCMobile does not expose:

// Simplified shape returned by fetchSaferSnapshot()
{
phone: '(617) 555-0123',
dunsNumber: '012345678',
mailingAddress: '100 Main St\nBoston, MA 02108',
hmShipper: false,
hmCarrier: true,
mcs150FormDate: '2025-06-14',
mcs150Mileage: 4_250_000
}

The scraper returns null on any parse failure; the caller treats failures as non-fatal and continues with the QCMobile-only record. The multi-line mailingAddress is split into mailingStreet / mailingCity / mailingState / mailingZipcode before being merged into the final carrier record.

fetchOosOrders(dotNumber) scrapes the FMCSA Licensing & Insurance (L&I) “Out-of-service Orders” view and returns any active OCO or OSO orders. An active order is a hard UW decline signal — FMCSAPanel renders a red banner and the underwriting rules engine sets auto_decline_oos_order.

// Return shape
{
orders: [
{
orderType: 'OCO' | 'OSO',
orderDate: '2026-02-11',
reason: 'Unsatisfactory safety rating',
status: 'active',
},
];
}

Three response states distinguish themselves by convention:

ResponseMeaning
{ orders: [] }Checked successfully; carrier has no orders
{ orders: [ { ... } ] }Carrier has one or more active orders
null / field omittedL&I fetch failed — unknown OOS state

The UW /v1/motor-carriers/dot/:dot response includes oosOrders when the fetch succeeded and omits the field entirely otherwise, so consumers can distinguish “no orders” from “no data”.

The FMCSA client lives in apps/api/src/lib/fmcsa.ts and exports:

lookupDotNumber(dotNumber, webKey, insuredName?) // QCMobile carrier + authority
fetchBasics(dotNumber, webKey) // QCMobile BASICs
fetchOosRates(dotNumber, webKey) // QCMobile OOS rates
fetchInsuranceFilings(dotNumber, webKey) // QCMobile BIPD + cargo filings
fetchSaferSnapshot(dotNumber) // SAFER HTML
fetchOosOrders(dotNumber) // L&I OOS orders

All QCMobile functions follow the same pattern:

  1. Build the QCMobile URL with the DOT number and webKey.
  2. Fetch with a 10-second timeout.
  3. On HTTP 404, return null (carrier not found in FMCSA — may be a new or deactivated carrier).
  4. On HTTP 429 or 503, throw FmcsaRateLimitError (see Retry Strategy below).
  5. On any other non-2xx status, throw FmcsaFetchError with the status code and URL.
  6. Parse and validate the response with Zod before returning.

The QCMobile API does not publish a formal rate limit, but sustained traffic above approximately 5 requests per second triggers HTTP 429 responses. The cron pipeline enforces a 200ms delay between requests — meaning a portfolio of 150 carriers takes roughly 90 seconds to fully scan (150 carriers × 3 endpoints × 200ms).

The retry strategy for transient errors:

ErrorAction
HTTP 429Exponential backoff: 1s → 2s → 4s, then skip carrier and log warning
HTTP 503Same as 429
Network timeoutRetry once immediately, then skip carrier
HTTP 404No retry — carrier not in FMCSA, mark as not_found
HTTP 400No retry — invalid DOT number, log error

Carriers that could not be fetched are excluded from the alert diff for that run. They are not marked as having no alerts — they are simply not evaluated. The staleCarriers field in the /v1/fmcsa-history/stats response surfaces carriers with gaps in their snapshot history.

Two API endpoints fuse QCMobile, SAFER, and L&I into a single response:

Interactive DOT lookup from the UW workbench Insured Info tab. Fans out QCMobile + SAFER + L&I OOS in parallel and returns the merged carrier record. Also triggers the OCO/OSO scraper so any later carrier-alert checks see fresh data.

Response includes carrierName, dbaName, phyStreet/City/State/Zipcode, mailingStreet/City/State/Zipcode, telephone, operatingAuthority, csaScore, issScore, totalPowerUnits, totalDrivers, crash/inspection/OOS counts, safetyRating, mcs150FormDate, mcs150Mileage, HM flags, and oosOrders[] when available. See FmcsaCarrierRecord in the OpenAPI spec for the full field list.

Server-side enrichment for a submission. In addition to the parallel fan-out above, this endpoint also fetches basics, oos-rates, insurance-filings, cargo-carried, operation-classification, and docket-numbers from QCMobile, merges everything into the submission’s extractedData.fmcsaEnrichment block, and writes an append-only row per endpoint to fmcsa_snapshots for trend analysis.

Returns { submission, fmcsa, safer, qcmobile }:

{
"submission": { "id": "sub_01J8...", "insuredName": "ACME Trucking LLC" /* ... */ },
"fmcsa": {
/* merged carrier record — same shape as motor-carriers/dot response */
},
"safer": { "phone": "...", "dunsNumber": "...", "mailingAddress": "...", "hmCarrier": true },
"qcmobile": {
"basics": {
/* ... */
},
"oos": {
/* ... */
},
"filings": [
/* ... */
],
"cargo": {
/* ... */
},
"operation": {
/* ... */
},
"docket": [
/* ... */
],
},
}

Sole-prop detection runs in this path: if the SAFER-sourced legal name matches an individual (e.g., “JOHN D SMITH”) the enrichment merges the name into the insured’s name fields (not the company field) and populates phone in one setInsured batch.