Advanced20 min

Build an Insurance Underwriting Risk Check

To assess a motor carrier's insurability you need four things: active operating authority, insurance on file, safety performance across FMCSA's seven BASICs, and any out-of-service or enforcement history. One /v2/profile call returns all of it — this guide turns that call into a tiered underwriting determination in about 60 lines of Python.

What underwriters evaluate, in order of severity

  1. Disqualifiers— inactive USDOT registration, an active out-of-service order, or (for interstate for-hire carriers) missing authority or deficient FMCSA insurance filings. Any one of these usually ends the evaluation. Intrastate carriers are state-regulated — don't hold them to FMCSA filing requirements they don't owe.
  2. Primary risk signals — BASIC alerts (especially Unsafe Driving, Hours-of-Service, and Crash Indicator, which FMCSA weights as the strongest crash predictors), the ISS inspection recommendation, and a Conditional or Unsatisfactory safety rating.
  3. Secondary signals new-entrant status, insurance lapse history, fleet size relative to violation counts, and MCS-150 freshness.
  4. Fraud context chameleon-carrier signals: authorities sharing addresses, phones, or equipment with other (possibly revoked) carriers.

Public sources make you assemble this from four systems (SAFER, SMS, L&I, and the Socrata historical datasets) — and two of the seven BASICs, Crash Indicator and Hazmat Compliance, aren't publicly displayed at all. CarrierOk computes those two in-house from the underlying public inspection and crash data, so the API returns all seven. See the SAFER glossary entry for how the public systems fit together.

1

Get the carrier profile

One call, by DOT number (or MC number, EIN, phone, email, VIN, plate — see the Profiles reference). sk_test_ keys hit the sandbox, sk_live_ keys hit production:

curl "https://api.carrierok.com/v2/profile?dot_number=1234567" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response200 OK
{
  "items": [
    {
      "dot_number": "1234567",
      "legal_name": "EXAMPLE TRANSPORT LLC",
      "entity_type_desc": "Carrier",
      "usdot_status": "Active",
      "carrier_operation_desc": "interstate",
      "operation_class_authorized_for_hire": true,
      "indicator_authority": true,
      "authority_common": "Active",
      "out_of_service_flag": false,
      "indicator_insurance": true,
      "insurance_bipd_on_file": "1000000",
      "insurance_bipd_required": "750000",
      "total_power_units": "42",
      "safety_rating_desc": "Satisfactory",
      "risk_score": "Low",
      "risk_score_probability": 0.08,
      "iss_value": "34",
      "iss_recommendation": "OPTIONAL",
      "basic_alert_unsafe_driving": false,
      "basic_alert_hours_of_service": true,
      "basic_alert_vehicle_maintence": false,
      "basic_measure_unsafe_driving": "0.42",
      "basic_percentile_crash_indicator": 0.61,
      "...": "320+ additional fields — see the data dictionary"
    }
  ],
  "total_count": 1
}

Three response conventions to know

The profile is a flat object of 320+ fields inside items[0]. Most numeric counters arrive as strings ("total_power_units": "42"). Fields with no value for a carrier are omitted from the response rather than returned as null — always read with .get(). The full field reference is the data dictionary and the machine-readable OpenAPI spec, which types all of them.
2

Define the risk determination

An illustrative framework — not advice

This guide is a technical integration example showing one defensible starting framework. It is not underwriting, legal, or compliance advice; risk appetite, thresholds, and any adverse-action handling must be set by your own underwriting and compliance teams for your book and jurisdiction. Every input is in the response if you weight things differently. API usage is governed by the API Terms.

Decline / refer immediately if any of:

ConditionCheck
USDOT registration not activeusdot_status != "Active"
Active out-of-service orderout_of_service_flag == true
No active operating authorityindicator_authority is not true
Authority explicitly inactive / revokedindicator_authority == false
Insurance below FMCSA requirementindicator_insurance == false

Interstate vs. intrastate — don't decline carriers for filings they don't owe

MC operating authority and FMCSA insurance filings are interstate for-hire requirements. Intrastate carriers are regulated by their state and routinely have no authority or FMCSA filing at all — for them the registration check is an active USDOT (usdot_status). The API encodes this: indicator_authority and indicator_insurance are tri-state(true / false / null), where null means "not applicable / nothing required." Treat only explicit false as a failure, and gate the authority-required check on carrier_operation_desc == "interstate" plus operation_class_authorized_for_hire.

Flag as high-risk if any of:

ConditionCheck
Composite risk score is Highrisk_score == "High"
ISS says inspectiss_recommendation == "INSPECT"
Conditional / Unsatisfactory ratingsafety_rating_desc in (...)
Alert in a crash-predictive BASICbasic_alert_unsafe_driving | basic_alert_hours_of_service | basic_alert_crash_indicator
Two or more BASIC alerts of any kindcount(basic_alert_*) >= 2

Field names and scales — read before you code

The BASIC field suffixes are the API's real spellings: hours_of_service (not hos_compliance), controlled_substance (singular), hazardous_materials, and vehicle_maintence (yes, that spelling). Percentiles (basic_percentile_*) are on a 0–1 scale— multiply by 100 to compare with the percentages shown on FMCSA's SMS site; the matching intervention thresholds are in intervention_threshold_*(e.g. 0.65 for Unsafe Driving). And don't escalate on indicator_carrier_safety == false: it's a composite pass-screen that is false for ~92% of carriers, mostly because they simply have too few inspections.

Route to enhanced review (underwriter attention) if any of:

ConditionCheck
Composite risk score is Mediumrisk_score == "Medium"
Any single BASIC alertcount(basic_alert_*) == 1
New entrantdot_age < 540

The unremarkable carrier — no adverse signals, risk_scorenot computed (it's null for intrastate, private, and non-carrier entities) — lands in standard review. Low risk requires positive evidence: risk_score == "Low" with zero alerts and clean registration — absence of data is not a clean bill of health. Chameleon signals (shared addresses, phones, equipment — the risk_factors array and network_graph_* fields) are best treated as context for manual review rather than automatic flags: large legitimate fleets share identifiers too.

3

The complete script

Save as risk_check.py:

risk_check.py
"""Carrier underwriting risk check — CarrierOk API.

Illustrative starting framework — not underwriting, legal, or compliance
advice. Validate thresholds against your own book and model governance.

Usage:
    export CARRIEROK_API_KEY=sk_test_your_key_here
    python risk_check.py 1234567

Field-name notes (these are the API's real names — don't "fix" them):
  - BASIC suffixes: unsafe_driving, hours_of_service, driver_fitness,
    controlled_substance, vehicle_maintence, hazardous_materials,
    crash_indicator
  - iss_recommendation values are UPPERCASE: INSPECT / OPTIONAL / PASS
  - indicator_authority / indicator_insurance are TRI-STATE
    (true / false / null): null means "not applicable / nothing required"
    — normal for intrastate and private carriers — and must not be
    treated as a failure
  - Numeric counters arrive as strings; fields with no value for a carrier
    are omitted from the response entirely (not returned as null)
"""
import os
import sys
import requests

API_KEY = os.environ["CARRIEROK_API_KEY"]  # sk_test_... or sk_live_...
BASE = "https://api.carrierok.com/v2"

# FMCSA weights these three as the strongest crash predictors and applies
# lower intervention thresholds to them.
CRASH_PREDICTIVE = ("unsafe_driving", "hours_of_service", "crash_indicator")
ALL_BASICS = CRASH_PREDICTIVE + (
    "driver_fitness", "controlled_substance",
    "vehicle_maintence", "hazardous_materials",
)


def get_profile(dot_number: str) -> dict:
    resp = requests.get(
        f"{BASE}/profile",
        params={"dot_number": dot_number},
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=15,
    )
    if resp.status_code == 429:
        raise RuntimeError(
            f"Rate limited — retry after {resp.headers.get('Retry-After')}s")
    resp.raise_for_status()
    items = resp.json().get("items", [])
    if not items:
        raise LookupError(f"No carrier found for DOT {dot_number}")
    return items[0]


def assess(p: dict) -> tuple[str, list[str]]:
    reasons = []

    # MC authority and FMCSA insurance filings are interstate for-hire
    # requirements. Intrastate carriers are regulated by their state —
    # for them the key registration check is an active USDOT.
    interstate_for_hire = (
        p.get("carrier_operation_desc") == "interstate"
        and p.get("operation_class_authorized_for_hire") is True
    )

    # ── Tier 1: disqualifiers ──────────────────────────────────────────
    if p.get("usdot_status") != "Active":
        reasons.append("USDOT registration not active "
                       f"(usdot_status={p.get('usdot_status')})")
    if p.get("out_of_service_flag"):
        reasons.append("Active out-of-service order")
    if interstate_for_hire and p.get("indicator_authority") is not True:
        reasons.append("Interstate for-hire without active operating "
                       f"authority (common={p.get('authority_common')}, "
                       f"contract={p.get('authority_contract')})")
    elif p.get("indicator_authority") is False:
        reasons.append("Operating authority explicitly inactive or revoked")
    if p.get("indicator_insurance") is False:
        # Only ever false where FMCSA requires a filing (null otherwise),
        # so this check self-gates for intrastate/private carriers.
        reasons.append("Insurance on file below FMCSA requirement "
                       f"(BIPD on file: {p.get('insurance_bipd_on_file', '0')}, "
                       f"required: {p.get('insurance_bipd_required', '?')})")
    if reasons:
        return "DECLINE / REFER", reasons

    # ── Tier 2: high-risk signals ──────────────────────────────────────
    if p.get("risk_score") == "High":
        reasons.append(f"CarrierOk composite risk score: High "
                       f"(p={p.get('risk_score_probability')})")
    if p.get("iss_recommendation") == "INSPECT":
        reasons.append(f"ISS recommendation: INSPECT "
                       f"(ISS {p.get('iss_value')})")
    if p.get("safety_rating_desc") in ("Conditional", "Unsatisfactory"):
        reasons.append(f"FMCSA safety rating: {p['safety_rating_desc']}")

    crash_alerts = [b for b in CRASH_PREDICTIVE
                    if p.get(f"basic_alert_{b}")]
    all_alerts = [b for b in ALL_BASICS if p.get(f"basic_alert_{b}")]
    if crash_alerts:
        reasons.append("Alert in crash-predictive BASIC(s): "
                       + ", ".join(crash_alerts))
    if len(all_alerts) >= 2:
        reasons.append(f"{len(all_alerts)} BASIC alerts: "
                       + ", ".join(all_alerts))
    if reasons:
        return "HIGH RISK", reasons

    # ── Tier 3: enhanced review (above standard, below high-risk) ─────
    if p.get("risk_score") == "Medium":
        reasons.append("CarrierOk composite risk score: Medium")
    if len(all_alerts) == 1:
        reasons.append(f"Single BASIC alert: {all_alerts[0]}")
    if int(p.get("dot_age") or "0") < 540:
        reasons.append("New entrant (DOT registered < 540 days)")
    if reasons:
        return "ENHANCED REVIEW", reasons

    # ── Tier 4: LOW RISK requires positive evidence; the unremarkable
    #    carrier (risk_score null/absent, no signals) is STANDARD REVIEW.
    if p.get("risk_score") == "Low":
        return "LOW RISK", ["Composite risk score: Low",
                            "No BASIC alerts, registration and filings clear"]
    return "STANDARD REVIEW", [
        f"Risk score: {p.get('risk_score', 'not computed')}",
        "No adverse signals — apply your standard workflow"]


if __name__ == "__main__":
    profile = get_profile(sys.argv[1])
    verdict, reasons = assess(profile)
    print(f"{profile.get('legal_name')} (DOT {profile.get('dot_number')})")
    print(f"Verdict: {verdict}")
    for r in reasons:
        print(f"  - {r}")

Run it:

$ python risk_check.py 1234567
EXAMPLE TRANSPORT LLC (DOT 1234567)
Verdict: HIGH RISK
  - Alert in crash-predictive BASIC(s): hours_of_service

Batch screening

Screening a book? The profiles endpoint answers one carrier per call — loop with a small delay and honor Retry-After on 429s (see rate limits). For full-book backfills and bulk files, contact support@carrierok.com.
4

Keep the assessment current

A point-in-time check goes stale the day an insurance filing lapses or an authority is revoked. Underwriting teams typically run the risk check at quote and at bind, then move the policy onto the Monitoring API for the policy term — it watches your carrier list and surfaces authority, insurance, safety-rating, and BASIC-alert changes as they land. The monitoring guide builds that pipeline end to end.

FAQ

How is this different from FMCSA's free QCMobile API or SAFER?

QCMobile and SAFER return registration and basic safety data, but you'd still need SMS for BASIC measures, L&I for insurance filings, and the Socrata datasets for history — four integrations, four data models. CarrierOk reconciles all of them (plus MOTUS and 90+ other sources) into one profile updated four times daily.

Do you include the Crash Indicator and Hazmat Compliance BASICs?

Yes. FMCSA doesn't display these two percentiles publicly, but the underlying inspection and crash data is public. CarrierOk computes both in-house, so the API returns all seven BASICs.

How fresh is the data?

Core datasets update four times daily. Insurance filings, authority changes, and out-of-service orders typically appear within hours of FMCSA posting them.

Does this risk check work for intrastate carriers?

Yes, with the gating shown in Step 2. Intrastate carriers are state-regulated and typically have no MC authority or FMCSA insurance filing — the API returns null (not false) for indicator_authority and indicator_insurance in that case. The registration check that applies to every carrier is an active USDOT (usdot_status), plus the out-of-service flag. CarrierOk's composite risk_score is also null for intrastate carriers.

What is risk_score?

A composite computed by CarrierOk across 50+ factors — BASIC performance, inspection history, authority and insurance history, fleet characteristics, and fraud signals — returned as Low / Medium / High with an accompanying probability. It's designed as a triage signal, not a replacement for your underwriting model; the underlying fields are all in the response if you'd rather score it yourself.