← Guides

How-to

How to run New Mexico Contractor License Lookup & Verify (NM CID)

Step-by-step: the exact input, what each field does, and the real rows that come back from NM CID.

You have a New Mexico contractor to check — a sub you’re about to put on a job, a vendor coming through onboarding, a name on an insurance application. The state’s public registry has the answer, and it is free. The catch is the shape of it: every single search on the NM CID portal makes you read a smudged word out of an image and type it in before it will show you anything. One contractor, that’s ten seconds of annoyance. A list of forty, and you are typing forty CAPTCHAs and copy-pasting forty rows into a spreadsheet by hand, because the portal has no export button. This actor runs those searches for you and hands back the same records as a table.

What you’ll need

Nothing but an Apify account. The actor talks to NM CID directly.

No proxy, no API key, no browser. The one thing this actor needs that most don’t is a CAPTCHA solve — the portal’s search form is gated by a distorted-text image (see it further down this page), and the actor pays an automated solving service to read it on every search. You don’t supply anything for that and you don’t hold an account with the solver — the cost is folded into the actor’s per-search fee, which is why this actor prices differently from the rest of the suite. More on that under “What it costs”.

Step 1 — Open the actor

Open New Mexico Contractor License Lookup & Verify (NM CID) on the Apify Store and press Try for free. The actor’s own page on this site lists its live health and pricing.

The New Mexico Contractor License Lookup & Verify (NM CID) listing on the Apify Store
The listing on the Apify Store. Pricing and the nightly health record are both public.

Step 2 — Fill in the input

The input form for New Mexico Contractor License Lookup & Verify (NM CID) in the Apify Console
The input form, as it appears in the Apify Console.
FieldTypeRequiredWhat it does
businessNamestringnoSearch by contractor business name (partial matches allowed, e.g. ‘Smith’). Combine with other fields to narrow results.
licenseNumberstringnoExact NM CID contractor license number. When set, returns that specific license.
citystringnoFilter results to a business city (e.g. ‘Albuquerque’).
zipCodestringnoFilter results to a business ZIP code.
maxResultsintegernoCap on the number of license matches returned in a single run. The portal returns 20 per page and this actor reads only the first page, so this is capped at 20. (default 20)

Every field is optional, but the actor needs at least one of licenseNumber, businessName, city, or zipCode to have something to search on. Leave them all blank and the run still succeeds: you get one free row saying which fields it wanted, and nothing is charged — no CAPTCHA is solved, so there is nothing to pay for.

Two fields decide almost everything you get back:

licenseNumber is the precise one. Set it and you are asking the registry a closed question: does this license exist, and what is its status? You get that one license back.

businessName is the one people get wrong. It’s a partial match, so Smith is enough to pull back ADAMS & SMITH, INC. — but the portal has rules about what it will accept. Its own form says you must type at least three characters, and at least the first word of the name including any punctuation in it, so a company filed as 3J'S needs the apostrophe. Type the name as the state filed it, not as people say it.

city and zipCode narrow, they don’t broaden. maxResults cannot exceed 20 — that isn’t a throttle we chose, it’s the portal’s page size, and this actor reads the first page only.

A working input:

{
  "businessName": "Smith",
  "maxResults": 20
}

Step 3 — Run it

Press Start. Rows are pushed to the dataset as they’re found, so the run log fills in as it works.

A run does one search: it loads the portal, sends the CAPTCHA image off to be read, submits the form with the answer, and parses the results page. That means the CAPTCHA solve sits in the middle of every run, and it is the slowest part — a solving service is a queue, not a function call, so this actor is not instant the way a plain JSON-API actor is.

A healthy run returns between 1 and 20 rows, deduplicated by license number. Zero rows is a normal outcome, not a broken actor: it means the registry has nothing matching those filters. Before you assume the actor is at fault, check the obvious things — a businessName that doesn’t start the way the state filed it, a city combined with a zipCode that don’t overlap, a license number with a typo. A genuinely broken run looks different: it fails loudly with an error about the CAPTCHA being rejected or the page layout having changed, and charges you nothing for the search.

Step 4 — Read the output

Each run returns a labelled table, not raw JSON:

The output fields returned by New Mexico Contractor License Lookup & Verify (NM CID)
The output view — every field the actor returns, named and typed.

Every row looks like this — a real row from a real run:

{
  "state": "NM",
  "license_number": "396966",
  "business_name": "ADAMS & SMITH, INC.",
  "person_name": null,
  "license_type": "Contractor",
  "status": "Active",
  "issue_date": null,
  "expiration_date": "12/31/2027",
  "city": "LINDON",
  "county": null,
  "address": "1380 W CENTER ST",
  "zip_code": "84042",
  "phone": null,
  "bond_amount": null,
  "bond_company": null,
  "insurance_company": null,
  "matched_by": "business_name",
  "source_url": "https://public.psiexams.com/search.jsp"
}
FieldExample valueMeaning
stateNMAlways ‘NM’ for this actor
license_number396966NM CID contractor license number
statusActiveLicense status (e.g. Active, Cancelled)
license_typeContractorAlways ‘Contractor’ (NM CID’s contractor company category)
business_nameADAMS & SMITH, INC.Licensed business/company name
address1380 W CENTER STPrincipal place of business street address
cityLINDONBusiness city
zip_code84042Business ZIP code

Most people came for two of them: status and expiration_date. Together they answer the only question a verification really asks — is this license good right now? license_number is the column you key on afterwards, because it’s the one stable identifier; business names get re-filed and re-spelled, license numbers don’t.

Watch the nulls. The sample row above is a real one, and half its fields are nullcounty, phone, bond_amount, bond_company, insurance_company, issue_date, person_name. That isn’t missing data on our side. Those fields simply are not on the results page the portal returns; they live behind a per-record detail view that is triggered by JavaScript rather than a URL, so there is nothing to link to and nothing to fetch. If your workflow needs bond or insurance figures, this actor will not give them to you, and no amount of retrying will change that. source_url points at the search portal rather than the individual record for the same reason.

One more thing to expect: the registry stores addresses as filed, which is not always in-state. The sample row’s city is LINDON with a Utah ZIP — an out-of-state company holding a New Mexico license. That’s the source telling the truth, not a parsing bug.

Step 5 — Export it

Open the Dataset tab and export to CSV, JSON, or Excel — or pull the same rows from the API, which is what you want if this is going to run on a schedule.

What it costs

This actor charges twice, and it’s worth understanding why, because it’s the only one in the contractor suite shaped this way.

  • $0.02 per search. Charged once per run, including when the search returns nothing.
  • $0.01 per license match. Charged per row actually returned.

A run that pulls a full page of 20 licenses costs $0.02 + 20 × $0.01 = $0.22. A run that verifies one license number costs $0.03. A run that finds nothing costs $0.02.

That per-search fee exists because every search on this portal has a real marginal cost attached to it: the CAPTCHA has to be read by a paid solving service before the form will submit, and it has to be paid for whether or not the registry has a match waiting on the other side. Charging it up front is the honest version — the alternative is inflating the per-row price so that people running broad searches subsidise people running empty ones. Apify’s platform compute is billed separately, on whatever plan you’re on.

Where the data comes from

This reads NM CID directly. The target is HTML form POST. Every night a canary runs this actor against that live source and diffs the result against a frozen fixture — what “verified” means.

The New Mexico contractor registry isn’t hosted by the state at all — it lives on PSI Exams, the testing vendor that runs the state’s licensing e-services, at public.psiexams.com. It’s an old Java web app: a plain HTML form POST with no API behind it, no per-record URLs, and a distorted-text image CAPTCHA (Java’s SimpleCaptcha) sitting on the search submission. That last part is what makes it awkward. It’s not a Cloudflare or PerimeterX JS-challenge wall — those we can’t beat and we don’t claim to — it’s a picture of a word that a human is expected to read. Clearing it costs money on every search, which is precisely why this actor prices the way it does. The tier and the mechanism are written down on hard targets, next to the nightly pass rate that shows we actually get through.

See it used

If the contractor turns out not to be a New Mexico one, or you don’t yet know which state they’re licensed in, reach for Multi-State Contractor & Trade License Lookup instead — it checks the same contractor across several states in one run. And if you’re building against more than one CAPTCHA-walled registry, South Carolina Contractor License Lookup & Verify (SC LLR) is the other one in the suite that has to solve its way in — a reCAPTCHA there rather than an image, but the same two-event pricing for the same reason.

Last updated 2026-07-13