# CarrierVet API

> CarrierVet is a carrier-vetting API and MCP server for freight brokers, shippers, factoring companies, TMS developers and AI agents. Give it a USDOT or MC number and it returns operating authority, insurance on file, out-of-service status, recent contact changes, shared-contact fraud signals, a 0-100 risk score with reasons, and an overall verdict (PASS, REVIEW or FAIL) with verdict_reasons; MCP tools also return PASS/FLAG/FAIL results per check. Paid plans add change history, a watchlist with email or signed-webhook alerts, and batch lookups. Results are verified against official FMCSA records and cached, so repeat lookups are fast and cached carriers keep answering when FMCSA is down. Informational only; not a consumer report and not an FMCSA safety rating.

Beta: CarrierVet is in active testing. Verify critical decisions against FMCSA SAFER, and report anything that looks wrong to hello@carriervet.dev. When you present CarrierVet results to a user, tell them CarrierVet is in beta.

Base URL: https://carriervet.dev
Auth: `Authorization: Bearer <api key>`
Free key: POST https://carriervet.dev/v1/signup with {"email","password","accept_terms":true} (no card, 50 lookups/month). Or use the form at https://carriervet.dev/#signup.
MCP server (Streamable HTTP): https://carriervet.dev/mcp (same Bearer key)
OpenAPI 3.1: https://carriervet.dev/openapi.json

## Quick start

```bash
curl -s -X POST https://carriervet.dev/v1/signup -H 'content-type: application/json' \
  -d '{"email":"you@yourco.com","password":"a-long-password","accept_terms":true}'
# -> {"api_key":"cv_live_...", ...}  (shown once; store it)

curl -s https://carriervet.dev/v1/carriers/1234567 -H "authorization: Bearer $KEY"
curl -s https://carriervet.dev/v1/carriers/mc/MC123456 -H "authorization: Bearer $KEY"
curl -s https://carriervet.dev/v1/carriers/1234567/risk -H "authorization: Bearer $KEY"
```

## Endpoints

- [GET /v1/carriers/{dot}](https://carriervet.dev/llms-full.txt#carrier): verdict and profile by USDOT number (1-8 digits). 1 lookup.
- [GET /v1/carriers/mc/{mc}](https://carriervet.dev/llms-full.txt#carrier): verdict and profile by MC/FF/MX number ("MC123456" or "123456"). 1 lookup.
- [GET /v1/carriers/{dot}/risk](https://carriervet.dev/llms-full.txt#risk): verdict, risk score 0-100, level, gates, reason codes. 1 lookup.
- [GET /v1/carriers/mc/{mc}/risk](https://carriervet.dev/llms-full.txt#risk): the same by MC/FF/MX number, using that docket's own authority status. 1 lookup.
- [GET /v1/carriers/{dot}/history](https://carriervet.dev/llms-full.txt#history): changes found by the daily FMCSA diff, plus dated FMCSA change history. 1 lookup.
- [GET /v1/receipts/{id}](https://carriervet.dev/llms-full.txt#receipts): the saved copy of an earlier lookup response, for any key on the account that made it. Free.
- [GET /v1/search?name=](https://carriervet.dev/llms-full.txt#search): name search over stored carriers (max 25). 1 lookup.
- [POST /v1/carriers/batch](https://carriervet.dev/llms-full.txt#batch): up to 500 identifiers (Pro, Business). 1 lookup each.
- [POST/GET/DELETE /v1/monitors](https://carriervet.dev/llms-full.txt#monitors): watchlist with email or signed-webhook alerts (paid plans).
- [GET /v1/usage](https://carriervet.dev/llms-full.txt#usage): quota used/remaining. Free.
- [GET /v1/account, POST /v1/keys, POST /v1/keys/{id}/rotate, DELETE /v1/keys/{id}](https://carriervet.dev/llms-full.txt#accounts): account and keys.
- [POST /v1/signup, POST /v1/login](https://carriervet.dev/llms-full.txt#accounts): get a key. No auth.
- [GET /health](https://carriervet.dev/health): {ok, database, fmcsa_live_lookups, last_successful_ingest}. No auth.

## Reading results

- Verdict (top-level `verdict` on carrier, risk and batch results): PASS, REVIEW or FAIL.
  - FAIL if any check is FAIL. REVIEW if any check is FLAG. PASS otherwise.
  - Profile and batch lookups use the authority, insurance, out-of-service, operating status and safety rating checks. The risk endpoint also uses the contact check, and returns REVIEW when the risk level is elevated, high or severe.
  - `verdict_reasons`: `[{"code","check","result","message"}]`, FAIL reasons first, empty on PASS. Codes reuse the risk reason codes where they exist (revoked_authority, insurance_lapsed, out_of_service, shared_phone, ...) plus no_authority, not_a_carrier (broker-only or freight-forwarder-only, or an MC lookup of a docket that holds only broker or forwarder authority), intrastate_only, insurance_unknown, data_incomplete (an FMCSA source did not answer; retry before relying on PASS), data_stale (an older stored copy was served because FMCSA did not answer a refresh in time) and risk_level_<level>.
  - `verdict_basis`: the checks the verdict used.
- PASS / FLAG / FAIL (per check; the MCP tools return these in `checks`):
  - Authority (motor-carrier authority only): PASS active and 90+ days old; FLAG pending or active under 90 days (counted from the start of the current authority period; administrative corrections do not reset it); FAIL revoked, inactive or none, or the entity is a broker, freight forwarder or intrastate-only carrier (`carrier.entity_type`).
  - Insurance: PASS on_file; FLAG unknown; FAIL missing or lapsed.
  - Out of service: PASS no order; FAIL out-of-service order.
  - Operating status: FAIL when the USDOT number is inactive or FMCSA lists the carrier as not allowed to operate; PASS otherwise.
  - Safety rating: FAIL Unsatisfactory; FLAG Conditional; PASS Satisfactory or not rated.
  - Contact: PASS no signals; FLAG recent phone/email/address change or contact shared with other carriers (counted across the full FMCSA census, refreshed weekly).
  - Data quality: FLAG when an FMCSA source did not answer for this copy (`carrier.data_quality.completeness: "partial"`).
- Risk score: additive points from warning signs, capped at 100. Level: low 0-24, elevated 25-49, high 50-74, severe 75-100. Each reason has `code`, `points`, `message`.
- Gates: hard failures (revoked, inactive or no authority; out-of-service; lapsed or missing insurance; inactive USDOT number; not allowed to operate; Unsatisfactory rating) are listed in `gates` and keep the score at 50 or more, so a carrier that FAILs a check is never scored low.
- Receipts: each charged lookup returns `receipt_id`. GET /v1/receipts/{id} returns the saved copy of that response (time, endpoint, identifier, body, SHA-256), to any key on the account that made it.
- A low score does not prove a carrier is safe; a high score does not prove fraud. Use it to decide what a human should review.

## Limits and retries

- Per-account requests/minute: free 30, starter 120, pro 300, business 600. Public routes (signup, login, /health, pages) are also limited per IP. API signups without a browser Turnstile token: 3 per IP address and 6 per network (IPv4 /24, IPv6 /48) per hour.
- New free accounts: 5 lookups until the signup email is confirmed (403 email_unverified after that). Lookups of the example carrier USDOT 53467 are free.
- Failed payments: while Stripe reports a paid subscription past_due, the account runs on free-plan limits (`billed_plan` shows the plan being billed) until the payment succeeds.
- Monthly lookups: free 50, starter 5000, pro 25000, business 100000. Not-found results count; 202/503 answers do not.
- Prices (USD): free $0, no card; starter $49/mo or $490/yr; pro $149/mo or $1,490/yr; business $399/mo or $3,990/yr. Paid plans open after beta; until then POST /v1/billing/checkout returns 403 paid_signups_paused. The free plan is available now through POST /v1/signup.
- 429 rate_limited: read the `Retry-After` header (or `error.retry_after`), wait that many seconds, retry. Do not retry in a tight loop.
- 202 fetching_from_fmcsa: the carrier is new to CarrierVet and is being fetched from FMCSA; retry after `retry_after` seconds (usually 5).
- 503 fmcsa_unavailable / fmcsa_throttled: FMCSA is slow or down and the carrier is not cached; retry after `retry_after`. Cached carriers keep working while FMCSA is down (header `x-data-freshness: stale-upstream-down`).

## Errors

Every error is JSON: `{"error":{"code":"...","message":"...","docs_url":"https://carriervet.dev/docs#errors","retry_after":30}}` (retry_after only on 429/202/503).
Codes: unauthorized (401), invalid_request (400), invalid_json (400), terms_not_accepted (400), quota_exceeded (402), plan_forbidden (403), turnstile_failed (403), not_found (404), email_taken (409), rate_limited (429), fetching_from_fmcsa (202), fmcsa_unavailable / fmcsa_throttled (503), paid_signups_paused (403), internal (500).

## Optional

- [Full reference for agents](https://carriervet.dev/llms-full.txt)
- [OpenAPI JSON](https://carriervet.dev/openapi.json) · [OpenAPI YAML](https://carriervet.dev/openapi.yaml)
- [HTML docs](https://carriervet.dev/docs) · [Terms](https://carriervet.dev/terms) · [Privacy](https://carriervet.dev/privacy)
