DataScoop.io
Build a list
Home / Examples / Find carriers by location, fleet and cargo

Find carriers by location, fleet and cargo

Find active FMCSA registrations based in Laredo, Texas, with the reported fleet and cargo you select.

Preview up to 25 matching rows. No account needed.

What the output looks like

Five saved rows and six selected columns from the DataScoop import recorded at . This is the import time, not the date each record was updated.

Saved sample output; blank cells mean no value was supplied
Legal nameUSDOT numberCityStatePower unitsDrivers
INTERCON CARRIERS LC621366LaredoTX94125
SOLO TRANSPORT INC776698LaredoTX2711
F & A EXPRESS LLC814450LaredoTX8270
CIRRUS TRANSPORT INC912918LaredoTX2624
DYNAMIC LOGISTICS LC919459LaredoTX3660

These were the first five matching preview rows captured on 21 October. They are saved output from the dated import; current results can differ after a refresh.

Check the scope and fields

The FMCSA register includes US and cross-border carriers, private fleets and bus operators. This request selects physical addresses in Laredo, Texas. An address is not a statement of the territory a carrier serves.

  • Status, operation, state, city and fleet band use equality filters. Classification and cargo use contains filters because a carrier can report several categories.
  • USDOT identifiers, company names, physical locations, fleet counts, cargo and registration fields are included. Phone numbers, emails and company-officer names are excluded.
  • Active registration and a reported Authorized For Hire classification do not establish current operating authority, insurance, capacity or suitability. Check an individual carrier's current FMCSA record before relying on those details.

Source: FMCSA Company Census File. Source: Federal Motor Carrier Safety Administration, Motor Carrier Census, via the US DOT open-data portal (data.transportation.gov). Public record; not endorsed by or affiliated with the FMCSA or US DOT. Registration details change; check the FMCSA record before relying on them.

Dataset fields and coverage · All datasets

The current request's filters

status
Active
Equality; case-insensitive
state
TX
Equality; case-insensitive
city
Laredo
Equality; case-insensitive
operation
Interstate
Equality; case-insensitive
fleetBand
21-100 units
Equality; case-insensitive
classification
Authorized For Hire
Contains; case-insensitive
cargo
General Freight
Contains; case-insensitive

strict_filters=1 rejects invalid or unknown nonempty filters. It does not change a contains filter into an exact match.

Open the first JSON page · Inspect the current schema

Use this with an assistant

In an assistant that supports hosted MCP, add https://datascoop.io/mcp using its connection settings. A key is optional for a free preview. Keep any key in the connection settings; do not paste it into a prompt.

Use DataScoop's us-motor-carriers dataset to find active registrations with a physical address in Laredo, Texas, interstate operations and 21–100 power units. Require classification to include Authorized For Hire and cargo to include General Freight. Start with 25 rows. Show legal name, USDOT number, city, state, power units and drivers. Include the source, recorded import date and whether more rows match. Treat these as reported census details, not verified current operating authority or available capacity.

Ask the assistant to inspect get_dataset_schema, then call query_dataset with these arguments:

{
  "dataset": "us-motor-carriers",
  "filters": {
    "status": "Active",
    "state": "TX",
    "city": "Laredo",
    "operation": "Interstate",
    "fleetBand": "21-100 units",
    "classification": "Authorized For Hire",
    "cargo": "General Freight"
  },
  "strict_filters": true,
  "limit": 25,
  "offset": 0
}

Read total, returned, truncated, applied_filters and provenance.imported_at before describing the result. If more rows match, request further pages only when you need them. A failed query or an empty result is not evidence that no such businesses exist.

Use this in your app

These examples use standard libraries: Node.js 20 or later, or Python 3. Save the file, then run it from a terminal. No account or paid AI model is needed to try a free query. If you have a DataScoop key, set DATASCOOP_API_KEY in your environment; it is sent as a Bearer header.

The code requests 25 rows per page, supports free pagination and stops above 2,500 matching rows or 100 pages. It prints JSON only after all reported rows have been retrieved. A zero-match result prints an empty row list; errors exit without printing a partial list.

JavaScript · Node.js 20+

Save as query.mjs, then run node query.mjs. Download the JavaScript example.

// Node.js 20+. No packages required. Optional key: DATASCOOP_API_KEY.
const endpoint = "https://datascoop.io/v1/datasets/us-motor-carriers/query";
const dataset = "us-motor-carriers";
const filters = {
  "status": "Active",
  "state": "TX",
  "city": "Laredo",
  "operation": "Interstate",
  "fleetBand": "21-100 units",
  "classification": "Authorized For Hire",
  "cargo": "General Freight"
};
const PAGE_SIZE = 25, MAX_ROWS = 2500, MAX_PAGES = 100;
const headers = { Accept: "application/json" };
const key = (process.env.DATASCOOP_API_KEY || "").trim();
if (key) headers.Authorization = "Bearer " + key;

async function retrieve() {
  const rows = [];
  let total = null, importedAt = null, first = null;
  for (let page = 0; page < MAX_PAGES; page++) {
    const url = new URL(endpoint);
    url.search = new URLSearchParams({ ...filters, strict_filters: "1",
      limit: String(PAGE_SIZE), offset: String(rows.length) });
    const response = await fetch(url, { headers, signal: AbortSignal.timeout(30000) });
    if (!response.ok) throw new Error("Query failed: HTTP " + response.status +
      ". Check filters and access; retry later for rate limits or server errors.");
    const result = await response.json();
    if (result.dataset !== dataset || !Array.isArray(result.rows) ||
        result.rows.some(row => !row || typeof row !== "object" || Array.isArray(row)) ||
        !Array.isArray(result.ignored) || result.ignored.length ||
        !Number.isSafeInteger(result.total) || result.total < 0 ||
        result.offset !== rows.length || result.returned !== result.rows.length ||
        result.returned > PAGE_SIZE || typeof result.truncated !== "boolean") {
      throw new Error("Unexpected query response; no partial result was printed.");
    }
    if (result.total > MAX_ROWS) throw new Error("More than " + MAX_ROWS +
      " rows match. Narrow the filters or review the bound before increasing it.");
    for (const [name, value] of Object.entries(filters)) {
      if (String(result.applied_filters?.[name]) !== String(value))
        throw new Error("A requested filter was not applied: " + name);
    }
    const imported = result.provenance?.imported_at;
    if (typeof imported !== "string" || !Number.isFinite(Date.parse(imported)))
      throw new Error("Import date is missing; no partial result was printed.");
    if (first === null) { first = result; total = result.total; importedAt = imported; }
    if (result.total !== total || imported !== importedAt)
      throw new Error("The import or match count changed. Run the request again.");
    const next = rows.length + result.returned;
    if (next > total || result.truncated !== (next < total) ||
        (result.returned === 0 && next < total))
      throw new Error("Pagination stopped making sense; no partial result was printed.");
    rows.push(...result.rows);
    if (rows.length === total) return { dataset, filters, total, rows,
      provenance: first.provenance, licence: first.licence, attribution: first.attribution };
  }
  throw new Error("Page limit reached. Narrow the filters; no partial result was printed.");
}

try {
  const result = await retrieve();
  if (result.total === 0) console.error("No matching records. Check the selected area and filters.");
  // Keep provenance and attribution with cached or republished data.
  console.log(JSON.stringify(result, null, 2));
} catch (error) {
  console.error(error instanceof Error ? error.message : "Request failed.");
  process.exitCode = 1;
}
Python · standard library

Save as query.py, then run python3 query.py. Download the Python example.

# Python 3. No packages required. Optional key: DATASCOOP_API_KEY.
import json
import os
import sys
from datetime import datetime
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen

ENDPOINT = "https://datascoop.io/v1/datasets/us-motor-carriers/query"
DATASET = "us-motor-carriers"
FILTERS = json.loads("{\"status\":\"Active\",\"state\":\"TX\",\"city\":\"Laredo\",\"operation\":\"Interstate\",\"fleetBand\":\"21-100 units\",\"classification\":\"Authorized For Hire\",\"cargo\":\"General Freight\"}")
PAGE_SIZE, MAX_ROWS, MAX_PAGES = 25, 2500, 100
HEADERS = {"Accept": "application/json"}
key = os.environ.get("DATASCOOP_API_KEY", "").strip()
if key:
    HEADERS["Authorization"] = "Bearer " + key

def text_value(value):
    return "true" if value is True else "false" if value is False else str(value)

def retrieve():
    rows, total, imported_at, first = [], None, None, None
    for _ in range(MAX_PAGES):
        params = dict(FILTERS, strict_filters="1", limit=PAGE_SIZE, offset=len(rows))
        request = Request(ENDPOINT + "?" + urlencode(params), headers=HEADERS)
        with urlopen(request, timeout=30) as response:
            result = json.load(response)
        if (not isinstance(result, dict) or result.get("dataset") != DATASET or
            not isinstance(result.get("rows"), list) or
            any(not isinstance(row, dict) for row in result["rows"]) or
            not isinstance(result.get("ignored"), list) or result["ignored"] or
            type(result.get("total")) is not int or result["total"] < 0 or
            type(result.get("offset")) is not int or result["offset"] != len(rows) or
            type(result.get("returned")) is not int or
            result.get("returned") != len(result["rows"]) or
            result["returned"] > PAGE_SIZE or type(result.get("truncated")) is not bool):
            raise ValueError("Unexpected query response; no partial result was printed.")
        if result["total"] > MAX_ROWS:
            raise ValueError("More than 2500 rows match. Narrow filters or review the bound.")
        applied = result.get("applied_filters")
        if not isinstance(applied, dict):
            raise ValueError("Applied filters are missing.")
        for name, value in FILTERS.items():
            if name not in applied or text_value(applied[name]) != text_value(value):
                raise ValueError("A requested filter was not applied: " + name)
        provenance = result.get("provenance")
        imported = provenance.get("imported_at") if isinstance(provenance, dict) else None
        if not isinstance(imported, str):
            raise ValueError("Import date is missing; no partial result was printed.")
        datetime.fromisoformat(imported.replace("Z", "+00:00"))
        if first is None:
            first, total, imported_at = result, result["total"], imported
        if result["total"] != total or imported != imported_at:
            raise ValueError("The import or match count changed. Run the request again.")
        next_offset = len(rows) + result["returned"]
        if (next_offset > total or result["truncated"] != (next_offset < total) or
            (result["returned"] == 0 and next_offset < total)):
            raise ValueError("Pagination stopped making sense; no partial result was printed.")
        rows.extend(result["rows"])
        if len(rows) == total:
            return dict(dataset=DATASET, filters=FILTERS, total=total, rows=rows,
                        provenance=first["provenance"], licence=first.get("licence"),
                        attribution=first.get("attribution"))
    raise ValueError("Page limit reached. Narrow filters; no partial result was printed.")

try:
    result = retrieve()
    if result["total"] == 0:
        print("No matching records. Check the selected area and filters.", file=sys.stderr)
    # Keep provenance and attribution with cached or republished data.
    print(json.dumps(result, indent=2))
except HTTPError as error:
    print("Query failed: HTTP " + str(error.code) + ". Check filters and access; retry later for rate limits or server errors.", file=sys.stderr)
    sys.exit(1)
except (URLError, TimeoutError, ValueError, TypeError, KeyError) as error:
    print(str(error), file=sys.stderr)
    sys.exit(1)

The code stops if the reported import date or total changes between pages. Requests do not pin a retained snapshot, so an unchanged date does not guarantee that an import cannot cause missing or repeated rows across pages. Keep the source, filters, import date and attribution with saved results; cache them instead of polling.

Try another example

Build a care-home directory · Research restaurants, cafés and canteens · All examples · Full dataset catalogue