Washington L&I Contractor & License Scraper
Pricing
from $1.50 / 1,000 results
Washington L&I Contractor & License Scraper
Washington State L&I contractor license scraper & API: search registrations and export license number, status, business name, owner, bond, insurance and expiry. Contractor verification, compliance and B2B lead generation — fast, no login.
Pricing
from $1.50 / 1,000 results
Rating
0.0
(0)
Developer
Haketa
Maintained by CommunityActor stats
0
Bookmarked
4
Total users
0
Monthly active users
a day ago
Last modified
Categories
Share
Washington L&I Contractor License Scraper — License, Insurance, Bond, Owner & UBI Data
The most complete Washington State Department of Labor & Industries (L&I) contractor data extraction tool on Apify. This Washington L&I scraper pulls every registered Construction, Electrical, Plumbing, and Elevator contractor in Washington — joined live with liability insurance, surety bond, and principal-owner data from
data.wa.gov— into clean, structured records ready for compliance, bidding, lead-gen, underwriting, and risk-analytics. No login, no coding. Use it to scrape Washington L&I contractor data at scale in a single run.
Export to JSON, CSV, Excel, or API.
Built for construction sales & lead-gen teams, GCs vetting subcontractors, insurance/surety underwriters, compliance and procurement officers, construction-defect attorneys, and researchers who need verified Washington contractor data in bulk.
🔎 Why scrape Washington L&I contractor data?
The Washington State Department of Labor & Industries (L&I) registers every Construction Contractor (CC), Electrical Contractor (EC), Plumbing Contractor (PC), and Elevator Contractor (LC) doing business in Washington. That registry is the single source of truth for who is legally allowed to contract in WA, what they are licensed to do, who insures and bonds them, who owns the company, and whether the registration is currently in good standing. As of the latest run there are roughly 76,000+ ACTIVE registrations and 160,000+ total records (active, expired, suspended, out-of-business) across the four license types.
This data is gold for sales, compliance, and risk teams — but collecting it manually is painful. L&I's consumer verify tool (secure.lni.wa.gov/verify) only does single-record, JavaScript-rendered lookups: no bulk export, insurance/bond/owner data split across separate sub-pages, specialty codes buried in tooltips, and search pagination that silently truncates past a few hundred hits. The official Socrata SODA API exists, but stitching four separate datasets together by license number is hours of SoQL plumbing.
This Washington L&I contractor license scraper does all of that for you. It talks directly to L&I's open-data API on data.wa.gov, joins four official datasets by contractorlicensenumber, normalises the cryptic field names, formats dates to ISO YYYY-MM-DD, de-duplicates, and returns one flat record per contractor.
Common reasons people scrape Washington L&I:
- Build targeted B2B prospect lists of active contractors by city, trade, and license type
- Verify a subcontractor's license, bond, and insurance before awarding a bid (RCW 18.27)
- Feed insurance / surety underwriting with verified carrier, bond, UBI, and owner data
- Run daily compliance monitoring — catch
ACTIVE → SUSPENDEDtransitions and impaired bonds - Enrich a CRM with UBI, principal-owner names, and contact phone numbers
- Support construction-defect litigation and investigative journalism with licensing evidence
- Power market & workforce research — contractor density, new-entrant velocity, enforcement rates
✨ What this Washington L&I scraper does
- ✅ Scrapes the full WA L&I contractor registry via the official Socrata SODA API on
data.wa.gov— the same backend as the state Open Data portal - ✅ Joins four official datasets in one run by
contractorlicensenumber: General license + Insurance + Bond + Principal/owner - ✅ Server-side filtering (SoQL pushdown) by license status, license type (CC/EC/PC/LC), trade specialty, city, and effective-date range — only matching rows cross the wire
- ✅ Automatic pagination with count-then-page logic, returning hundreds to tens of thousands of de-duplicated records, capped by
maxRecords - ✅ Rich captured fields — identity (
businessName,licenseNumber,ubi,licenseTypeDesc,status), location (address,city,state,zip,phone), classification (specialty1Desc,businessType), dates (licenseEffectiveDate,licenseExpirationDate), insurance (insuranceCompany,insurancePolicyNo,insuranceAmount,insuranceExpiration), bond (bondFirm,bondAmount,bondExpiration,bondImpaired), and owners (primaryPrincipal,principals) - ✅ Latest-wins enrichment — the most recent insurance and bond record per contractor is selected automatically
- ✅ De-duplicated output — each
licenseNumberemitted at most once per run - ✅ ISO date normalisation to
YYYY-MM-DD - ✅ Fast & reliable HTTP-only engine — no headless browser, no CAPTCHA, no login, no proxy. A broad 500-record statewide pull with full enrichment completes in ~20 seconds
- ✅ Export to JSON, CSV, Excel, XML, HTML, or via the Apify API
Only features the code actually implements are claimed above.
🎯 Use cases
- Construction lead generation — building-material suppliers, equipment dealers, and construction SaaS (Procore, Buildertrend, CompanyCam) build targeted lists of active GCs and specialty subs by metro and trade.
- Subcontractor vetting & bid compliance — GCs and owner's reps verify a sub's license is
ACTIVE, the bond is not impaired, and insurance is current before issuing an RFQ or subcontract. - Insurance underwriting & surety risk modeling — carriers pre-fill quotes with UBI, business type, prior carrier, and bond data; flag impaired bonds and bond churn.
- Compliance, procurement & public works — agencies, school districts, and prime contractors verify prime/sub registration and keep audit-ready, timestamped snapshots.
- Legal, litigation & investigation — construction-defect attorneys confirm a contractor was licensed on the contract date, identify the surety for bond claims, and map principal-owner overlap across entities.
- Real-estate development & origination — developers profile contractor density and capacity by submarket before mobilizing on a project.
- Recruiting & staffing — trade-staffing firms build employer lists by specialty and metro for outbound sourcing.
- Market & academic research — labor economists study small-business formation, enforcement, and new-license velocity over time.
📥 Input
Pick from the dropdowns or just press Start — there are no codes to memorise. Every parameter is optional; an empty run returns a fast, diverse 100-record statewide sample.
| Parameter | Type | Description |
|---|---|---|
statuses | array<string> | License status filter. Values: ACTIVE, EXPIRED, SUSPENDED, RE-LICENSED, OUT OF BUSINESS, INACTIVE, SUPERCEDED, PASSED AWAY. Empty = all statuses (case-insensitive). Prefilled with ["ACTIVE"]. |
licenseTypes | array<string> | License type code: CC (Construction), EC (Electrical), PC (Plumbing), LC (Elevator). Empty = all. |
specialties | array<string> | Trade/specialty code filter on specialtycode1 (e.g. 01 General, 02 Residential, AD Plumbing, CD Roofing, SM/6A HVAC, CB Painting). The dropdown lists every trade with a friendly label. Empty = all trades. |
cities | array<string> | City filter (case-insensitive) from a dropdown of major WA cities, e.g. ["SEATTLE","TACOMA","SPOKANE"]. Empty = whole state. |
citiesCustom | array<string> | Advanced free-text list for any WA city not in the dropdown. Merged with cities. |
effectiveAfter | string | Only licenses with licenseeffectivedate >= YYYY-MM-DD. Great for finding newly licensed contractors. Empty = no lower bound. |
effectiveBefore | string | Only licenses with licenseeffectivedate <= YYYY-MM-DD. Empty = no upper bound. |
enrichInsurance | boolean | Join the Insurance dataset (carrier, policy no, amount, expiration). Default true. |
enrichBond | boolean | Join the Bond dataset (firm, amount, impaired status, expiration). Default true. |
enrichPrincipal | boolean | Join the Principal dataset (owners/officers). Default true. |
maxRecords | integer | Cap on total contractor records. 0 = unlimited (the full matching set). Prefilled 100. |
pageSize | integer | Records fetched per Socrata page. Min 100, max 50000, default 1000. |
requestDelay | integer | Milliseconds between API calls (0–5000, default 200). |
Example input
{"statuses": ["ACTIVE"],"licenseTypes": ["CC"],"specialties": ["01"],"cities": ["SEATTLE", "BELLEVUE", "TACOMA"],"effectiveAfter": "2024-01-01","enrichInsurance": true,"enrichBond": true,"enrichPrincipal": true,"maxRecords": 2000,"pageSize": 1000,"requestDelay": 200}
📤 Output
Each row is a flat JSON object with a stable shape regardless of enrichment toggles — fields you didn't request (or that have no source record) are simply null. This keeps downstream ingestion (BigQuery, Postgres, Snowflake, Salesforce upserts) branch-free.
The two examples below are real records from a fresh verification run (statewide ACTIVE, full enrichment):
{"businessName": "WA MOBILE LOCKSMITH","licenseNumber": "WAMOBML743P9","licenseTypeCode": "CC","licenseTypeDesc": "CONSTRUCTION CONTRACTOR","status": "ACTIVE","statusCode": "A","address": "1901 Merrill Creek Pkwy","city": "EVERETT","state": "WA","zip": "98203","phone": "4253285921","ubi": "606306938","businessType": "Limited Liability Company","specialty1": "SQ","specialty1Desc": "Locks, Security and Alarm Equipment","specialty2": null,"specialty2Desc": null,"primaryPrincipal": "Artzi, Segev","licenseEffectiveDate": "2026-10-30","licenseExpirationDate": "2028-10-30","suspendDate": null,"insuranceCompany": "HISCOX INSURANCE COMPANY INC","insurancePolicyNo": "P1074539261","insuranceAmount": "1000000.0000","insuranceExpiration": "2027-10-16","insuranceAgency": null,"bondFirm": "Westfield Insurance Co","bondAmount": "15000.0000","bondExpiration": "Until Canc","bondImpaired": "No","principals": "Artzi, Segev","scrapedAt": "2026-11-02T00:14:25.397Z"}
{"businessName": "STILLWATER 2 LLC","licenseNumber": "STILL2L742PT","licenseTypeCode": "CC","licenseTypeDesc": "CONSTRUCTION CONTRACTOR","status": "ACTIVE","statusCode": "A","address": "7020 147TH AVE NE","city": "LAKE STEVENS","state": "WA","zip": "98258","phone": "4258700249","ubi": "603290508","businessType": "Limited Liability Company","specialty1": "01","specialty1Desc": "GENERAL","specialty2": null,"specialty2Desc": null,"primaryPrincipal": "TOUCHETTE, MICHAEL WYNN","licenseEffectiveDate": "2026-10-30","licenseExpirationDate": "2028-10-30","suspendDate": null,"insuranceCompany": "Nautilus Ins Co","insurancePolicyNo": "NN1802678","insuranceAmount": "1000000.0000","insuranceExpiration": "2027-02-09","insuranceAgency": null,"bondFirm": "Merchants Bonding Co (Mutual)","bondAmount": "30000.0000","bondExpiration": "Until Canc","bondImpaired": "No","principals": "TOUCHETTE, MICHAEL WYNN; TOUCHETTE, ROBERT LEWIS","scrapedAt": "2026-11-02T00:14:25.494Z"}
Output field reference
| Field | Description |
|---|---|
businessName | Legal business name as registered with L&I |
licenseNumber | L&I contractor license number — the primary join key across all four datasets |
licenseTypeCode | CC, EC, PC, or LC |
licenseTypeDesc | Human-readable license type, e.g. CONSTRUCTION CONTRACTOR |
status | License status — ACTIVE, EXPIRED, SUSPENDED, RE-LICENSED, OUT OF BUSINESS, INACTIVE, SUPERCEDED, PASSED AWAY |
statusCode | L&I single-letter status code (e.g. A for active) |
address | Registered business address (line 1) |
city / state / zip | Registered city, two-letter state, and ZIP. state may be out-of-state (e.g. OR, ID) for firms licensed to work in WA |
phone | Registered contractor phone (digits only) |
ubi | Washington Unified Business Identifier — cross-agency 9-digit ID |
businessType | Entity type, e.g. Limited Liability Company, Corporation, Individual |
specialty1 / specialty1Desc | Primary trade specialty code + description |
specialty2 / specialty2Desc | Secondary specialty code + description — present when available (most contractors have one specialty) |
primaryPrincipal | Primary principal/owner name from the General dataset |
licenseEffectiveDate / licenseExpirationDate | License dates, ISO YYYY-MM-DD |
suspendDate | Suspension date — present only for suspended records |
insuranceCompany | Liability insurance carrier — present when the contractor has an insurance record on file (~93% of ACTIVE) |
insurancePolicyNo | Policy number on file with L&I |
insuranceAmount | Coverage amount as reported by Socrata (raw string, e.g. 1000000.0000) |
insuranceExpiration | Policy expiration date, ISO YYYY-MM-DD |
insuranceAgency | Producing agency name — present when available (sparsely populated in source) |
bondFirm | Surety firm issuing the bond |
bondAmount | Bond face amount as reported (raw string, e.g. 15000.0000, 6000.0000, 30000.0000) |
bondExpiration | Bond expiration — often the literal "Until Canc" (until cancelled) rather than a date, mirroring L&I's source |
bondImpaired | Yes / No — whether the bond is currently impaired by a claim |
principals | Semicolon-delimited list of currently-active owners/officers |
scrapedAt | ISO-8601 timestamp of extraction |
Honest note on coverage (from the fresh run): core identity/location/date fields, bond fields, and owner fields populate at 100%; insurance fields at ~93–96% (the remainder genuinely have no insurance record on file yet).
specialty2,suspendDate, andinsuranceAgencyare legitimately sparse/data-dependent — they are not bugs, they're simply empty when the source has no value. Amounts (insuranceAmount,bondAmount) andbondExpirationare faithful passthroughs of the raw Socrata values; apply numeric/date parsing downstream if you need typed values.
🔁 Pagination & scale
The actor is built for bulk. It first calls Socrata count(*) to size the run, then walks the General dataset in $limit/$offset pages (default pageSize 1000) using SoQL ORDER BY licenseeffectivedate DESC, advancing page by page until the result set is exhausted or maxRecords is reached. License numbers are then batched into chunks of 100 for the three enrichment datasets' IN(...) lookups (URL-length safe), and results are joined in process.
maxRecordscontrols the ceiling — set0for the entire matching set (tens of thousands; up to the full ~160K statewide).- Output is de-duplicated on
licenseNumber— a contractor appearing on multiple General rows (e.g. a renewal or second specialty) is collapsed to a single record. - Verified in the latest run: a
pageSize: 250,maxRecords: 500request fetched 2 pages (offset 0, offset 250 = 500 General rows) which de-duplicated to 484 unique contractors in ~21 seconds with full insurance + bond + principal enrichment.
⚙️ How to use
- Open the actor on the Apify Console and click Try for free (or Start). The default input returns ~100 ACTIVE Washington contractors with full enrichment.
- Set your filters —
statuses,licenseTypes,specialties,cities, and date range — and amaxRecordscap (use0for everything). - Run it on-demand, via the API, or on a schedule (Apify Scheduler, any cron).
- Download the dataset as JSON, CSV, Excel (XLSX), XML, or HTML from the dataset tab, or pull it from the dataset API endpoint.
- Integrate downstream — Google Sheets, Make.com, Zapier, n8n, webhooks to Postgres/Snowflake/BigQuery, or CRM upserts (Salesforce/HubSpot) keyed on
licenseNumberorubi.
API run (cURL)
curl -X POST "https://api.apify.com/v2/acts/haketa~washington-li-contractor-license-scraper/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"statuses": ["ACTIVE"],"licenseTypes": ["CC"],"cities": ["SEATTLE"],"enrichBond": true,"enrichInsurance": true,"maxRecords": 500}'
API run (Python)
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("haketa/washington-li-contractor-license-scraper").call(run_input={"statuses": ["ACTIVE"],"licenseTypes": ["CC", "EC"],"specialties": ["01"],"cities": ["SEATTLE", "BELLEVUE", "TACOMA"],"maxRecords": 2000,})for r in client.dataset(run["defaultDatasetId"]).iterate_items():print(r["licenseNumber"], r["businessName"], r["status"], r["city"], r["insuranceCompany"], r["bondFirm"])
💡 Tips
- General contractors =
CC+ specialty01. Washington L&I has no distinctGCcode — filterlicenseTypes: ["CC"]withspecialties: ["01"]to get general contractors; aCCwith a trade specialty (e.g. roofingCD) is a specialty/sub contractor. - Find brand-new contractors with
effectiveAfter(e.g."2025-01-01") — great for untapped prospect lists. - Compliance dashboard: run
statuses: ["SUSPENDED"]with enrichment on, then post-filterbondImpaired == "Yes"for the highest-risk firms. - Fastest bulk pull: set
enrichInsurance/Bond/PrincipaltofalseandpageSizeto5000for a lightning-fast identity-only export, then enrich only the subset you care about. - HVAC lives under two codes — include both
SMand6A. Plumbing specialty isAD. - Join to other WA datasets on
ubi(Department of Revenue, Secretary of State, Employment Security) and onlicenseNumberfor history diffing across scheduled runs.
❓ FAQ
Is scraping Washington L&I data legal?
This actor only reads public open data that L&I publishes at data.wa.gov under Washington's Public Records Act (RCW 42.56), via the official Socrata SODA API built for programmatic access. It does not scrape the secure.lni.wa.gov/verify HTML page. Downstream marketing use (CAN-SPAM, TCPA, GDPR/CCPA) remains the data consumer's responsibility.
How many results can I get, and how fast?
Washington has ~76,000+ ACTIVE and ~160,000+ total contractor records. Set maxRecords: 0 for the full matching set. In the latest verification run, 484 de-duplicated records with full enrichment came back in ~21 seconds; identity-only bulk pulls are faster still.
Do I need proxies or a login? No. The Socrata API is public and needs no authentication — just an Apify account. No proxy and no browser are required. (A Socrata app token is optional if you want higher per-IP quotas.)
What export formats are supported? JSON, CSV, Excel (XLSX), XML, HTML, RSS, and JSON Lines — from the dataset view or the Apify API.
Is there an API? Yes — run the actor and fetch results through Apify's REST API, or trigger it from Make/Zapier/n8n and webhooks.
How fresh is the data? Live at run time. Socrata mirrors L&I's contractor registration system on a regular cadence (near-daily for status changes, periodic for full refreshes).
Can I filter by city, trade, or license type? Yes — all three, plus status and an effective-date range, filtered server-side via SoQL. Numeric filtering on bond/insurance amount isn't an input yet; apply it downstream on the returned fields.
Are contractor emails included? No — L&I does not publish email addresses in the public datasets. Phone numbers and mailing addresses are included.
Why is insuranceCompany sometimes null on an ACTIVE contractor?
Either enrichment was disabled, or the contractor has no insurance record on file in the Socrata dataset at run time (a genuine source gap, ~4–7% of ACTIVE records). For mission-critical verification, also check the official L&I verify page by UBI.
What does bondImpaired = "Yes" mean?
Claims have been filed against the bond and drawn down its value. L&I treats an impaired bond as a compliance trigger — the contractor generally cannot legally bid new work until it's restored.
Does the actor de-duplicate?
Yes. Each licenseNumber is emitted at most once per run, and the latest-effective-date insurance and bond record is selected per contractor.
Can I get historical snapshots?
The actor returns current state. To build history, schedule it (e.g. daily) and archive each Apify dataset run, then diff on licenseNumber.
🔗 Related scrapers
Need licensing data from other US states or regulatory bodies? Check these complementary Apify actors by haketa:
- Arizona ROC Contractor License Scraper — every AZ Registrar of Contractors record
- NC Licensing Board for General Contractors Scraper — North Carolina GCs
- Colorado Professional License Scraper — CO DORA licensed professionals
- Virginia DPOR Professional License Scraper — VA regulated occupations
- California DCA Professional License Scraper — California DCA license boards
- SAM.gov Federal Contractor Entity Scraper — federal procurement registrations
- BBB Business Scraper — Better Business Bureau profiles, ratings, and complaints
Ready to pull verified Washington contractor data? Hit Start with the default input for a free sample, or set your filters and maxRecords: 0 to export the whole state — license, insurance, bond, owner, and UBI data in one run. ⭐ If this Washington L&I scraper saves you time, a 5-star rating helps other compliance, construction, and underwriting teams find it.