Minnesota DLI Professional License Scraper
Pricing
from $1.20 / 1,000 results
Minnesota DLI Professional License Scraper
Scrape contractor and tradesman license records from Minnesota DLI (Department of Labor and Industry). Downloads bulk CSV regulant lists — residential contractors, electricians, plumbers, roofers, mechanical bond holders and more. No browser needed, direct file download.
Pricing
from $1.20 / 1,000 results
Rating
0.0
(0)
Developer
Haketa
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 hours ago
Last modified
Categories
Share
Minnesota DLI Professional License Scraper — Contractor, Electrician, Plumber, Roofer & Boiler License Data Extractor
The most complete Minnesota Department of Labor and Industry (DLI) license data extraction tool on Apify. Download the full public regulant export for residential contractors, electricians, plumbers, roofers, boiler operators, elevator constructors, high-pressure pipefitters, mechanical contractor bond holders, and manufactured home installers — structured, normalized, and ready for compliance, B2B sales, recruiting, credentialing, and analytics workflows across Minneapolis, Saint Paul, Rochester, Duluth, and every other Minnesota market.
What This Actor Does
The Minnesota DLI Professional License Scraper is a production-grade Apify Actor that extracts the complete public regulant database published by the Minnesota Department of Labor and Industry (DLI) — the state agency that licenses contractors, registers tradespeople, and tracks bond and enforcement records across Minnesota's construction, mechanical, and building trades industries.
In a single run (typically 30–90 seconds for the full ZIP), the actor returns thousands of structured records spanning ten distinct license categories that DLI publishes as bulk CSV exports — no browser, no login, no API keys, no captcha solving. Each record is normalized into a single flat JSON schema so downstream consumers (warehouses, CRMs, BI tools) can ingest the entire dataset without per-category branching logic.
The ten Minnesota DLI license categories covered:
- Electrical — Class A/B masters, journeyworkers, installers, power-limited technicians, electrical contractors
- Plumbing — master, journeyworker, and restricted plumbers plus plumbing contractors
- Residential Contractors — Residential Building Contractor (RBC) and Residential Remodeler (RR) licenses
- Contractor Registrations — registered (non-licensed) construction contractors using the residential-exemption pathway
- Mechanical Contractor Bond — surety bond filings for mechanical / HVAC contractors with bond amount and surety company
- Boiler — operator and inspector credentials (1st-class, 2nd-class, low-pressure, special)
- High Pressure Piping — pipefitter and pipefitting contractor licenses
- Residential Roofer — standalone Minnesota residential roofer licenses
- Manufactured Home Installer — licensed installers of HUD-code manufactured housing
- Elevator — elevator constructor mechanics, limited mechanics, and elevator contractors
Each record carries license number, status, original issue date, expiration date, full mailing address, entity classification (Business vs. Personal), DBA, responsible/qualifying individual, phone, email (when published), bond amount + surety (where applicable), and enforcement action flags — making this the fastest path to a clean Minnesota tradesperson and contractor dataset for compliance, lead generation, credentialing, and market intelligence use cases.
Why scrape DLI yourself when this exists?
DLI publishes the raw data as ten separate CSV files (plus one combined ZIP) on secure.doli.state.mn.us. Most teams who try the DIY route hit the same wall of papercuts:
- Per-category CSVs have slightly different column orderings (Electrical exposes Phone/Email, Mechanical Bond exposes Bond Amount/Surety, Roofer omits both) so naive
pd.concat()joins fail - Header casing drifts between exports (
Lic_NumbervsLic_NovsLicense_Number) and breaks string-keyed parsers - The
Bus_Persfield is a single-letter code (B/P) that must route Name into eitherbusinessNameorindividualName Addr1+Addr2come as separate columns and must be merged; ZIPs ship with a-0000suffix that needs stripping- Date columns alternate between
MM/DD/YYYYandYYYY-MM-DDdepending on category - No incremental update endpoint exists — you must redownload the full export every run
- DLI occasionally adds new categories (Residential Roofer was added later; Mechanical Bond was promoted to its own export) — homegrown parsers silently miss new files
- The official site is HTTPS-only with a strict TLS profile that trips older Python
requestssetups
This actor solves all of that: it streams the CSVs (or the combined ZIP) directly, applies a 100+ entry header alias table to survive schema drift, normalizes dates and ZIPs, routes Name into the correct field by entity type, applies your filters, and outputs deterministic JSON — no parsing scripts, no schema babysitting, no glue code.
Quick Start
One-Click Run
- Click "Try for free" on the Apify Store page for
haketa/minnesota-dli-license-scraper - Pick specific categories (prefilled with
Mechanical_Contractor_BondandResidential_Roofer) or tick Download ZIP (All Categories) to grab everything in one shot - Hit Start — full export ready in under two minutes
- Download as JSON, CSV, Excel, or HTML from the Apify dataset view, or stream to Google Sheets / BigQuery / your CRM via built-in integrations
API Run (Python)
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("haketa/minnesota-dli-license-scraper").call(run_input={"categories": ["Residential_Contractors", "Plumbing", "Electrical"],"statusFilter": "issued","maxRecords": 0})for record in client.dataset(run["defaultDatasetId"]).iterate_items():print(record["licenseNumber"], record["name"], record["city"], record["status"])
API Run (Node.js / TypeScript)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('haketa/minnesota-dli-license-scraper').call({useZip: true,statusFilter: 'issued',maxRecords: 0,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(`Got ${items.length} active Minnesota DLI license records`);const minneapolis = items.filter(r => (r.city || '').toLowerCase() === 'minneapolis');console.log(` of which ${minneapolis.length} are in Minneapolis`);
API Run (cURL)
curl -X POST "https://api.apify.com/v2/acts/haketa~minnesota-dli-license-scraper/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"categories": ["Residential_Roofer"],"statusFilter": "issued","maxRecords": 0}'
How It Works
DLI publishes ten category-specific CSV files plus a single combined ZIP containing all ten, refreshed continuously as licenses are issued, renewed, expired, suspended, or revoked. This actor downloads them directly over HTTPS — no headless browser, no Puppeteer, no Playwright, no API key.
| Source File | URL Pattern | Coverage |
|---|---|---|
MNDLILicRegCertExport_Electrical.csv | https://secure.doli.state.mn.us/ccld/data/MNDLILicRegCertExport_Electrical.csv | Electrical contractors, masters, journeyworkers, installers, PLTs |
MNDLILicRegCertExport_Plumbing.csv | same path with _Plumbing | Master/journeyworker/restricted plumbers, plumbing contractors |
MNDLILicRegCertExport_Residential_Contractors.csv | ..._Residential_Contractors | RBC, RR licenses |
MNDLILicRegCertExport_Contractor_Registrations.csv | ..._Contractor_Registrations | Registered (non-licensed) construction contractors |
MNDLILicRegCertExport_Mechanical_Contractor_Bond.csv | ..._Mechanical_Contractor_Bond | Mechanical/HVAC contractor bonds with surety + bond amount |
MNDLILicRegCertExport_Boiler.csv | ..._Boiler | Boiler operators, inspectors, special licenses |
MNDLILicRegCertExport_High_Pressure_Piping.csv | ..._High_Pressure_Piping | High-pressure pipefitters, pipefitting contractors |
MNDLILicRegCertExport_Residential_Roofer.csv | ..._Residential_Roofer | Residential roofer licenses |
MNDLILicRegCertExport_Manufactured_Home_Installer.csv | ..._Manufactured_Home_Installer | Manufactured-home installers |
MNDLILicRegCertExport_Elevator.csv | ..._Elevator | Elevator constructors, limited mechanics, contractors |
MNDLILicRegCertExport.zip | https://secure.doli.state.mn.us/ccld/data/MNDLILicRegCertExport.zip | Combined archive of all ten files |
Engineering details
- Direct HTTPS GET via
got-scrapingwith a desktop Chrome User-Agent — no headless browser, no API key - Two ingestion modes — individual category CSVs (
categories) or combinedMNDLILicRegCertExport.zip(useZip: true) - In-memory ZIP extraction via
unzipper— archive streamed to/tmp, expanded, each CSV parsed in sequence - 3-attempt retry with linear backoff (2 s, 4 s) on every download
- 100+ entry HEADER_ALIASES map absorbs DLI's column-name drift (
Lic_Number,License_Number,Lic_No,Lic_Nbr,Registration_Number,Certificate_Numberall map tolicenseNumber) - Entity-type routing —
Bus_Pers=B→businessName;P→individualName; mergednamepreserved as convenience - Address merging —
Addr1+Addr2joined with a comma; ZIP normalization strips trailing-0000; date normalization coercesMM/DD/YYYY,YYYY/MM/DD,YYYY-MM-DDto ISO - Status filter —
issuedkeeps active licenses only;allkeeps every status - 200-record batched writes keep memory flat even on the full ZIP
- Graceful partial-save on failure — if DLI hiccups mid-run, every record collected so far is committed before the actor exits non-zero
Input Parameters
{"categories": ["Mechanical_Contractor_Bond", "Residential_Roofer"],"useZip": false,"statusFilter": "all","maxRecords": 200,"requestDelay": 200,"proxyConfiguration": { "useApifyProxy": false }}
Parameter reference
| Parameter | Type | Default | Description |
|---|---|---|---|
categories | array<string> | [] (= all 10) | DLI license categories to scrape — one CSV download per category. Allowed values: Electrical, Plumbing, Residential_Contractors, Contractor_Registrations, Mechanical_Contractor_Bond, Boiler, High_Pressure_Piping, Residential_Roofer, Manufactured_Home_Installer, Elevator. Leave empty to scrape all categories. |
useZip | boolean | false | When true, downloads the single combined MNDLILicRegCertExport.zip archive and extracts all ten CSVs in one round trip. Overrides categories. Recommended for full-export jobs. |
statusFilter | string | all | issued returns only currently active/issued licenses. all returns every record including expired, revoked, suspended, surrendered, and retired. |
maxRecords | integer | 200 | Cap total output across all categories. 0 = unlimited. The default of 200 exists to keep first-time test runs cheap; set 0 for production scrapes. |
requestDelay | integer | 200 | Milliseconds to sleep between category downloads in individual-CSV mode. Has no effect in ZIP mode. |
proxyConfiguration | object | { useApifyProxy: false } | Optional Apify proxy settings. Almost never needed — DLI does not rate-limit or geo-block its public file downloads. |
Output Schema
Every record — regardless of which DLI category it came from — uses the same flat JSON schema. Category-specific fields like bondAmount, bondCompany, phone, and email are present only when the underlying CSV publishes them.
Field reference
| Field | Type | Always Present | Description |
|---|---|---|---|
licenseNumber | string | yes (when present in source) | DLI-issued license, registration, or certificate number — typically a 2-letter category prefix plus digits |
licenseType | string | usually | License type as reported by DLI (e.g., Electrical Contractor, Master Plumber) |
licenseSubtype | string | sometimes | Specialty or sub-class (e.g., Class A, Restricted, Limited) |
sourceCategory | string | yes | One of the ten file categories — Electrical, Plumbing, etc. — useful for downstream routing |
entityType | string | yes | Normalized Business or Personal (derived from DLI's single-letter B/P code) |
businessName | string | when entityType=Business | Firm / company name |
individualName | string | when entityType=Personal | Person name |
name | string | yes | Convenience merged name field (business name or individual name) |
dba | string | optional | Doing-Business-As name |
responsibleIndividual | string | optional | Qualifying person on file for firm/contractor licenses |
status | string | yes | Raw status (Issued, Expired, Revoked, Suspended, Surrendered, etc.) |
issueDate | string | usually | Original issue date in YYYY-MM-DD format |
expirationDate | string | usually | Current expiration date in YYYY-MM-DD format |
address | string | usually | Full mailing address (Addr1 + Addr2 merged) |
city | string | usually | City |
state | string | usually | Two-letter US state abbreviation |
zipCode | string | usually | ZIP code (-0000 suffix stripped) |
county | string | optional | Minnesota county name (when published) |
phone | string | optional | Contact phone number |
email | string | optional | Contact email address |
bondAmount | string | Mechanical_Contractor_Bond only | Surety bond face value (e.g., 25000) |
bondCompany | string | Mechanical_Contractor_Bond only | Issuing surety/bond company |
enforcementAction | string | optional | Active enforcement-action flag |
renewalInProgress | string | optional | Indicator that a renewal is currently being processed |
scrapedAt | string | yes | ISO-8601 timestamp of extraction |
Example: Residential Building Contractor (Business)
{"licenseNumber": "BC999999","licenseType": "Residential Building Contractor","licenseSubtype": "BC","sourceCategory": "Residential_Contractors","entityType": "Business","businessName": "NORTHERN LIGHTS BUILDERS LLC","individualName": null,"name": "NORTHERN LIGHTS BUILDERS LLC","dba": "NORTHERN LIGHTS HOMES","responsibleIndividual": "ANDERSON, ERIK J","status": "Issued","issueDate": "2018-04-12","expirationDate": "2027-03-31","address": "1234 NICOLLET AVE, SUITE 200","city": "Minneapolis","state": "MN","zipCode": "55403","county": "Hennepin","phone": "612-555-0100","email": "info@example-builders.test","bondAmount": null,"bondCompany": null,"enforcementAction": null,"renewalInProgress": null,"scrapedAt": "2026-05-16T09:00:00.000Z"}
Example: Master Plumber (Personal) — abbreviated
{"licenseNumber": "PM099999","licenseType": "Master Plumber","sourceCategory": "Plumbing","entityType": "Personal","individualName": "JOHNSON, MARK D","name": "JOHNSON, MARK D","status": "Issued","issueDate": "2009-06-30","expirationDate": "2026-09-30","address": "789 GRAND AVE","city": "Saint Paul","state": "MN","zipCode": "55105","county": "Ramsey","scrapedAt": "2026-05-16T09:00:00.000Z"}
Example: Mechanical Contractor Bond — abbreviated
{"licenseNumber": "MB099999","licenseType": "Mechanical Contractor Bond","sourceCategory": "Mechanical_Contractor_Bond","entityType": "Business","businessName": "TWIN CITIES HVAC SERVICES INC","name": "TWIN CITIES HVAC SERVICES INC","status": "Issued","city": "Rochester","state": "MN","zipCode": "55901","county": "Olmsted","bondAmount": "25000","bondCompany": "MIDWEST SURETY COMPANY","scrapedAt": "2026-05-16T09:00:00.000Z"}
License Status Reference
DLI's status column carries these canonical values. Use statusFilter: "issued" to keep only the first row; use all for everything.
| Status | Practice/Operate? | Meaning |
|---|---|---|
Issued | Yes | License is currently active and in good standing |
Expired | No | Failed to renew by the deadline; may be reinstated |
Revoked | No | Terminated by Board action |
Suspended | No | Temporarily barred from practice |
Surrendered | No | Voluntarily relinquished (often pre-disciplinary) |
Retired | No | Retired credential — no longer practicing |
Cancelled | No | Administratively cancelled |
Pending | Conditional | Application in progress |
Tip: When
statusFilteris set toissued, the actor performs a case-insensitive exact match onIssued. All other statuses are filtered out and tallied in thetotalSkippedlog line.
Use Cases
Construction & Trades B2B Lead Generation
Suppliers, distributors, software vendors, and equipment rental companies use the Minnesota DLI dataset to build clean targeted lead lists for the Upper Midwest construction market:
- Pull every active Residential Building Contractor in the Twin Cities metro for a roofing-material or siding catalog mail drop
- Segment Mechanical Contractor Bond holders by bond amount to focus on larger HVAC firms for distributor onboarding
- Build territory routes by joining city / county on Hennepin, Ramsey, Olmsted, Dakota, Anoka, Washington
- Enrich CRM accounts (Salesforce, HubSpot, Pipedrive) with current license status, expiration, and responsible-individual fields keyed off the DLI license number
- Target license-class upgrades — sell exam prep, CE, or insurance to journeyworkers approaching the master's exam window
Construction Compliance, Credentialing & Verification
GCs, developers, property managers, REITs, and GL insurers in Minnesota use DLI data to enforce credential rules on every job:
- Pre-bid verification of every sub's electrical, plumbing, HVAC, or roofing license before awarding work
- Automate monthly re-checks for every subcontractor on the master vendor list — flip a record from
IssuedtoExpiredand trigger a procurement alert - Capture
enforcementActionflags so a sub with a fresh administrative penalty surfaces in the next compliance review - Build audit-ready logs with the timestamped
scrapedAtfield for OSHA, MSHA, or insurance carrier audits - Validate bond coverage for mechanical contractors at policy bind by joining
bondCompany+bondAmountwith the GL underwriter's bond minimums
Insurance Underwriting (Contractor & Surety)
GL carriers, surety bond agencies, and workers comp carriers writing Minnesota contractor policies use DLI data to:
- Verify license validity at quote, bind, and annual renewal
- Price residential roofer policies using the standalone Residential Roofer roster (a hard market in MN after multiple hail-driven loss years)
- Cross-check Mechanical Contractor Bond filings against the surety's own book to find book-of-business overlap
- Monitor portfolio risk — flag insureds whose status changes mid-policy from
IssuedtoSuspendedorRevoked - Process bond claims faster with pre-verified licensee information including the responsible qualifying individual
Recruiting, Staffing & Trade Workforce Analytics
Tradesperson recruiters, union halls, and apprenticeship programs use the DLI roster as the closest thing Minnesota has to a complete licensed-trade workforce census:
- Source candidates for journeyworker electrician, plumber, or pipefitter openings by city or county
- Map apprentice-to-journeyworker conversion by tracking individuals across years of snapshots
- Quantify trade workforce supply for IBEW, UA, Sheet Metal, and other local trade unions
- Identify retired or expired credentials as a re-engagement pool for back-to-work programs
Real Estate & Property Development Due Diligence
Homebuyers, real estate attorneys, title insurers, and home inspectors use DLI data to verify the people who worked on a property:
- Validate that the contractor on a permit was licensed at the date the work occurred (combined with archived run snapshots)
- Identify the responsible qualifying individual behind an LLC contractor that has since dissolved
- Run pre-purchase verification on the roofers and remodelers who touched the home — critical in hail-prone Minnesota where roof age and roofer credentials drive insurability
- Support construction-defect litigation discovery with full license history across multiple categories
Market Research, Competitive Intelligence & M&A
Private equity firms and management consultants use DLI data to size and segment Minnesota's construction market:
- Quantify total active residential contractor count in MN over time as a leading indicator for housing-starts demand
- Build target lists for HVAC, plumbing, or electrical roll-up plays with bond amount as a proxy for firm size
- Track new-license issuance rates as an inflow indicator for the regional trades labor pool
- Benchmark license-density per capita across MN metros — Twin Cities vs. greater Minnesota
Government, Regulatory Research & Investigative Journalism
State legislators, academic policy researchers, and investigative reporters use DLI data to study Minnesota's construction-industry regulation:
- Investigate enforcement-action patterns — which categories see the most discipline?
- Map license density vs. housing permit volume to study under- or over-supply of trades labor
- Track post-disaster contractor influx — out-of-state roofer registrations spike after major hail events
- Cover unlicensed contracting by joining DLI records to municipal permit databases and flagging the gap
Marketing, Direct Mail & Litigation
Trade media, direct-mail houses, attorneys, and forensic experts also tap the dataset to:
- Build print-distribution lists for trade magazines targeting MN plumbers, electricians, or HVAC techs
- Power trade-show invite mailings for IBS, AHR Expo, NECA, MCAA regional events
- Verify expert witness credentials before engagement
- Build chronologies of an individual or firm's license history across snapshots
- Pull complete public address-of-record history for service-of-process / subpoena prep
Sample Queries & Recipes
Recipe 1: All active residential contractors statewide (lead list)
{ "categories": ["Residential_Contractors", "Contractor_Registrations"], "statusFilter": "issued", "maxRecords": 0 }
Recipe 2: Full Minnesota DLI export in one shot (fastest path)
{ "useZip": true, "statusFilter": "all", "maxRecords": 0 }
Recipe 3: Active master plumbers (compliance / recruiting)
{ "categories": ["Plumbing"], "statusFilter": "issued", "maxRecords": 0 }
Then filter downstream: [r for r in items if (r.get("licenseType") or "").lower().startswith("master")]
Recipe 4: All Mechanical Contractor Bond filings with surety info
{ "categories": ["Mechanical_Contractor_Bond"], "statusFilter": "issued", "maxRecords": 0 }
Records ship with bondAmount + bondCompany populated.
Recipe 5: Residential roofer roster (insurance underwriting)
{ "categories": ["Residential_Roofer"], "statusFilter": "issued", "maxRecords": 0 }
Recipe 6: Sample 50 records before committing to a full scrape
{ "categories": ["Electrical"], "statusFilter": "all", "maxRecords": 50 }
Recipe 7: Boiler operators + high-pressure pipefitters (industrial workforce)
{ "categories": ["Boiler", "High_Pressure_Piping"], "statusFilter": "issued", "maxRecords": 0 }
Integration Examples
Google Sheets
Set up an Apify schedule running this actor nightly at 02:00 Central, add the "Export to Google Sheets" integration, and receive a fresh MN DLI license CSV in your Sheet every morning ready for VLOOKUPs, pivot tables, and conditional-format dashboards.
Make.com / Zapier / n8n
Use the official Apify connector on any major automation platform. Trigger downstream workflows on new records (alert sales on net-new Twin Cities contractors), status changes (Issued → Suspended opens a compliance ticket), address changes (territory rebalancing), or new enforcement actions (Slack / Teams push).
Power BI / Tableau / Looker
Connect the Apify REST API as a data source and refresh on the actor's schedule. Useful MN DLI dashboards: active license count by category/county, roofer issuance rate (hail-event indicator), mechanical bond size distribution, license expiration calendar (30/60/90-day), contractor density heat maps across Hennepin, Ramsey, Dakota, Anoka, Washington, Olmsted, Saint Louis, Stearns counties.
Postgres / Snowflake / BigQuery / Redshift
Use the Apify webhook integration to POST run results directly to your warehouse ingestion endpoint after every scheduled run. The flat JSON schema maps 1:1 to a single normalized table (license_number as PRIMARY KEY, source_category for routing, dates as DATE, bond_amount as NUMERIC) — no JSON unnesting required.
Salesforce / HubSpot / Pipedrive CRM Enrichment
Trigger an Apify run nightly, then upsert against your Account records keyed on DLI license number. Status-change events can auto-create Tasks, open Cases, or trigger sequences. Pair with the responsibleIndividual field to populate Contact records for the qualifying person at firm-licensed accounts.
Direct Mail & Email Service Providers
Export the dataset as CSV and load into Lob, Click2Mail, Mailchimp, Klaviyo, or HubSpot Marketing — DLI publishes business mailing addresses (and phone/email on some categories) that map cleanly to direct-mail and email-marketing input requirements.
Major Minnesota Markets at a Glance
| Metro / Region | County | Population | Construction Relevance |
|---|---|---|---|
| Minneapolis | Hennepin | 430K (3.7M metro) | Largest construction market in MN; densest electrical, plumbing, HVAC base |
| Saint Paul | Ramsey | 311K | State capital; commercial and government construction |
| Rochester | Olmsted | 121K | Mayo Clinic expansion drives mechanical, electrical, piping demand |
| Duluth | Saint Louis | 87K | Port/industrial center; high-pressure piping, boiler work |
| Bloomington | Hennepin | 89K | Mall of America corridor; commercial GC + HVAC density |
| Plymouth | Hennepin | 80K | Affluent western suburb; high residential remodel + roofer volume |
| Edina | Hennepin | 53K | Tear-down + luxury remodel hotspot |
| Maple Grove | Hennepin | 71K | Fastest-growing NW suburb; new-construction roofer demand |
| Saint Cloud / Mankato | Stearns / Blue Earth | 69K / 45K | Central + south-central MN trades hubs |
| Eagan / Burnsville | Dakota | 68K / 64K | South-metro mechanical / HVAC concentration |
Cost & Performance
| Metric | Value |
|---|---|
| Engine | Direct CSV / ZIP download — no browser overhead |
Runtime (single category, default maxRecords:200) | 5–15 seconds |
| Runtime (full ZIP, unfiltered) | 30–90 seconds |
| Cost per run | Pay-per-event — typically a few cents per full export |
| Pricing model | Pay-per-event (transparent per-record pricing) |
| Data freshness | Live at run time — DLI exports refresh continuously |
| Auth required | None |
| Proxy required | None (Apify proxy supported but not needed) |
| Concurrency | Safe to run multiple parallel filtered configurations |
| Memory footprint | 256 MB default; up to 4096 MB available for the full ZIP scrape |
| Retries | 3 attempts per file with linear backoff (2 s, 4 s) |
| Failure mode | Graceful — partial results are written before non-zero exit |
Compliance, Privacy & Legal Notes
- Public data only — every field in this dataset is published by the Minnesota Department of Labor and Industry at
secure.doli.state.mn.usunder Minnesota Government Data Practices Act (Minn. Stat. Ch. 13) - No PHI / no patient data — these are professional license records, not health information; HIPAA does not apply
- No SSNs, DOBs, or financial account data — only license-related public information is published by DLI
- Address data is the business or practice address of record with DLI (in most cases business mailing address; for individual licensees it may be a home address where that is the address-of-record)
- Email / phone fields are published only where the licensee provided them to DLI and DLI included them in the public export
- The dataset must not be used for unlawful purposes including identity fraud, harassment, stalking, or any use that violates the Minnesota Government Data Practices Act, CAN-SPAM, TCPA, GDPR, CCPA, or other applicable laws
- Robots.txt / ToS — DLI publishes the CSV / ZIP exports for public download; this actor only consumes the documented bulk-export endpoints
- Compliance with direct-mail, telemarketing, and email-marketing law is the responsibility of the data consumer
Important: DLI license data is provided for licensing transparency. Anyone using this dataset for hiring, lending, insurance, or housing decisions should also consider FCRA-style permissible-purpose obligations and verify the most current status directly with DLI before taking adverse action.
Frequently Asked Questions
How fresh is the data?
DLI updates its bulk CSV and ZIP exports continuously as licenses are issued, renewed, expired, suspended, or revoked. Every run downloads the latest version live from DLI's server — typically no more than a few hours stale.
How many records will I get?
Counts shift constantly. Across all ten categories expect roughly tens of thousands of active records plus a longer tail of expired/inactive history when statusFilter: "all". Set maxRecords: 0 for the full export, or maxRecords: 50 for a cheap sampler.
Does this scraper require login or API keys to DLI?
No. DLI publishes the CSV files and combined ZIP as public downloads with no authentication, no captcha, and no API key. You only need an Apify account.
Does this work for other states (Wisconsin, Iowa, North Dakota, South Dakota)?
Not this actor — DLI is Minnesota-specific. Haketa maintains separate actors for other state licensing boards. See the Related Actors section.
Can I use this for license verification at scale?
Yes. Many GCs and insurers run this nightly, diff against the previous day, and trigger alerts on status changes via Make.com / n8n / Slack / Teams.
Are licensee emails and phones included?
Where DLI publishes them. Electrical, Plumbing, and most personal licensee categories include phone and email when the licensee provided them; bond and registration categories often do not. Check the field on each record.
What's the difference between categories mode and useZip mode?
categories mode makes one HTTPS GET per selected category (faster for partial scrapes). useZip: true makes one GET for the combined archive containing all ten CSVs (faster for full exports). Output records are identical.
Why is the default maxRecords set to 200?
To keep first-time test runs cheap. Set maxRecords: 0 for production scrapes.
Does the actor deduplicate?
DLI's exports are already deduplicated by license number. If the same business holds licenses in multiple categories, each license appears as its own record.
What's responsibleIndividual?
For firm/business licenses, MN DLI requires a qualifying individual (the "responsible individual") who holds the personal credential the firm operates under. This field exposes that person's name when DLI publishes it.
Residential_Contractors vs Contractor_Registrations? Boiler vs High_Pressure_Piping?
Residential Contractors hold a true license (RBC or RR). Contractor Registrations cover smaller sub-threshold contractors using MN's registration pathway. Boiler licenses cover operators/inspectors of pressure vessels; High Pressure Piping licenses cover contractors/pipefitters installing high-pressure pipe systems (typically above 15 psi steam / 160 psi water).
Is residential or proxy access required?
No. DLI does not block, geo-restrict, or rate-limit public file downloads. Optional Apify proxy is supported via proxyConfiguration for orgs that require it.
Is there a historical snapshot version of the data?
Not directly — DLI publishes a point-in-time export. To build a history, schedule this actor daily/weekly and archive each dataset run.
Can I get federal contractor (SAM.gov) data through this actor?
No — see the SAM.gov Federal Contractor Entity Scraper and pair the two datasets.
Does this actor work with the Apify Free Plan?
Yes — full functionality on the free tier.
Can I run this on a schedule automatically?
Yes — Apify's Scheduler triggers any cron expression. Combine with webhooks for a fully automated nightly compliance pipeline.
What formats can I export the data in?
JSON, CSV, Excel (XLSX), HTML, XML, RSS — directly from the Apify dataset view. The API also supports JSON Lines.
Does this dataset include workers comp claims or injury data?
No. DLI's bulk regulant exports cover licensing and registration only. Workers comp claim data is handled separately by DLI's Workers Compensation Division and is not part of these public CSV files.
Related Apify Actors by Haketa
If you need licensing data from other US states or related construction/government datasets, these sibling actors all live on the same publisher account:
- Arizona ROC Contractor License Scraper — Arizona Registrar of Contractors licensee database
- Washington L&I Contractor License Scraper — WA Department of Labor and Industries contractor data
- Virginia DPOR Professional License Scraper — Virginia Department of Professional and Occupational Regulation
- Colorado Professional License Scraper — Colorado DORA professional licenses
- California DCA Professional License Scraper — California Department of Consumer Affairs
- Ohio eLicense Scraper — Ohio eLicense central system
- Illinois IDFPR License Scraper — Illinois Department of Financial and Professional Regulation
- NC Licensing Board for General Contractors Scraper — North Carolina general contractor licenses
- Texas Pharmacy License Scraper — TSBP — Texas State Board of Pharmacy
- TTB Alcohol Permittee Scraper — Federal TTB alcohol permittees
- SAM.gov Federal Contractor Entity Scraper — Federal contractor entity records
- BBB Business Scraper — Better Business Bureau profiles for credential cross-reference
Comparison vs. Alternatives
| Approach | Setup time | Data freshness | Cost (full export) | Schema normalization | Compliance audit log |
|---|---|---|---|---|---|
| This actor | < 1 minute | Live | A few cents per run | Built-in (100+ header aliases) | Automatic (scrapedAt) |
| Manual CSV / ZIP download | 5–10 min/day | Live | Free | None | None |
| Custom Python / Node script | 4–8 hours dev | Live | Free + infra | DIY (and re-DIY when DLI drifts) | DIY |
| Paid license-verification API | Hours of vendor setup | Real-time per-lookup | $100–500+/mo, per-lookup | Yes | Yes |
| DLI public records request | Days–weeks | Stale by the time you get it | Variable | None | None |
Why Pay-Per-Event Pricing?
Most data scrapers either charge a flat monthly subscription (you pay even if you don't use it) or per-Compute-Unit (unpredictable). This actor uses pay-per-event pricing, which means:
- You only pay when the actor runs
- Charges scale with how many records you actually save
- Transparent line-item billing inside the Apify Console
- No monthly minimums and no commitment
- Free to evaluate — sample 50 records with
maxRecords: 50for pennies before scaling to the full export
Changelog
| Version | Date | Notes |
|---|---|---|
| 1.0.0 | 2026-05 | Initial public release — 10 DLI categories supported, ZIP and per-category modes, 100+ header aliases, status filter, batched dataset writes, graceful partial-save on failure |
Keywords
Minnesota DLI license lookup · MN contractor verification · Minneapolis license data · Minnesota plumbing electrician license · DLI license search · Minnesota builder license lookup · Minnesota Department of Labor and Industry scraper · MN DLI scraper · Minnesota residential building contractor data · Minnesota residential remodeler license · Minnesota electrical contractor license · Minnesota master plumber lookup · Minnesota journeyworker plumber data · Minnesota high pressure piping license · Minnesota boiler operator license · Minnesota elevator constructor license · Minnesota residential roofer license · Minnesota manufactured home installer license · Mechanical contractor bond Minnesota · MN HVAC contractor surety bond data · Minneapolis contractor database · Saint Paul contractor lookup · Rochester MN contractor data · Duluth contractor license · Bloomington MN contractor list · Plymouth Minnesota contractor verification · Edina MN remodeler license · Maple Grove roofer license · Hennepin County contractor database · Ramsey County license data · Olmsted County contractor list · MN contractor lead generation · Minnesota construction compliance API · DLI CSV download · MN tradesperson workforce data · Apify Minnesota DLI actor · contractor license verification Minnesota · Minneapolis HVAC bond lookup · Saint Paul electrician database · Minnesota contractor enforcement action data · Twin Cities contractor lead list · Minnesota construction permit cross-reference · MN contractor credentialing automation
Support
- Bug reports: Use the Issues tab on the Apify Store page for
haketa/minnesota-dli-license-scraper - Feature requests: Same place, please describe your use case (Wisconsin / Iowa / North Dakota / South Dakota equivalents are popular asks)
- Direct contact: Through the Apify developer profile
If this actor saves you time, a 5-star rating on the Apify Store helps other Minnesota construction, compliance, and insurance teams discover it. Thank you!