# KYB Company Data Enrichment (`deadwood_data_solutions/company-intel-enrichment`) Actor

Company name in, the public record out: registry identity, UCC liens and stacking, tax warrants, WARN layoffs, FMCSA fleet/phone and NPI — joined by a precision-gated entity resolver. Every field carries its source and confidence. No login, no API key. Billed only for sources that return data.

- **URL**: https://apify.com/deadwood\_data\_solutions/company-intel-enrichment.md
- **Developed by:** [K O](https://apify.com/deadwood_data_solutions) (community)
- **Categories:** Lead generation, Agents, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 company resolveds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Company Intel Enrichment — KYB Lookup on the US Public Record

Give it a list of company names. It returns what the public record says about
each one — who they legally are, what they owe, who they owe it to, whether they
are in distress, and (for carriers and providers) a phone number filed with a
federal agency.

**Every field carries its source, its match confidence and the time it was
retrieved. Nothing is asserted bare.**

Everyone else enriches with contact data bought from a broker. This enriches
with the public record, from the agencies that publish it.

***

### What makes this different

Most enrichment tools tell you an email address. This one answers the question
an underwriter, a lender, a KYB analyst or a B2B seller is actually asking:

> *Is this a real, current entity? How old? Who is the registered agent? How many
> UCC liens are against it, from how many different lenders, and is the debt
> stacked? Any state tax warrants? Any WARN layoff notice? If it's a carrier, how
> many trucks and what's the phone number on file with FMCSA?*

**A false join is worse than no answer.** A wrong lien attached to the wrong
company cannot be detected downstream. So matching runs through a precision-gated
entity resolver — hand-tested against 100 labelled companies at **100% precision
on auto-accepted matches** — and only a match at **0.85 confidence or better** is
ever merged. Anything from 0.60 to 0.85 comes back in `possibleMatches[]` for a
human to decide, and is **never** folded into the record.

***

### Sources

| Signal | Source | Coverage |
|---|---|---|
| **Identity** | CO Secretary of State, OR Secretary of State, NY Department of State, TX Comptroller franchise roll | CO, OR, NY, TX |
| **Liens** | CT UCC filings, CO UCC filings, OR UCC filings, NYC Register (ACRIS) personal-property records | CT, CO, OR, NY |
| **Distress** | NYS Department of Taxation and Finance tax warrants; Texas Workforce Commission WARN notices | NY, TX — queried for every company |
| **Trucking** | FMCSA motor-carrier census | National |
| **Healthcare** | CMS NPI Registry | National |
| **Awards** | USAspending federal contract awards | National — opt-in |
| **Environmental** | EPA ECHO facility compliance, inspections and enforcement | National — opt-in |
| **Securities** | SEC EDGAR filings | National — opt-in |
| **Litigation** | CourtListener federal dockets (RECAP) | National — opt-in |

All of it is official, published, keyless government open data. No login, no
captcha, no scraping of a site that does not want to be read.

The last four are **opt-in**: pass them in `signals` to widen the search. They
are off by default because billing is per source that returns data, and turning
them on by default would change the cost of a run you had already priced. They
were chosen over state licence boards for one reason — a federal award, a
lawsuit, an EPA facility or an SEC filing can attach to a company in any
industry and any state, where a vertical census only answers for its own
vertical.

**Coverage is US-only and state-patchy, and the Actor says so per record.**
Connecticut publishes UCC filings but no joinable business registry; Texas
publishes a business registry but no UCC data at all. When you search a state a
source does not cover, the record carries a `coverageGaps` note — *"no source
covers TX"* — instead of a silent zero. "We looked and found nothing" and "there
was nothing to look in" are different answers and this Actor never prints them
the same way.

***

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `companies` | array | — | **Required.** Up to 25 company names, one per line. Add a state to narrow it: `"ACME LOGISTICS LLC, CO"`. Suffixes and punctuation are handled — `Acme Logistics, LLC` finds `ACME LOGISTICS LLC`. |
| `signals` | array | the five above | Which sources to join: `identity`, `liens`, `distress`, `trucking`, `healthcare`, plus the opt-in `awards`, `environmental`, `securities`, `litigation`. |
| `states` | array | all five | Which state registries to search for identity and liens. National sources are not limited by this. |
| `routing` | string | `auto` | `auto` runs the vertical sources only when the name suggests them. `all` tries everything for every company. |
| `matchMode` | string | `startsWith` | Controls which candidates are *fetched*. What gets *merged* is decided by the scorer either way. |
| `maxItems` | integer | 25 | Cap on companies returned. |

```json
{
  "companies": [
    "STEM CIDER LLC, CO",
    "BUTCHY'S TRUCKING LLC",
    "FIVE STARS CHEF CONSULTING LLC, NY"
  ],
  "signals": ["identity", "liens", "distress", "trucking"],
  "routing": "auto"
}
```

#### Why `auto` routing is the default

Five sources per company could mean five times the latency. It does not, because
a detector decides which vertical sources are worth a request: a trucking-sounding
name goes to FMCSA, a medical-sounding one goes to NPI, and the universal sources
always run. Measured on 50 real companies: `auto` averages **0.5 s per company**
and returns **73 of the 75 source hits** that `all` finds at 0.6 s. Use `all`
when your list is a single known vertical.

***

### Output

One record per company.

| Field | Description |
|---|---|
| `queryName`, `queryState` | What you asked for |
| `resolved` | Whether the state registry produced a confident identity |
| `legalName`, `entityId`, `entityState`, `entityType`, `entityStatus` | Registry identity |
| `formedDate`, `ageYears` | Business age |
| `registeredAgent`, `sosFileNumber` | Registry detail |
| `street`, `city`, `state`, `zip` | Address of record |
| `phone`, `email` | Contact, when a federal source filed one |
| `lienCount`, `activeLienCount`, `lienTotalAmount`, `lienLenders`, `lastLienDate` | UCC exposure |
| `isStacked` | More than one distinct lender — the MCA stacking tell |
| `taxWarrantCount`, `taxWarrantTotal`, `lastTaxWarrantDate` | State tax warrants |
| `warnNoticeCount`, `lastWarnDate`, `warnEmployeesAffected` | WARN layoff notices |
| `dotNumber`, `powerUnits`, `driverCount` | FMCSA carrier profile |
| `npiNumber`, `npiTaxonomy` | Healthcare provider identity |
| `federalAwardCount`, `federalAwardTotal`, `lastFederalAwardDate`, `awardingAgencies` | Federal contract awards |
| `epaFacilityCount`, `epaFacilitiesInViolation`, `epaComplianceStatus`, `epaLastInspectionDate`, `epaFormalActionCount`, `epaRegistryId` | EPA facility compliance |
| `secCik`, `secFilingCount`, `lastSecFilingDate`, `secForms` | SEC filing history |
| `federalCaseCount`, `openFederalCaseCount`, `lastCaseFiledDate`, `suitNatures` | Federal litigation |
| `stackingScore` | 0–100. Grades what `isStacked` only answers yes/no: distinct lenders, live filings, recency |
| `distressScore` | 0–100. Tax warrants, WARN notices, EPA enforcement, loss of good standing, recent litigation |
| `riskFlags` | The named reasons behind the scores, e.g. `stacked-lenders`, `tax-warrant`, `epa-noncompliance` |
| `matchConfidence` | Confidence of the identity resolution, `null` if it was not resolved |
| `sourcesQueried`, `sourcesHit`, `sourcesHitList` | What was asked and what answered |
| `coverageGaps` | Sources with no supply for the states searched |
| `provenance` | **Per field**: `{ source, matchConfidence, retrievedAt }` |
| `possibleMatches` | Scored candidates from 0.60 to 0.85 — surfaced, never merged |
| `acceptThreshold` | The line a match must clear to be merged (0.85) |

A real record, abridged — one company, three sources, nothing bought from a broker:

```json
{
  "queryName": "EL CHAPIN TRUCKING LLC",
  "resolved": true,
  "legalName": "EL CHAPIN TRUCKING LLC",
  "entityState": "CO",
  "entityType": "DLLC",
  "entityStatus": "Good Standing",
  "formedDate": "2025-02-28",
  "ageYears": 1.5,
  "city": "Montrose",
  "state": "CO",
  "zip": "81401",
  "phone": "(970) 209-1788",
  "lienCount": 1,
  "lienLenders": "Alpine Bank, A Colorado Banking Corporation",
  "isStacked": false,
  "dotNumber": "3325232",
  "powerUnits": 1,
  "driverCount": 2,
  "matchConfidence": 1,
  "sourcesHit": 3,
  "sourcesHitList": "identity | liens | trucking",
  "provenance": {
    "dotNumber": {
      "source": "FMCSA motor-carrier census",
      "matchConfidence": 1,
      "retrievedAt": "2026-09-01T00:04:24.675Z"
    }
  },
  "possibleMatches": [],
  "acceptThreshold": 0.85
}
```

A company one year old, one bank lien, one truck, two drivers, and a phone
number filed with the FMCSA. That is an underwriting decision's worth of context
from a company name, and every line of it says where it came from.

***

### How the matching works, and what it refuses to do

The resolver is shared, tested and deliberately conservative. It will refuse an
answer rather than risk a wrong one:

- **A differing legal form is never auto-merged.** `ELITE MOTORCARS LLC` and
  `ELITE MOTORCARS, INC.` share an address in the Colorado registry and are two
  entities with two lien sets. Both are surfaced; neither is merged.
- **Digits are identity.** `ACME 12 LLC` is not `ACME 2 LLC`.
- **Two candidates within 0.05 of each other are a coin flip**, and a coin flip
  is a false positive half the time. Neither is accepted.
- **A shared address is not evidence.** Thirty-seven distinct companies sit at one
  Austin suite — a registered agent's office. The Actor measures how many distinct
  companies occupy a matched address and withholds the address bonus when it looks
  like an agent's office.
- **Never guess across states.** A Colorado registry hit for a Texas company is
  capped below the accept line unless the address corroborates.

***

### Sync to your CRM, Notion, HubSpot, Airtable or Supabase (optional)

Turn on **Sync records to a connected app**, pick the connector you have already
authorised under **Integrations** in Apify Console, and name the tool that creates
one record (`create_page` for Notion, `insert` for Supabase, and so on). Map
fields with `syncFieldMap` if the destination's names differ.

The Actor never sees your third-party credentials — it authenticates to Apify's
MCP proxy with the run's own token and the proxy attaches the rest. **Sync never
triggers a billable event, and a sync failure never fails the run.**

***

### Pricing (Pay-Per-Event)

| Event | Price | When it fires |
|---|---|---|
| `company-resolved` | $0.001 | Once per company record returned |
| `signal-enriched` | $0.002 | Once per source that **actually returned data** |

**You are only billed for hits.** A source that was queried and came back empty
costs nothing. A candidate that only reached `possibleMatches[]` costs nothing —
you did not get an enrichment out of it. A typical company resolving against two
sources costs $0.005.

There is no per-run query charge.

***

### Notes & compliance

- Every source is an official government publication, retrieved from its
  published open-data endpoint. Nothing here is scraped from a site that gates
  access, and no login or API key is required.
- This is public-record data about **businesses**. It is not a consumer report,
  and it must not be used for any FCRA-regulated purpose (credit, employment,
  insurance or tenancy decisions about individuals).
- Registry extracts have publication lag. `retrievedAt` on every field tells you
  when this Actor read the source; it does not tell you when the agency last
  refreshed it.
- Oregon, New York and the Texas franchise roll publish **active-only** extracts,
  so presence is the status signal and absence is not proof of dissolution. The
  Actor labels those statuses honestly rather than printing a bare "Active".

### FAQ

**Does it find every company?** No. Coverage is five states plus two national
registries, and the Actor tells you per record which sources had no supply for
the states you searched.

**Why did it return nothing for a company I know exists?** Most often the state
is not covered for that signal, or the registry spells the name differently
enough that the match landed below 0.85. Check `possibleMatches[]` — a near miss
is reported there with the reason it was not merged.

**Can I lower the confidence threshold?** No, deliberately. The threshold is the
product. A wrong join is undetectable downstream and is worth less than nothing.

**Is this a feed?** No. It is a lookup and it answers in full every time — it
never suppresses a repeat lookup of the same company. Wire it into an onboarding
or underwriting flow and run it per applicant.

# Actor input Schema

## `companies` (type: `array`):

Up to 25 company names, one per line. Add a state to narrow the search and cut false matches: "ACME LOGISTICS LLC, CO". Suffixes and punctuation are handled - "Acme Logistics, LLC" finds "ACME LOGISTICS LLC".

## `signals` (type: `array`):

Which public-record sources to join. You are only billed for sources that actually return data. The four national sources (awards, environmental, securities, litigation) are opt-in: tick them to widen the search.

## `states` (type: `array`):

Which state registries to search for identity and UCC liens. Coverage is US-only and state-patchy - these are the states that publish open, queryable data. Nationwide and single-state sources are not limited by this: FMCSA and NPI are national, and the distress check always queries NYS tax warrants and TX WARN, because a company registered in one state can be pursued for tax or lay off staff in another.

## `routing` (type: `string`):

Auto keeps a five-source lookup fast: registry, liens and distress always run; FMCSA runs for trucking-sounding names and NPI for medical-sounding ones. Choose All when your list is a single known vertical.

## `matchMode` (type: `string`):

This controls only which candidates are fetched. What gets merged is decided by the match scorer either way, and nothing below 0.85 confidence is ever merged.

## `maxItems` (type: `integer`):

Caps how many company records this run pushes.

## `syncEnabled` (type: `boolean`):

Send each enriched record to a connected destination (Notion, HubSpot, Airtable, Supabase, or any Apify MCP connector) in addition to the dataset. Free - this never triggers a billable event. A sync failure never blocks or fails the run.

## `syncDestination` (type: `string`):

Connect the app under Integrations in Apify Console first, then pick it here. Required only if 'Sync records' is on.

## `syncWriteTool` (type: `string`):

Name of the destination's MCP tool that creates one record, e.g. 'create\_page' for Notion, 'insert' for Supabase. Run the dataset-sync-connector Actor in 'list-tools' mode against the same destination if you don't know it.

## `syncFieldMap` (type: `object`):

Maps destination argument names to this Actor's output field names, e.g. {"title": "legalName", "phone": "phone"}. Leave empty to pass each record through unchanged.

## `syncExtraArgs` (type: `object`):

Fixed arguments merged into every sync write call, e.g. {"database\_id": "abc123"} for Notion.

## Actor input object example

```json
{
  "companies": [
    "EL CHAPIN TRUCKING LLC",
    "DEER CREEK DENTAL, CO",
    "FIVE STARS CHEF CONSULTING LLC, NY",
    "DEFIANCE PRO SPORTS, INC, CO"
  ],
  "signals": [
    "identity",
    "liens",
    "distress",
    "trucking",
    "healthcare"
  ],
  "states": [
    "CO",
    "CT",
    "NY",
    "OR",
    "TX"
  ],
  "routing": "auto",
  "matchMode": "startsWith",
  "maxItems": 25,
  "syncEnabled": false,
  "syncFieldMap": {},
  "syncExtraArgs": {}
}
```

# Actor output Schema

## `results` (type: `string`):

All enriched company records from this run as JSON.

## `resultsCsv` (type: `string`):

All enriched company records from this run as CSV.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "EL CHAPIN TRUCKING LLC",
        "DEER CREEK DENTAL, CO",
        "FIVE STARS CHEF CONSULTING LLC, NY",
        "DEFIANCE PRO SPORTS, INC, CO"
    ],
    "syncFieldMap": {},
    "syncExtraArgs": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("deadwood_data_solutions/company-intel-enrichment").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "companies": [
        "EL CHAPIN TRUCKING LLC",
        "DEER CREEK DENTAL, CO",
        "FIVE STARS CHEF CONSULTING LLC, NY",
        "DEFIANCE PRO SPORTS, INC, CO",
    ],
    "syncFieldMap": {},
    "syncExtraArgs": {},
}

# Run the Actor and wait for it to finish
run = client.actor("deadwood_data_solutions/company-intel-enrichment").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companies": [
    "EL CHAPIN TRUCKING LLC",
    "DEER CREEK DENTAL, CO",
    "FIVE STARS CHEF CONSULTING LLC, NY",
    "DEFIANCE PRO SPORTS, INC, CO"
  ],
  "syncFieldMap": {},
  "syncExtraArgs": {}
}' |
apify call deadwood_data_solutions/company-intel-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deadwood_data_solutions/company-intel-enrichment"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3AY2eKLXzG9MHXvpW/builds/7xd3Vl9X6uoTSCivP/openapi.json
