Minnesota DLI Contractor License Lookup avatar

Minnesota DLI Contractor License Lookup

Pricing

from $0.86 / 1,000 item extracteds

Go to Apify Store
Minnesota DLI Contractor License Lookup

Minnesota DLI Contractor License Lookup

Search and export official Minnesota DLI residential contractor licenses by business, number, type, status, city, and enforcement indicator.

Pricing

from $0.86 / 1,000 item extracteds

Rating

0.0

(0)

Developer

Automation Lab

Automation Lab

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Categories

Share

Search, filter, and export Minnesota DLI contractor licenses from the official residential-contractor roster.

Use a business name, holder name, license number or batch of licenses, type, status, city, ZIP, enforcement indicator, or renewal indicator to turn the state CSV into integration-ready records.

The Actor is designed for contractor verification, procurement screening, compliance reviews, and scheduled status checks.

What does Minnesota DLI Contractor License Lookup do?

The Actor downloads the public residential-contractor roster published by the Minnesota Department of Labor and Industry (DLI).

It then:

  1. validates your filters;
  2. downloads the current official CSV;
  3. checks that the response has the expected DLI structure;
  4. parses and normalizes every row;
  5. applies all requested filters together;
  6. deduplicates records by license number;
  7. stops at your maxItems limit;
  8. saves typed records to the default Apify dataset.

No login, browser, or proxy is required.

Who is it for?

Procurement teams

Verify a contractor before onboarding or renewing an agreement.

Compliance and risk teams

Review license status, expiry, and DLI's enforcement-action indicator.

Marketplaces and property platforms

Enrich contractor profiles with official public license facts and provenance.

Data and operations teams

Export a repeatable snapshot to JSON, CSV, Excel, a warehouse, or an automation workflow.

Developers

Call one Actor endpoint instead of writing and maintaining CSV download, parsing, filtering, and normalization code.

Why use this Actor?

  • Official source: every row comes from DLI's public residential-contractor export.
  • Useful filters: search by broad text or narrow fields without downloading the file yourself.
  • Typed results: dates, booleans, contact fields, status, and provenance have stable names.
  • Bounded output: maxItems controls dataset size and the number of returned license records.
  • Clear failure behavior: an unavailable or unrecognizable source fails the run instead of pretending there are zero matches.
  • Scheduling ready: rerun the same input to compare snapshots in your own workflow.

What data can I extract?

FieldMeaning
holderTypeDLI's business/person classification
licenseTypeTop-level license category
licenseSubtypeSpecific contractor license or certificate subtype
nameOfficial holder name
businessNamePublished DBA name
addressLine1, addressLine2Published business address
city, state, zipCodePublished location
phone, emailPublic contact values present in the roster
licenseNumberDLI license, registration, or certificate number
licenseStatusStatus text in the current roster
originalDateOriginal date, normalized to YYYY-MM-DD
expirationDateExpiry date, normalized to YYYY-MM-DD
hasEnforcementActionBoolean derived from DLI's enforcement indicator
renewalInProgressBoolean derived from DLI's renewal indicator
sourceUrlExact official CSV URL
sourceAgencyPublishing regulator
retrievedAtUTC retrieval timestamp

Source text and date fields can be null when DLI leaves a cell empty. Enforcement and renewal indicators are always booleans; provenance fields are always strings.

Getting started

  1. Open the Actor in Apify Console.
  2. Enter one or more filters.
  3. Keep maxItems small for your first run.
  4. Click Start.
  5. Open the Dataset tab.
  6. Review or export the matching license rows.
  7. Save the task if you want to schedule the same check.

A simple broad search:

{
"query": "CONSTRUCTION",
"maxItems": 10
}

An exact lookup:

{
"licenseNumber": "XB230289",
"maxItems": 5
}

Verify a supplier list with one roster download (unmatched values produce no synthetic rows):

{
"licenseNumbers": ["XB230289", "BC123456"],
"maxItems": 500
}

Export an issued territory list:

{
"zipCode": "55401",
"licenseStatus": "Issued",
"maxItems": 100
}

An enforcement-indicator snapshot:

{
"enforcementOnly": true,
"maxItems": 100
}

Input parameters

InputTypeDefaultDescription
querystringnoneSubstring across holder name, DBA, license number, type/subtype, and city
businessNamestringnoneSubstring in holder or DBA name
licenseNumberstringnoneExact license number, case-insensitive
licenseNumbersstring[]none1–500 nonempty exact licenses; trimmed, case-insensitive, deduplicated; OR within the list
zipCodestringnoneFive-digit ZIP or nine-digit ZIP+4, optional hyphen
licenseTypestringnoneSubstring in type or subtype
licenseStatusstringnoneExact DLI status
citystringnoneExact city, case-insensitive
enforcementOnlybooleanfalseKeep only rows carrying DLI's enforcement indicator
renewalInProgressOnlybooleanfalseKeep only rows marked as renewal in progress
maxItemsinteger100Save 1 to 50,000 matching records

All filters are combined with AND logic, including licenseNumber AND licenseNumbers when both are set. Batch members use OR within the list. The list must contain 1–500 strings of up to 40 characters before deduplication; blank, nonstring and empty lists are rejected rather than treated as an unfiltered export. Matching rows appear once in source order, not list order; absent licenses produce no rows. maxItems is a global limit after filtering and deduplication, not per license.

ZIP is a string so leading zeros are retained. A five-digit input such as 55401 matches both 55401 and 55401-1234 source ZIPs. 55401-1234 or 554011234 matches only that full ZIP+4. Surrounding whitespace is trimmed; empty or malformed ZIP input is rejected. Output retains the source ZIP value. Other exact filters remain exact, not fuzzy.

For example, city: "ROCHESTER" and licenseStatus: "Issued" returns only issued records in Rochester.

query is also combined with every specific filter.

Output example

A current record has this shape (values below are illustrative and anonymized):

{
"holderType": "Business",
"licenseType": "Residential Contractors",
"licenseSubtype": "Residential Building Contractor",
"name": "SAMPLE CONTRACTOR LLC",
"businessName": "SAMPLE HOME BUILDERS",
"addressLine1": "100 SAMPLE AVE",
"addressLine2": null,
"city": "MINNEAPOLIS",
"state": "MN",
"zipCode": "55401",
"phone": null,
"email": null,
"licenseNumber": "BC123456",
"licenseStatus": "Issued",
"originalDate": "2020-04-15",
"expirationDate": "2027-03-31",
"hasEnforcementAction": false,
"renewalInProgress": false,
"sourceUrl": "https://secure.doli.state.mn.us/ccld/data/MNDLILicRegCertExport_Residential_Contractors.csv",
"sourceAgency": "Minnesota Department of Labor and Industry (DLI)",
"retrievedAt": "2026-09-15T08:00:00.000Z"
}

How much does it cost to look up Minnesota contractor licenses?

Pricing has two events:

  • Run started: $0.00005 once per run.
  • License record returned: tiered per matching dataset item.

At the BRONZE tier, a returned license record costs $0.00144.

Approximate BRONZE examples:

Matching recordsApproximate total
1$0.00149
10$0.01445
100$0.14405
1,000$1.44005

An empty valid search incurs only the start fee.

The selected result cap controls the maximum number of paid record events; a restrictive run charge budget can reduce delivery further. Dataset writes use the SDK's budget-aware item charging path so extra uncharged rows are not delivered. Infrastructure usage is not charged separately under pay-per-event pricing.

Search and filtering tips

  • Use licenseNumber for the narrowest verification.
  • Use businessName when punctuation or spacing may differ from your internal record.
  • Use query for discovery across several identity fields.
  • Status values in DLI data include Issued, Expired, Revoked, Suspended, Voluntary Termination, and Withdrawn.
  • Use licenseType with phrases such as Residential Building Contractor, Qualifying Builder, or Residential Roofer Contractor.
  • Combine enforcementOnly with another filter for focused recurring checks.
  • Increase maxItems only when you need more matches.

Enforcement and renewal indicators

hasEnforcementAction reports whether DLI's published row contains the enforcement-action flag.

It does not describe the action, allegation, order, or case outcome.

renewalInProgress reports the roster's renewal marker.

Neither boolean should be interpreted as legal advice or as a complete regulatory history.

Follow the official source and DLI guidance when a decision requires case-level context.

Recurring verification workflow

  1. Create an Apify Task with a stable license or supplier filter.
  2. Schedule it daily, weekly, or before a procurement checkpoint.
  3. Send results to your preferred storage or automation.
  4. Compare licenseStatus, expirationDate, hasEnforcementAction, and renewalInProgress with your previous snapshot.
  5. Route material changes to a human reviewer.

The Actor returns the current roster snapshot; it does not maintain history or send alerts by itself.

Export and integrations

The default dataset supports:

  • JSON and JSONL;
  • CSV and Excel;
  • XML and RSS;
  • direct API reads;
  • Make integrations;
  • Zapier integrations;
  • webhooks;
  • Google Sheets workflows;
  • cloud warehouse loaders.

For a CRM or vendor database, use licenseNumber as the stable join key when it is present.

Run through the API with cURL

curl -X POST \
"https://api.apify.com/v2/acts/automation-lab~minnesota-dli-contractor-license-lookup/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"businessName":"CONSTRUCTION","licenseStatus":"Issued","maxItems":25}'

To wait for results in one request:

curl -X POST \
"https://api.apify.com/v2/acts/automation-lab~minnesota-dli-contractor-license-lookup/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"licenseNumber":"XB230289","maxItems":5}'

Run through the API with JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/minnesota-dli-contractor-license-lookup').call({
enforcementOnly: true,
maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

Run through the API with Python

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/minnesota-dli-contractor-license-lookup').call(
run_input={'city': 'DULUTH', 'licenseStatus': 'Issued', 'maxItems': 100}
)
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)

Use with MCP and AI agents

Add the Apify MCP server to Claude Code:

claude mcp add --transport http apify \
"https://mcp.apify.com?tools=automation-lab/minnesota-dli-contractor-license-lookup"

Claude Desktop

Add this HTTP MCP server in your Claude Desktop configuration:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=automation-lab/minnesota-dli-contractor-license-lookup"
}
}
}

Cursor

Add the same apify server URL under Settings → MCP in Cursor.

VS Code

Add the same HTTP MCP server URL to your workspace MCP configuration in VS Code.

Example prompts:

  • “Verify Minnesota DLI contractor license XB230289 and summarize its status and expiry.”
  • “Return up to 50 issued residential building contractors in Duluth.”
  • “Export 20 Minnesota contractor rows carrying DLI's enforcement indicator.”

Source freshness and reliability

Each run downloads the roster currently available at DLI's official URL.

The Actor does not cache a private copy between runs.

DLI controls publication timing, corrections, outages, and field coverage.

Transient network errors, rate limits, and server errors get at most three download attempts with bounded exponential backoff and jitter. Permanent HTTP errors and invalid CSV structures are not retried.

Valid UTF-8 (including BOM) is decoded strictly. If the export is not valid UTF-8, its legacy Windows-1252 bytes are decoded without UTF-8 replacement-character corruption. Reordered or extra columns are accepted; all 18 published columns are required and malformed rows, missing license identities, HTML, empty files or empty rosters fail visibly. A valid roster with no matching filters still succeeds with zero output.

If the response is unavailable or no longer resembles the expected CSV, the run fails visibly.

Limitations

  • Coverage is the DLI residential contractor roster, not every Minnesota occupation or trade.
  • Electrical, plumbing, boiler, and other rosters are outside this Actor's scope.
  • The source may contain historical expired certificates as well as current records.
  • Public contact fields can be blank.
  • Search is textual and case-insensitive; it does not perform fuzzy identity resolution.
  • Enforcement output is only DLI's indicator, not enforcement case details.
  • The Actor returns a current snapshot and does not calculate changes from previous runs.
  • Broad unfiltered output is capped by maxItems.

Failure behavior and troubleshooting

My run returns zero records

Check spelling, remove one filter, or use query for a broader substring search.

Remember that specific filters are combined with AND logic.

My status filter returns less than expected

Try removing the status filter and inspect the current text published by DLI.

The run fails while downloading

Inspect the run log for the HTTP status. A DLI outage or changed CSV structure intentionally causes failure rather than unreliable empty output.

Retry later after confirming the official source is reachable.

Dates or contact values are null

Those cells are blank in the official roster. The Actor does not infer or enrich missing government data.

Should I enable a proxy?

No proxy input is needed. The source is a public downloadable file and the Actor uses direct HTTPS.

Private error diagnostics

Failed operations send sanitized diagnostic input, exceptions, and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL query strings are removed; reports are retained for 30 days. Normal no-match searches are not reported as failures. No AI provider is used and no data is sent for model training. The roster is processed ephemerally without a cross-run cache. Results and logs remain in your Apify run storage under your platform retention settings; you can delete them using Apify storage controls. Public contact information is returned only as published by DLI. For bugs or how-to questions, open an issue on the Actor's Apify Store page.

Responsible use and legality

The Actor processes publicly accessible professional and business license data published by a government regulator.

Use it for legitimate verification, compliance, research, and data-quality purposes.

Follow applicable privacy, anti-discrimination, procurement, records-retention, and consumer-reporting rules.

Do not treat one automated field as a final eligibility or legal conclusion.

Verify material decisions against the linked official source and provide appropriate human review.

Choose this Actor when your source of truth is Minnesota DLI's residential-contractor roster.

FAQ

Is this an official Minnesota government service?

No. It is an independent Apify Actor that reads and links to DLI's official public data.

Does it verify all Minnesota professional licenses?

No. It is intentionally limited to the residential-contractor export.

Can I download the whole roster?

Not when the roster exceeds 50,000 rows. An unfiltered run returns up to maxItems (default 100, hard cap 50,000), not a guaranteed complete roster. Use focused filters or the linked official CSV if you need the entire larger file; this release does not raise the cap.

Can I search by a person's name?

Yes. Use businessName to search both official holder and DBA names, or use query to search those fields plus license, type, and city.

Are email and phone always available?

No. They are returned only when DLI publishes them in the row.

Does an enforcement flag mean the license is currently revoked?

Not necessarily. Treat the indicator and status as separate source fields and review official case information for any consequential decision.

Are no-result runs charged?

A valid no-result run incurs the one-time start event but no per-record events.

Can I schedule it?

Yes. Save the input as an Apify Task, add a schedule, and compare each dataset with your own previous snapshot.