Dev.to · 6 min read

Build a fresh trucking-insurance lead feed: every new US carrier with phone + email, updated weekly

Build a fresh trucking-insurance lead feed: every new US carrier with phone + email, updated weekly

When a trucking company registers with the FMCSA and gets its authority, the clock starts on a bunch of things it now legally has to buy: primary liability and cargo insurance, a BOC-3 process agent, an ELD, often factoring to survive the 30–60 day payment cycle. Whoever reaches that carrier first — in the days after it registers, while it's still shopping — wins the account. The good news for anyone selling into that market: the list of who just registered is a public federal record, published on an official US DOT open-data portal, with phone and email on file for essentially every carrier registered in the last couple of years. You do not need to scrape SAFER, solve captchas, or buy a $300/month lead list. You need one HTTPS request and a filter. This post shows how to pull it yourself, where it gets fiddly, and how to turn it into a scheduled weekly feed. Where the data lives Everything SAFER shows you about a carrier — legal name, DBA, address, phone, email, company officers, fleet size, cargo, hazmat flag, safety rating — is rendered from FMCSA's Company Census File. FMCSA publishes that census on data.transportation.gov, the US DOT's open-data portal, which runs on Socrata. That means the same SODA API you'd use for any city open-data set: plain HTTPS in, JSON out, SQL-ish query params, no API key for moderate use. First, find the current dataset. Portal dataset IDs occasionally change when an agency re-publishes, so don't hard-code one you found in a blog post — ask the catalog: curl "https://api.us.socrata.com/api/catalog/v1?domains=data.transportation.gov&q=motor%20carrier%20census&only=datasets" That returns the dataset's id (a four-four Socrata token like az4n-8mr2) and its resource URL. From there the census is queryable at: https://data.transportation.gov/resource/.json Pull the five most-recently-added carriers: curl "https://data.transportation.gov/resource/az4n-8mr2.json?\ \$order=add_date%20DESC&\$limit=5" The annoying parts (there are three) 1. The column names are not what you'd guess. The census predates every naming convention you like. Depending on the published extract, the phone field is telephone or phone, email is email_address, physical state is phy_state, the power-unit count is nbr_power_unit (not power_units), and the "when did this carrier first appear" date is add_date. Before you build anything, hit the dataset's own API-docs page (linked from the portal page) or just pull one row and read its keys: curl "https://data.transportation.gov/resource/az4n-8mr2.json?\$limit=1" | python3 -m json.tool 2. "New carrier" is a date field, and dates on Socrata are strings. Filter server-side with $where against add_date, ISO-formatted: curl "https://data.transportation.gov/resource/az4n-8mr2.json?\ \$where=add_date%20%3E=%20'2026-06-01T00:00:00'&\ \$order=add_date%20DESC&\$limit=1000" 3. Contact completeness varies by age. Carriers registered recently almost always have both phone and email on file; older records have gaps. If you're building a contactable lead feed, filter for the fields you actually need (WHERE email_address IS NOT NULL) rather than assuming. DIY: a weekly new-carrier feed in ~30 lines Here's a self-contained Node script: every active carrier added since a given date, in your states, with phone and email, newest first. const DATASET = 'az4n-8mr2'; // verify via the catalog query above const BASE = `https://data.transportation.gov/resource/${DATASET}.json`; async function newCarriers({ states, addedAfter, limit = 1000 }) { const stateList = states.map((s) => `'${s}'`).join(','); const where = [ `add_date >= '${addedAfter}T00:00:00'`, `phy_state in (${stateList})`, `telephone IS NOT NULL`, `email_address IS NOT NULL`, ].join(' AND '); const params = new URLSearchParams({ $where: where, $order: 'add_date DESC', $limit: String(limit), }); const rows = await (await fetch(`${BASE}?${params}`)).json(); return rows.map((r) => ({ dotNumber: r.dot_number, legalName: r.legal_name, dbaName: r.dba_name || null, phone: r.telephone, email: r.email_address, state: r.phy_state, powerUnits: r.nbr_power_unit ? Number(r.nbr_power_unit) : null, addedDate: (r.add_date || '').slice(0, 10), safer: `https://safer.fmcsa.dot.gov/query.asp?searchtype=ANY&query_type=queryCarrierSnapshot&query_param=USDOT&query_string=${r.dot_number}`, })); } const leads = await newCarriers({ states: ['TX', 'OK'], addedAfter: '2026-06-01' }); console.log(`${leads.length} contactable new carriers`); console.log(leads.slice(0, 5)); Cron that weekly, diff against last week's DOT numbers so you only email each carrier once, and you have a real lead pipeline for the cost of nothing. One data-source gotcha that will bite you if you don't know it: FMCSA populates a carrier's cargo-classification flags about two months after it first registers. So if you filter for, say, "refrigerated" carriers and "added in the last 30 days," you'll get almost nothing — not because they don't exist, but because that field hasn't been backfilled yet. Filter on cargo type for established carriers, and on add_date for fresh ones; don't combine the two on a tight recent window. Things you'll end up building on top of it Within a week of using the raw feed seriously you'll want: fleet-size banding (nbr_power_unit between 1 and 10 is the factoring/fuel-card sweet spot), interstate-vs-intrastate filtering, hazmat and safety-rating fields, company-officer extraction (the decision-maker's name is in there), de-duplication across weekly runs, the ~1,000-row Socrata page limit handled with $offset paging, and retry/backoff. None of it is hard. All of it is maintenance you now own. The maintained shortcut I package exactly this — the census, streamed and filtered server-side, with all the fields above normalized into clean names and the fleet/cargo/hazmat/date filters as simple inputs — as an Apify actor: FMCSA Motor Carrier Scraper The weekly-insurance-lead query becomes: { "states": ["TX", "OK"], "statuses": ["active"], "addedAfter": "2026-06-01", "requireEmail": true, "requirePhone": true, "maxResults": 1000 } Every record comes back in the same clean shape — dotNumber, legalName, dbaName, phone, email, companyOfficers, powerUnits, cargoTypes, safetyRating, saferUrl — and Apify gives you JSON/CSV/Excel export, a scheduler (run it every Monday at 6am), and webhooks into your CRM. You're charged per record returned, so a weekly pull of a few hundred fresh carriers costs about a dollar. It also handles the paging, the cargo-lag warning, and re-discovers the dataset ID if DOT re-publishes it — the babysitting I described above. Which route should you take? One state, personal prospecting → the script above. It's 30 lines and the data is free. Multiple states, weekly scheduling, fleet/cargo filters, CRM webhooks, or feeding an AI agent → the actor (it's also callable as an MCP tool via Apify, so "any new reefer carriers in Texas this week?" becomes a one-line agent query). Enterprise BD with dialer integration and territory routing → that's when the dedicated trucking-lead SaaS earns its subscription. Either way the underlying record is a public federal registration — official, free, and yours to use. That's the part most people selling "exclusive carrier leads" would rather you didn't know. Questions about a specific census field or a state's registration volume? Drop a comment — I've spent more time in this dataset's column names than I'd like to admit.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News