FMCSA Carrier Contact Takeover Monitor
Pricing
$2.50 / 1,000 carrier checks
FMCSA Carrier Contact Takeover Monitor
Monitor a USDOT watchlist for FMCSA phone, email, officer, legal-name and address changes. Normalized before/after events come from the official public Company Census, with formatting-only noise suppressed.
Pricing
$2.50 / 1,000 carrier checks
Rating
0.0
(0)
Developer
Vadim Bezrukov
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Monitor a USDOT watchlist for FMCSA phone, email, officer, legal-name and address changes. Paste the approved roster, run it, and use the before/after rows as a re-verification list before the next load is tendered.
The source is the official public FMCSA Company Census on data.transportation.gov (resource az4n-8mr2, also called the Company Census File or MCMIS census). The Actor does not open SAFER pages, does not score safety, and does not decide that fraud occurred. Attention level indicates workflow priority, not fraud probability.
An agent can call https://mcp.apify.com?tools=automa-flow/fmcsa-carrier-contact-takeover-monitor and ask: "Check these USDOT numbers and tell me which phone, email, officer, legal name or address fields changed since the baseline."
Why carrier contact changes matter
Brokers, 3PLs, shippers and carrier-compliance teams keep an approved roster. A new dispatch phone, email, company officer or legal name is a reason to re-check the carrier before tendering, even when authority and insurance monitors stay quiet. This Actor watches that contact slice and returns the old value and the new value.
What this Actor monitors
Each USDOT is read from the daily Company Census file. The compared fields are:
| Field | Census column |
|---|---|
| Legal name | legal_name |
| DBA | dba_name |
| Phone | phone |
| Cell phone | cell_phone |
email_address | |
| Physical address | phy_street, phy_city, phy_state, phy_zip, phy_country |
| Mailing address | carrier_mailing_street, carrier_mailing_city, carrier_mailing_state, carrier_mailing_zip, carrier_mailing_country |
| Officers | company_officer_1, company_officer_2 |
Officer rows have a name and the source field. The census does not publish a reliable role, so none is invented. Empty census values stay null. The Actor does not add authority, insurance, safety or cargo fields.
Quick start
The form is prefilled with three active public carriers: Schneider National Carriers (264184), Swift Transportation Co. of Arizona (54283) and Werner Enterprises (53467).
- Replace that list with your approved USDOT numbers.
- Keep
includeBaselineon. - Run. Each found carrier is a
BASELINE_CREATEDrow. A number that is not in the file isNO_RESULTS. - Schedule the same input daily. The default repeat still writes one row per carrier. A changes-only task sets
emitUnchangedto false and then returns material changes only.
{"usdotNumbers": ["264184", "54283", "53467"],"snapshotKey": "approved-carriers","emitUnchanged": false,"includeBaseline": true}
Input
usdotNumbers is required. One to 10,000 values. Each value is 1 to 8 digits. Spaces are trimmed, leading zeros are removed, and repeats are checked once. 0 is rejected before the run starts.
snapshotKey names an independent roster. Use a different key for a different list so the baselines do not mix. Allowed characters are letters, numbers, _ and -, up to 80 characters. The stored key is a hash of that label.
includeBaseline defaults to true. emitUnchanged defaults to true, so a repeat of the prefilled roster still writes OK_NO_CHANGE rows. Set emitUnchanged to false only on a scheduled task that should list changes. The watchlist length is capped at 10,000 by the input form. A longer list is rejected before the run starts.
Output
One dataset row is one carrier observation. status and recordType are the same.
| Status | Meaning |
|---|---|
BASELINE_CREATED | First successful census record for this USDOT and snapshot. No historical change is invented. |
OK_NO_CHANGE | Normalized contact fields match the last successful check. Written by default. Hidden when emitUnchanged is false. |
CHANGE | At least one field changed in a way the normalizer treats as material. |
AMBIGUOUS_CHANGE | The only difference is an officer name whose tokens match in a different order. The row is emitted so it is not dropped. |
NO_RESULTS | The census request succeeded and this USDOT is not in the file. |
SOURCE_FAILED | This USDOT could not be checked. Its baseline is left as it was. |
UNPROCESSED | The check was not charged, the run time budget ran out, or the saved baseline schema is not one this build can read. The baseline is left as it was. |
changes keeps the raw before/after values and the normalized forms. current is the raw census contact when the carrier was found. attentionLevel is high or medium, or null when nothing material changed.
High means phone, cell phone, email, officer, legal name, or two or more identity fields together. Medium means only a DBA or a single address. Attention level indicates workflow priority, not fraud probability.
source.datasetUpdatedAt is the census file time, not the carrier's MCS-150 filing date. A null timestamp means the catalog time was unavailable for that run. The rows are still the census records that were returned.
The run summary is RUN_SUMMARY in the run key-value store. It counts unchanged carriers even when those rows are omitted. outcome is ok, partial, no_results, source_failed or internal_error.
Baselines are not stored in the run store. They live in the named store fmcsa-carrier-contact-takeover-monitor-state, one record per snapshot and USDOT. That record keeps the last census contacts, including a business email and a cell phone, until you delete it. In Apify Storage, open that key-value store and delete the baseline keys for one roster, or delete the store. Deleting the store removes every snapshot. It does not delete dataset rows that were already delivered.
Change types
Field codes stay specific. A phone change is never collapsed into a generic alert.
LEGAL_NAME_CHANGEDDBA_CHANGEDPHONE_CHANGEDCELL_PHONE_CHANGEDEMAIL_CHANGEDPHYSICAL_ADDRESS_CHANGEDMAILING_ADDRESS_CHANGEDOFFICER_CHANGEDMULTIPLE_IDENTITY_FIELDS_CHANGEDwhen two or more of those fields change togetherCENSUS_RECORD_REMOVEDwhen a previously seen USDOT leaves the fileCENSUS_RECORD_ADDEDwhen a USDOT that was confirmed absent appears for the first timeCENSUS_RECORD_RESTOREDwhen a previously seen USDOT returns
changeClasses adds CONTACT_CHANGE, NAME_CHANGE, ADDRESS_CHANGE or OFFICER_CHANGE as a convenience. Filter on changeTypes when the field matters.
When a previously seen USDOT disappears from the file, the row is NO_RESULTS, previouslyObserved is true, and the only change type is CENSUS_RECORD_REMOVED. The row does not invent a change for every field. If that USDOT comes back with the same contacts, the row is CHANGE with only CENSUS_RECORD_RESTORED. If it comes back with different contacts, the row lists those field changes and CENSUS_RECORD_RESTORED. A USDOT that was confirmed absent before it ever had a census row, and later appears, is CENSUS_RECORD_ADDED with previouslyObserved false. Repeated absence stays NO_RESULTS with an empty changes object.
Normalization
Formatting noise is suppressed. Semantic changes are not. Raw text is always what you see in before and after.
Phone: trim, keep digits, and drop a leading 1 only when the number has 11 digits. (555) 123-4567, 555-123-4567, 5551234567 and +1 555 123 4567 match. 5551234567 and 5559990000 do not.
Email: Unicode normalization, trim, and lowercasing of the whole address. Plus tags stay. Ops@Example.com matches ops@example.com. ops+dispatch@example.com does not match ops@example.com.
Names: case, whitespace and decorative punctuation such as commas and periods. Dotted L.L.C. matches LLC. The words LLC, INC, CORP, CO and LP are not removed. Hyphens stay, so Smith-Jones does not match Smith Jones.
Addresses use the same text rules, plus ZIP punctuation only. There is no geocoding, USPS rewrite or fuzzy street match. 123 Main St. matches 123 main st. A different street number does not.
Officers use the same text rules. The same names in a different slot order are not a change. A different set of names is OFFICER_CHANGED. SMITH, JOHN and JOHN SMITH in the same slot are AMBIGUOUS_CHANGE, not a silent match and not a clean replacement.
First-run baseline
The first successful run creates the comparison baseline. Starting from the next successful run, the Actor emits material changes.
With includeBaseline true, that first run writes a BASELINE_CREATED row for every USDOT that is in the file, so the dataset is not empty. It does not invent a change that happened before you started watching.
An immediate second run with the same census values produces no CHANGE rows. By default it writes an OK_NO_CHANGE row for each carrier, so the dataset is not empty. Set emitUnchanged to false when you want that second run to stay empty unless something material changed. RUN_SUMMARY.unchanged still counts the carriers that were checked.
A failed check does not replace a good baseline. The next successful check compares against the last good one.
Scheduling
Save the input, schedule it daily, and send the dataset to the place you already review carriers.
- In Apify, open the run and create a schedule for the saved input. Daily is the cadence that matches the census file.
- Add a webhook on successful runs if you only need the dataset when the run finishes.
- Read the
changesview. It includesCHANGE,AMBIGUOUS_CHANGE, and aNO_RESULTSrow for a USDOT that was seen before and then left the file.
Use one snapshotKey per roster. Two schedules with the same key can block each other: the second run stops and asks you to wait, so the baselines are not written by two runs at once.
n8n, Make and API
Start the Actor with the Apify API using the same JSON input. In n8n or Make, use the Apify node or an HTTP request to run the Actor, then read the default dataset. Map usdotNumber, status, attentionLevel, changeTypes and changes into the review queue.
Do not build a fraud rule on attentionLevel. Route high rows to a person who can re-verify the carrier. Route NO_RESULTS to someone who can confirm the USDOT. Leave SOURCE_FAILED for a retry. Those rows were not billed and the baseline did not move.
The direct MCP tool is https://mcp.apify.com?tools=automa-flow/fmcsa-carrier-contact-takeover-monitor. Hosted MCP search and a run on your own Apify account are separate steps. This README does not start a run for you.
Related automa-flow tools
- Send only the changes on: Dataset Cleaner filters this run's Dataset to rows whose
statusisCHANGEorAMBIGUOUS_CHANGE, renames columns and dedupes, before an alert or CRM export. Pass the Dataset ID asdatasetId.
Each Actor runs and charges separately. This Actor does not call them.
Pricing
A completed carrier check is one carrier-check event at $0.0025. One thousand successful checks are $2.50. The event covers a baseline, a no-change check, a change, an ambiguous change, or a confirmed absence.
SOURCE_FAILED, invalid input and UNPROCESSED are not charged. Duplicate USDOT numbers in one input are checked once. Retries against the census are not a separate event.
RUN_SUMMARY.carrierChecksBilled is the number of events charged on a pay-per-event run. On a run that is not pay-per-event the field stays 0, while checked still counts the work.
This price is the launch hypothesis for a later pricing review. It is not a live Store price until the Actor is published. Platform usage for an ordinary run is intended to be included in the event. Measure a 10,000-carrier day on staging before publication. Do not treat this README as a bill.
Data source
Requests go to https://data.transportation.gov/resource/az4n-8mr2.json in batches of 75 USDOT numbers, with at most 4 batches in flight. The client retries 408, 425, 429, 500, 502, 503, 504, timeouts and connection failures, at most 3 attempts, with exponential backoff and Retry-After when the server sends it. After the first HTTP 429 the run sends one batch at a time and waits 5 seconds, then 10, or the Retry-After when it is a minute or less. No proxy and no browser.
The watchlist is processed in slices of 450 USDOT numbers. Each slice is checked, saved, charged and written to the dataset before the next one starts, so a 10,000-carrier roster fits the default 256 MB, and a run that reaches its time limit keeps every slice it finished. The format-change and absence checks below look at one slice at a time.
The catalog is read from https://api.us.socrata.com/api/catalog/v1?ids=az4n-8mr2 for the file timestamp. A missing catalog does not fail the census read. Checked on 29 September 2026, the id was still az4n-8mr2, the file had been updated the day before, and the public API returned legal name, phone, email, both officer fields and both addresses without a token.
Email and officer coverage is high on active rows and is not 100 percent. A null email means the census file has no email, not that the lookup failed.
Privacy and limitations
The census is a public FMCSA business file. Officer names and business emails in that file are part of the carrier record the agency publishes. The Actor does not add personal data from other sites, does not infer fraud, and does not call a carrier safe or unsafe.
Logs contain the USDOT and the names of fields that changed. They do not contain phone numbers, emails, addresses or officer names. Those values are only in the dataset.
Limits you should plan for:
- The census can lag a filing.
datasetUpdatedAttells you which file you read. - Null contact fields are real gaps in the file.
- Formatting rules are conservative. An unusual abbreviation can still alert. That is preferred to hiding a new phone number.
- This is not an authority, insurance, safety, new-carrier or discovery product.
- Two runs of the same snapshot key cannot update baselines together. The second run stops while the first is still running. A later run takes the lock about three minutes after the last heartbeat. If this run cannot refresh its own lock, it stops without saving or charging.
- If a run stops after it has saved a new observation and before that row is delivered, the next run delivers it when the census still matches. It does not charge that observation again. If the census has moved on, the comparison uses the last delivered baseline.
- Delivery is at least once. If a run is killed after the dataset row is written and before the baseline is marked delivered, the next run writes that same row again. Deduplicate on
usdotNumberplusfingerprint. - If the census keeps failing, the run stops requesting later batches. Those USDOT numbers are
SOURCE_FAILEDand their baselines stay as they were. - If the account charge limit is reached, the rest of the list is
UNPROCESSED. Those carriers are not billed and their baselines stay as they were. - After a census rate limit the run slows to one batch at a time. A
Retry-Afterlonger than 60 seconds on a rate limit, or longer than 20 seconds on a server error, fails that batch instead of pausing the run for the full delay. That batch isSOURCE_FAILED, not billed, and its baselines stay as they were.
NO_RESULTS vs SOURCE_FAILED
NO_RESULTS means the request succeeded, the payload matched the census shape, and that USDOT was not in the rows. It is a completed check.
SOURCE_FAILED means this USDOT was not verified. Typical causes are a timeout, a rate limit that survived retries, HTTP 401, 403, 404 or 5xx, invalid JSON, a row with no legal name, or a row that is not tied to one USDOT. If one census batch drops too many carriers that were present on the last good check, or the same field changes for more than half of a roster of 20 or more and for at least 10 carriers, or a phone or physical address is filled on fewer than 20 percent of at least 10 present rows, that batch is SOURCE_FAILED and those baselines stay. One failed batch does not erase results from batches that succeeded. Failed USDOTs do not update their baselines, and they are not charged.
If every USDOT fails, the run itself fails. A mix of successes and failures finishes as a partial success so you still get the rows that were checked.
FAQ
Does this decide that a carrier was taken over? No. It reports contact-field evidence from the official census. A person or a downstream process decides what to re-verify.
Why is the second run empty? The default repeat writes OK_NO_CHANGE rows. The dataset is empty on a repeat only when emitUnchanged is false and nothing material changed. Read RUN_SUMMARY for the unchanged count.
Why did a phone format change not alert? The digits match, including a leading US 1 on an 11-digit number. The raw strings can differ.
What if the officer was stored as LAST, FIRST and later as FIRST LAST? That is AMBIGUOUS_CHANGE. It is not treated as the same person and it is not dropped.
Can I watch two rosters? Yes. Use two snapshotKey values. They do not share baselines.
Does the default run need my carriers? The prefilled USDOTs are public active carriers so a first run returns baselines. Replace them with your roster before you schedule it.
If a run fails or the rows look wrong, open an Issue on this Actor and include the run URL. Apify shares that run automatically. Do not paste phone numbers, emails, addresses or officer names into the Issue. RUN_SUMMARY has the counts, the census time and the error family.
Update history is in the changelog.