How it works
How a Better Vetters contractor check works.
A check takes a contractor’s name (and optionally state, city, license number, phone, or website), finds the best match in official license records, attaches insurance, permit, and discipline records where they exist, and returns a verdict, a 0 to 100 trust score, and the reasons behind it. Everything comes from official open-data sources, and every response says when each source was last refreshed.
The steps
From request to verdict.
- You send a request.
POST /v1/contractor/checkwith a Bearer API key.nameis required;stateis strongly recommended. - We normalize the name. Accents are folded,
&becomesAND, punctuation and suffixes such as LLC and INC are dropped, andDBAtails are removed. - We find candidates. A typo-tolerant search over the license records finds likely matches, which are then re-scored by name similarity.
- We score the match. An exact license number, phone, city, or website-domain agreement raises confidence. A name alone never exceeds 0.95.
- We attach the records. Insurance and bond (WA and OR), permits (Seattle, Austin, NYC), and discipline (WA suspension dates, NYC DCWP charges), each with a link to the source record where available.
- We score the contractor. A weighted trust score and a verdict, with reason codes that show how the score was built.
- You get one JSON response including
data_freshnessandbilling.
Verdicts and score
How the trust score and verdict are built.
The score is a weighted sum, normalized to 100 over only the components that can speak to this contractor.
| Component | Weight | Notes |
|---|---|---|
| License status and expiry | 50 | Active scores full; expiring within 60 days scores less; expired, suspended, or revoked score little or none. |
| Insurance and bond | 12 | Washington and Oregon only. Insurance 8, bond 4. |
| Discipline | 13 | WA suspension on record, or NYC DCWP guilty findings in the last 3 years. |
| Permit activity | 25 | Only counted when permits are found. Zero permits is neutral, because permit coverage is one city per state. |
Caps: a suspended or revoked license caps the score at 15, an expired license at 45, and an ambiguous match at 70. A fuzzy name-only match below 0.8 confidence, with no license-number or phone match, returns needs_review and is capped at 50.
| Verdict | What it means |
|---|---|
| trusted | Confident match (confidence 0.8+) with a score of 80 or more. License in good standing in the covered sources. |
| likely_ok | Mostly clean, score 60 to 79. Worth a glance, not a blocker. |
| caution | Needs a human look, score 35 to 59. For example an expired license or enforcement findings. |
| high_risk | License suspended or revoked, or a score under 35 in the covered sources. |
| needs_review | The closest record is only a fuzzy name match: confidence below 0.8 with no license-number or phone match. The score is capped at 50. Confirm it is the right business before relying on it. |
| unknown | No reliable match to score, so no judgment either way. |
| not_found | No matching record in the covered sources. A missing record is not proof of a problem. |
| out_of_coverage | The contractor’s state is outside current coverage (WA, TX, NY, OR, CT, PA). Not charged. In Pennsylvania only Pittsburgh is covered. |
Compared with doing it by hand
One call instead of one portal per place.
The issuing agency’s own site is always the authoritative record, and every license result includes a link to it where one exists.
| Looking up each source by hand | One Better Vetters call | |
|---|---|---|
| Sources | A separate portal or dataset for each state and city | Nine official datasets from WA, TX (with Austin), and NYC behind one endpoint |
| Output | Each publisher’s own fields and wording | One JSON shape: verdict, score, reasons, license, insurance and bond, permits, discipline, freshness |
| Name matching | You search and compare names yourself | Normalized, typo-tolerant matching with a 0 to 1 confidence and alternate candidates |
| Use from an agent | Needs browsing or custom parsing | REST and MCP, OpenAPI 3.1 spec, and llms.txt |
| Freshness | Check each page’s own date | Per-source last_refreshed_at and publisher_updated_at in every response |
| Cost | Free to look up; your time | $0.15 per check; 20 free with a new key |
Billing
What a check costs.
$0.15 per check, and every new API key includes 20 free checks with no card. A check that returns not_found or unknown is charged because the search ran. out_of_coverage requests and server errors are not charged. Retries with an Idempotency-Key are free. When credits run out the API returns HTTP 402 with a machine-readable body. Details on the pricing page.
All credit purchases are final and non-refundable, except where applicable law requires a refund, or to correct a duplicate or erroneous charge. Credits do not expire while the account is active.
What it is not
Public records, not a consumer report.
It tells you
- Whether a license record matches, and its status and expiry
- Insurance and bond on file (WA and OR)
- Permits naming the contractor in Seattle, Austin, or NYC
- WA suspension dates and NYC DCWP charges
It does not tell you
- Anything outside WA, TX, NYC, OR, CT, and Pittsburgh, PA
- That a missing record means a problem
- Anything a consumer report would cover
Do not use results for employment, credit, insurance, or tenant-screening decisions. See the Terms of Service. Where each source comes from: Coverage.
Next
Go deeper.
- API docs and quickstartRequest, response, errors, billing
- MCP serverHosted endpoint and local stdio server
- WashingtonL&I licenses, insurance, bond, Seattle permits
- TexasTDLR trade licenses and Austin permits
- New York CityDCWP licenses, charges, and DOB permits
- OregonCCB licenses with insurance and bond
- ConnecticutDCP contractor credentials
- Pennsylvania (Pittsburgh)City of Pittsburgh licenses and permits
- FAQCommon questions
Run your first 20 checks free.
Create a key and send one request.
# Get a key (20 free checks, no card). The key is shown once.
curl -s -X POST https://api.bettervetters.com/v1/keys \
-H 'content-type: application/json' \
-d '{"label":"my-agent"}'