# Secretary of State Business Entity Search + UCC Liens — KYB (`shelvick/business-entity-lien-search`) Actor

Secretary of State (SOS) business registry search, 44 states + DC incl. Sunbiz: company registration lookup, company lookup by name. Look up company registration, registered agent, company officers lookup, UCC liens (28 states), UCC search by debtor name. Verify a business: KYB business verification

- **URL**: https://apify.com/shelvick/business-entity-lien-search.md
- **Developed by:** [Scott Helvick](https://apify.com/shelvick) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 81 total users, 45 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.25 / 1,000 entity record resolveds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Verify a US company and see the liens against it in one call. Search official
state business registries by company name for the normalized entity record —
legal name, ID, type, status, formation date, registered agent, officers — and
get each entity **joined to its UCC / lien filings where covered**. Built for KYB, lending, and
diligence agents that need to answer *"is this company real, in good standing, and
does it already have liens against its assets?"* without stitching two systems
together.

### What this does

- **Business entity search by company name** across 48 states + DC in one
  call — the lookup you'd otherwise run on each state's Secretary of State
  business search, returned as a single normalized record per matched entity
  (company-registration lookup / business-entity verification).
- **Entity → UCC lien join.** Where lien-join coverage is available, each resolved entity is joined
  to the Uniform Commercial Code (secured-transaction) filings recorded against it
  as debtor — filing number, type, status, filed/lapse dates, secured party
  (lender), and collateral. This is the differentiator: entity search and lien
  search, connected, keyed by the debtor's legal name.
- **Normalized fields per entity:** legal name, state entity/registration ID,
  entity type, registration status (e.g. Good Standing, Active, Forfeited,
  Dissolved), formation/registration date, jurisdiction of formation, registered
  agent, principal / mailing address, and officers where the state publishes them.
- **Registered agent lookup and company officers search, included.** Every
  completed record carries the registered agent's name (and address where the
  state publishes it) plus the officer / director roster where published — a
  registered agent search or corporate officers lookup by company name is the
  same single call, with no separate enrichment charge.
- **Multi-state in one request** — pass a company name, optionally scope to a list
  of states, and get one record per matched entity across every covered state.
- **KYB / diligence use cases:** confirm a counterparty is registered and in good
  standing, check whether a borrower's assets are already encumbered before
  lending, enrich a CRM/onboarding record with the legal entity + lien picture,
  screen an acquisition target, or resolve which state(s) a company is registered
  in.
- **Deterministic and agent-callable.** Every result carries a machine-readable
  `status` (`completed` / `no_match` / `not_covered` / `failed`), an explicit
  `lien_coverage` outcome, and structured `field_notes` explaining any field that
  is structurally absent, so an agent can reason about coverage instead of guessing.

Entity search covers **48 states + DC**; lien joins cover **31 states + DC**.

| State | Entity search | UCC lien join |
|---|---:|---:|
| Alaska (AK) | clean | clean |
| Alabama (AL) | clean | clean |
| Arkansas (AR) | clean | clean |
| Arizona (AZ) | extended | none |
| California (CA) | extended | extended |
| Colorado (CO) | clean | clean |
| Connecticut (CT) | clean | clean |
| District of Columbia (DC) | clean | extended |
| Delaware (DE) | extended | none |
| Florida (FL) | clean | clean |
| Georgia (GA) | extended | none |
| Hawaii (HI) | clean | extended |
| Iowa (IA) | clean | extended |
| Idaho (ID) | clean | clean |
| Illinois (IL) | clean | none |
| Indiana (IN) | extended | none |
| Kentucky (KY) | clean | clean |
| Louisiana (LA) | extended | none |
| Massachusetts (MA) | extended | none |
| Maryland (MD) | extended | extended |
| Maine (ME) | clean | none |
| Michigan (MI) | extended | extended |
| Minnesota (MN) | clean | none |
| Missouri (MO) | extended | extended |
| Mississippi (MS) | clean | clean |
| Montana (MT) | extended | extended |
| North Carolina (NC) | extended | extended |
| North Dakota (ND) | clean | none |
| New Hampshire (NH) | on request | none |
| New Jersey (NJ) | clean | extended |
| New Mexico (NM) | clean | clean |
| Nevada (NV) | extended | none |
| New York (NY) | clean | extended |
| Ohio (OH) | extended | extended |
| Oklahoma (OK) | extended | clean |
| Oregon (OR) | clean | extended |
| Pennsylvania (PA) | clean | extended |
| Rhode Island (RI) | clean | clean |
| South Carolina (SC) | extended | clean |
| South Dakota (SD) | extended | none |
| Tennessee (TN) | extended | extended |
| Texas (TX) | clean | none |
| Utah (UT) | extended | none |
| Virginia (VA) | extended | extended |
| Vermont (VT) | clean | extended |
| Washington (WA) | extended | extended |
| Wisconsin (WI) | clean | clean |
| West Virginia (WV) | clean | none |
| Wyoming (WY) | on request | none |

**Don't see your target?** Run the lookup anyway — lookups outside coverage are never charged, return an explicit not-covered result, and tell us where to expand next.

### Why the entity → lien join matters

Anyone can look up whether a company is registered. The expensive, manual part of
KYB and lending diligence is the *second* question — are there already liens
against the company's assets? Answering it normally means finding the entity in one
system, then searching a separate UCC/secured-transactions registry by hand,
matching names, and hoping you found the same legal entity.

This Actor does both and connects them. It resolves the entity from the state
business register, then searches that state's UCC filings by the entity's *core*
legal name and attaches only the filings whose debtor name actually matches —
precise, so a lien against a similarly-named company never gets misattributed. One
call returns the entity plus its lien exposure, ready for a risk decision.

### How it compares to standalone registry tools

| | This Actor | Standalone entity-search tools | Single-state UCC scrapers |
|---|---|---|---|
| Entity search by name | Yes, multi-state | Usually yes | No |
| Normalized schema across states | Yes | Varies | N/A |
| UCC / lien filings | **Joined to each entity** | No | Yes, but not linked to an entity |
| One call for entity + liens | **Yes** | No | No |
| Machine-readable status + coverage notes | Yes | Rarely | Rarely |

The design bet: multi-jurisdiction assembly behind one schema, plus the
entity-to-lien join, is worth more to a diligence workflow than any single registry
scraped in isolation.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `companyName` | string | one of these | — | The company name to search, e.g. `Tesla` or `Acme Holdings LLC`. |
| `companyNames` | array of strings | one of these | `[]` | Up to 20 company names to verify in one run; names are deduplicated case-insensitively before processing. Directory-style qualifiers no registry holds — `formerly …`, `dba …`, `a Div. of …`, and a trailing `(…)` in broad modes — are removed before the search, and each record says which name was searched in `message`. |
| `states` | array of strings | no | all covered | Two-letter US state codes to scope the entity search. Lien joins cover a subset; the run's `COVERAGE` key-value record is authoritative. An unrecognized code returns one uncharged `malformed_input` row naming it, and the states that were spelled correctly are still searched. |
| `includeLiens` | boolean | no | `true` | Join UCC / lien filings where available. A state we cover for entity search but don't join liens for still returns the full entity record (with `liens_checked=false`, `lien_coverage="no_lien_source"`, and a note) — you still get the entity. Set `false` to skip the lien join entirely. |
| `matchMode` | string | no | `contains` | `contains` (broad — find all entities sharing a name), `exact` (legal name equals the query), or `starts` (name begins with the query). In `exact`, punctuation and spacing are ignored, but the legal-form suffix must match — LLC, Inc and Corp are different entities; a name typed with no form at all also matches the one registered name that differs from it only by its form. |
| `maxRecordsPerCompany` | integer | no | `25` | Cap on entity records returned (and billed) per company name, across states. Matches past the cap are dropped (`results_truncated=true`), so a very low cap can miss the entity you want; scope `states` first to bound cost. |

| `reportType` | string | no | `standard` | `standard`, or `due_diligence` for a per-company due diligence check (see below). A `due_diligence` run matches names exactly, takes up to 5 companies and defaults to 3 records per company. |

Provide `companyName` and/or `companyNames` — at least one is required.

### Output

One dataset record per matched entity. Example (`completed`, with a lien join):

```json
{
  "query": "Tesla Electric Company",
  "input_index": 0,
  "status": "completed",
  "state": "CO",
  "legal_name": "Tesla Electric Company LLC",
  "entity_id": "20141524018",
  "entity_type": "DLLC",
  "entity_status": "Good Standing",
  "formation_date": "2014-08-27",
  "jurisdiction": "CO",
  "registered_agent_name": "Felix Keil",
  "principal_address": "9750 Hilldale Dr, Morrison, CO, 80465",
  "officers": [],
  "liens_checked": true,
  "lien_coverage": "joined",
  "ucc_filings": [
    {
      "filing_number": "20192017103",
      "filing_type": "UCC financing statement",
      "status": "active",
      "filed_date": "2019-03-01",
      "lapse_date": "2024-03-01",
      "secured_party": "US BANK NA",
      "collateral": "All equipment now owned or hereafter acquired",
      "debtor_name": "TESLA ELECTRIC COMPANY LLC"
    }
  ],
  "ucc_summary": { "total": 6, "active": 6, "filings_truncated": false },
  "results_truncated": false,
  "source": "Colorado Secretary of State (Business Entities)",
  "source_url": "https://www.coloradosos.gov/biz/BusinessEntityCriteriaExt.do",
  "fetched_at": "2026-07-09T14:03:11Z",
  "field_notes": []
}
```

Key fields: `status` (resolution outcome), `error_class` (machine-routable failure
class on non-completed rows — see the failure taxonomy below), `retry` (what to do
about that class: `later`, `with_changes`, or `never`; `null` on `completed` rows),
`entity_status` (registration standing,
verbatim from the state), `liens_checked` (whether a covered lien source was queried),
`lien_coverage` (`joined`, `no_lien_source`, `unavailable`, or `not_requested`),
`ucc_filings` (the join), `ucc_summary` (`{total, active, filings_truncated}`), `results_truncated` (whether the entity cap dropped matches), and `field_notes` (why
any field is structurally absent). Entity coverage is broader than lien-join
coverage: an uncovered-for-liens state still returns the entity with
`liens_checked=false` and a coverage note. A run also writes a `COVERAGE` key-value
record listing the authoritative live entity-search and lien-join state sets.

### Due diligence check

Set `reportType` to `"due_diligence"` to get, for each resolved company:

- **Current registry status.** The state's status is read at run time (or from the
  state's own published register when it was refreshed within the last 24 hours),
  with `status_as_of`, `status_source_type`, and a normalized `standing` (`active`,
  `not_in_good_standing`, `inactive`, or `unknown` — the registry's own wording stays
  in `entity_status`, and wording we have not mapped is `unknown`, never guessed).
- **Recent financing.** From the UCC join: original financing statements filed in
  the last 12 months and how many distinct secured parties filed them
  (`ucc_stacking`). Where a state does not publish a field needed to count, the count
  is null with the reason — never a zero.
- **Sanctions-list screen.** The legal name and the entity entries of the U.S.
  Treasury SDN and consolidated non-SDN lists, each as of its own publication date
  (`sanctions_screen`). Similar names come back as potential matches with a score
  for you to review; an empty result means no potential matches against the lists
  of those dates.
- **Recorded liens** (`lien_bundle`). The county recorder for the company's
  principal-office ZIP code, searched for the company's legal name; a filing is
  listed only when the lien is against the company. Also California tax liens from
  the state's UCC index and Florida federal liens from the state's federal lien
  registry. Each source says whether it was searched, not covered, or unavailable
  on the run; lien types whose debtor side a recorder's records do not establish are
  counted, not listed. Individuals named on a filing are never listed by name.
- **A link to the source record** where the registry publishes one.

A record becomes a due diligence check (`due_diligence.available: true`) when its
status was confirmed current at run time and it was screened. When a state's status
cannot be confirmed current at run time, you get the standard record at the standard
rate, with the reason in `due_diligence.reason`. Lien coverage never decides the
charge.

#### What a due diligence check is not

- **Not a certificate of good standing.** Order that from the state.
- **Not an identity or TIN check.** It does not confirm who controls the company or
  match a tax ID.
- **Not a consumer report.** It covers business entities only and must not be used
  for decisions about individuals governed by the Fair Credit Reporting Act.
- **Not a complete lien search.** One county recorder and the listed state sources
  are searched; liens recorded elsewhere are not covered.
- **Not a sanctions determination.** Potential matches are leads to review, and no
  result means only no potential matches against the lists of the stated dates.

### Batch API Quickstart

The synchronous endpoint returns dataset items directly. These examples submit the
same three-company batch and branch on both the entity outcome and the independent
lien outcome.

`states` in these examples is illustrative: set it to the states where the companies are registered, or omit it to search every default-covered state.

```bash
curl -X POST "https://api.apify.com/v2/acts/shelvick~business-entity-lien-search/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companyNames":["Acme Holdings LLC","Northstar Freight LLC","Riverbend Manufacturing LLC"],"states":["CO","CT"],"includeLiens":true}'
```

```python
import requests

items = requests.post(
    "https://api.apify.com/v2/acts/shelvick~business-entity-lien-search/run-sync-get-dataset-items",
    params={"token": APIFY_TOKEN},
    json={
        "companyNames": ["Acme Holdings LLC", "Northstar Freight LLC", "Riverbend Manufacturing LLC"],
        "states": ["CO", "CT"],
        "includeLiens": True,
    },
    timeout=900,
).json()
for row in items:
    if row["status"] != "completed":
        print(row["query"], row["status"])
    elif row["lien_coverage"] == "joined":
        print(row["query"], row["ucc_summary"])
    else:
        print(row["query"], "entity delivered; lien outcome:", row["lien_coverage"])
```

```javascript
const url = new URL("https://api.apify.com/v2/acts/shelvick~business-entity-lien-search/run-sync-get-dataset-items");
url.searchParams.set("token", process.env.APIFY_TOKEN);
const items = await fetch(url, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    companyNames: ["Acme Holdings LLC", "Northstar Freight LLC", "Riverbend Manufacturing LLC"],
    states: ["CO", "CT"],
    includeLiens: true,
  }),
}).then((response) => response.json());
for (const row of items) {
  if (row.status !== "completed") console.log(row.query, row.status);
  else if (row.lien_coverage === "joined") console.log(row.query, row.ucc_summary);
  else console.log(row.query, "entity delivered; lien outcome:", row.lien_coverage);
}
```

### Result and correlation contract

Names from `companyName` and `companyNames` are trimmed, deduplicated
case-insensitively, and processed in first-seen order. `input_index` is the zero-based
position in that accepted order. One name may produce several entity rows, so group
records by `query` or `input_index`; `results_truncated=true` means
`maxRecordsPerCompany` excluded additional entity matches.

When multiple names resolve to the same state and entity ID within a run, each
name still receives a `completed` answer. `same_record_as` is set to the earlier
query string whose registry record was already returned and charged; this repeated
answer has no additional charge, including extended records and due-diligence
checks. It does not consume the run's remaining charge budget. Otherwise the field
is null. Entity IDs are compared after trimming surrounding whitespace and
uppercasing only; punctuation and leading zeros remain significant. Records without
an entity ID are not deduplicated. This applies within one run, not across retries.

Retries are at-least-once. Consumers should deduplicate completed records by
`query + state + entity_id`, and terminal misses by `query + state + status`.

### Entity and lien coverage

Entity-search coverage and lien-join coverage are separate capabilities. A completed
entity remains useful when its lien outcome is `no_lien_source`, `unavailable`, or
`not_requested`; inspect `lien_coverage` on every completed row rather than inferring
coverage from the entity state. The run's `COVERAGE` artifact is authoritative.

Records come straight from official state registry and UCC systems, as-is. Registries can lag recent filings and occasionally contain clerk-side errors — for anything with legal weight, verify against the official record at the source.

Clean lien joins cover **AK, AL, AR, CO, CT, FL, ID, KY, MS, NM, OK, RI, SC, and WI**.
Oklahoma's and South Carolina's entity searches are explicit-request, so their clean-tier joins are delivered only when the state is named.
Extended joins cover **CA, DC, HI, IA, MD, MI, MT, NC, NJ, NY, OH, OR, PA, TN, VA, VT, and WA** only when the state is explicitly
requested with liens enabled. Hawaii's, the District of Columbia's and Oregon's joins bill the standard per-record rate. Extended entity searches cover **AZ, CA, DE, GA, IN, LA, MA, MD, MI, MO, MT, NC, NV, OH, OK, SC, SD, TN, UT, VA, and WA** only when explicitly
requested; AZ, GA, IN, MO, LA, NV, MA, SD, UT and DE currently have entity coverage but no lien join.
**NH** and **WY** are searched only when named as well, but they bill the standard per-record rate: each needs a
browser, not a metered connection, and you are not charged for a cost the lookup does not incur.
**IN** returns current legal-name rows, including foreign legal names; assumed, former and reserved names are excluded. Indiana has no lien join. It is searched only when named in `states` and bills the extended per-record rate.
**SC** is searched only when named in `states` and bills the extended per-record rate.
**SD** returns current registry rows with the state's own status. South Dakota has no lien join. It is searched only when named in `states` and bills the extended per-record rate.

#### Coverage by state name

Searched by default at the standard rate: Alabama, Alaska, Arkansas, Colorado,
Connecticut, District of Columbia, Florida (the Division of Corporations' Sunbiz
register, inactive and dissolved entities included), Hawaii, Idaho, Iowa (active
entities only), Illinois, Kentucky, Maine, Minnesota, Mississippi, New Jersey, New Mexico,
New York (Department of State active corporations), North Dakota, Oregon,
Pennsylvania, Rhode Island, Texas (the Comptroller's franchise-tax
register), Vermont, West Virginia, and Wisconsin. A Florida business search, a
New York business entity search, a Colorado business entity search, or a Texas
business entity search is the same call scoped with `states`.

Searched only when named in `states`: Arizona, California, Delaware, Georgia, Indiana, Louisiana,
Maryland, Massachusetts, Michigan, Missouri, Montana, Nevada, New Hampshire,
North Carolina, Ohio, Oklahoma, South Carolina, South Dakota, Tennessee, Utah, Virginia, Washington, and Wyoming. A
California SOS business search or a Virginia SOS business search bills the
extended rate; the coverage table above says which fields each state withholds.

Not covered today: Kansas and
Nebraska. A lookup scoped to one of them returns an explicit not-covered
result and is never charged.

### Calling from an AI agent

Agents are the primary customer. Three ways to call it:

- **Apify MCP server** (`mcp.apify.com`): expose `shelvick/business-entity-lien-search`
  as a tool; the input schema and per-field descriptions are advertised to the model.

- **Apify Python SDK:**

  ```python
  from apify_client import ApifyClient

  client = ApifyClient("<APIFY_TOKEN>")
  run = client.actor("shelvick/business-entity-lien-search").call(
      run_input={"companyName": "Acme Holdings", "includeLiens": True}
  )
  for record in client.dataset(run["defaultDatasetId"]).iterate_items():
      print(record["legal_name"], record["entity_status"], record["ucc_summary"])
  ```

- **REST:** `POST /v2/acts/shelvick~business-entity-lien-search/run-sync-get-dataset-items`
  for a synchronous call, or the async `/runs` endpoint plus polling for large batches.

#### Agent decision guide

- `completed` + `joined` + `ucc_summary.total == 0`: the covered source was searched and returned zero matching filings; check `filings_truncated` before treating the count as exhaustive.
- `no_lien_source`: the entity was delivered, but this state has no integrated lien source.
- `unavailable`: the entity was delivered, but the lien search failed at request time; retry later.
- `not_requested`: the entity was delivered without a lien search because `includeLiens=false`.
- `no_match`: covered entity sources were searched but no entity matched; this says nothing about liens.
- `failed`: the stated search did not complete; never interpret it as no entity or no liens.

Never collapse these outcomes into a single "no liens" result.

#### Failure taxonomy: when to retry

Every non-`completed` record carries `error_class`, a stable machine-routable value.
Branch on it rather than on the prose in `error`, which may be reworded at any time.

Retry the same request later, unchanged:

- `source_error` — the official registry could not be reached. Registries have
  scheduled maintenance and unplanned outages, sometimes lasting a day; this is the
  source being unavailable, not a problem with the query.
- `source_walled` — the registry refused an automated search. Usually temporary.
- `budget_exhausted` — the run deadline arrived before this company finished, or the
  run reached its Max cost per run before this company (or some of its records) could
  be delivered. For the cost case, raise Max cost per run first.
- `lien_join_error` — the entity was found, but its lien filings could not be
  retrieved; the entity fields on other rows for this company are still good.

Do not retry unchanged; the answer will not differ:

- `no_match` — the registry answered and no entity matched the name.
- `not_covered` — the requested state is not yet supported.
- `internal_error` — the search could not be completed.

`error_class` is `null` on `completed` records. Each row also carries `retry`, the
same advice as a single value (`later` / `with_changes` / `never`, `null` on a
result), so an agent can route without holding this table.

**Retry only what failed.** A `failed` row is free, but re-running the whole company
re-searches — and re-bills — every state that succeeded. Each `failed` row's `message`
names the one-state retry (`states: ["MN"]`), and the run's key-value store `OUTPUT`
carries `failed_states` (state → companies) plus `retry_input`, a ready-to-submit input
that fetches only the missing states. The run status message summarizes the same.

### Pricing and billing outcomes

Billing occurs only when a completed entity record has first been delivered to the
dataset. `no_match`, `not_covered`, and `failed` rows are never billed. A completed
entity remains billable when a lien join was not requested, is not covered, or was
temporarily unavailable; the entity result itself was still delivered.

A run stops delivering at its Max cost per run. A completed record is delivered only
if it can be charged; once the limit is reached, the rest come back as uncharged
`failed` rows with `error_class: "budget_exhausted"` (one per company and state,
or one per company not yet searched), `OUTPUT.budget_clamped` counts the records
withheld, and `OUTPUT.retry_input` is a ready-to-submit input naming the companies
and states that were withheld. Raise Max cost per run before re-running it. Where a
company and state had some records delivered before the limit, that re-run returns
(and charges for) those records again. The Store **Pricing** tab is the
authoritative source for current rates and discounts.

A `due_diligence` run bills one due diligence check per record that earned it, and
that record bills nothing else. Companies the run's maximum charge cannot cover are
not searched; each gets an uncharged row saying so.

### Batch size, latency, and partial runs

A run accepts at most 20 unique company names and processes up to four companies
concurrently. Clean-path lookups generally finish in seconds to under a minute;
explicit extended coverage may need a longer startup and can take several minutes.
Split larger batches across runs.

Completed records are pushed and charged as each company finishes. If the internal
run deadline arrives, completed work remains available, each unfinished company gets
an uncharged `failed` row with `error="run_deadline_exceeded"`, and
`OUTPUT.partial=true` with `OUTPUT.pending_companies` showing the count.

### Schedules and retries

For recurring batch execution, use scheduled asynchronous runs and consume each run's
dataset after completion. Retry whole or partial inputs using the correlation keys
above. Searches are point-in-time lookups, not monitoring or change alerts.

### High-volume needs

Running a large KYB pipeline or planning sustained batch volume? Open a conversation
on the Actor's **Issues** tab with your target states and workload shape. Volume
feedback directly drives state-coverage priorities.

### Behavior

- **Statuses:** `completed` (entity found + normalized), `no_match` (covered states
  searched, nothing matched — a clear "not found"), `not_covered` (a requested
  state isn't supported yet), `failed` (a covered state's registry could not be
  reached; see `error`). Only `completed` is billed.
- **Graceful degradation:** if one state's registry is briefly unavailable, that
  state yields a `failed` record while the rest of the run completes — a source
  hiccup never fails the whole batch or charges you for it.
- **Source freshness:** lien data is only as current as each state's published
  index. Florida lien data is indexed from the state's official public UCC data
  files on a periodic refresh cycle, so a very recently filed Florida lien may not
  appear until the next refresh; those files publish filing images rather than
  collateral text. Kentucky's UCC index in particular publishes on roughly a
  two-week lag, so a very recently filed Kentucky lien may not appear yet. Iowa's
  and Rhode Island's lien searches cover active filings plus those lapsed within
  roughly the past year — older lapsed history is not published, so lien totals
  there describe the current picture rather than all history. Rhode Island
  publishes no per-filing lapse date; each filing's `status` is derived from the
  state's own active/lapsed indexes and its filing history instead.
- **South Carolina liens:** the free UCC grid publishes filing number, type, filed
  date and lapse date only; `status`, `secured_party` and `collateral` are not
  exposed and arrive as null.
- **Missouri liens:** the state's UCC index lists filing number, debtor, filed date,
  status and lapse date, but not the secured party. For up to 10 active filings per
  company (newest first) the secured party is read from the filing image itself and
  marked `secured_party_source: "filing_image"` with a `secured_party_confidence`
  (0-1); a read that is not confident enough arrives as null with a `field_notes`
  entry. Only the secured party is taken from the image; collateral is not published.
- **Current-only sources:** a few states publish only their *currently registered*
  entities — dissolved and withdrawn ones are absent from the source. For those, a
  `no_match` means no currently registered entity matched the name, not that it never
  existed; the record carries a `field_notes` entry saying so. Iowa reports every
  returned entity as active (its bulk file has no status column); Alaska keeps each
  entity's real standing (e.g. `Good Standing`, `Non-Compliant`) but still omits
  dissolved entities. Florida is the opposite: its register includes inactive and
  dissolved entities with their real status, so an FL record can come back `Inactive`
  — useful when you need to know a counterparty was dissolved rather than never
  registered. Mississippi also includes the full register and preserves state standing
  values such as `Good` and `Dissolved`, but its source does not publish registered-agent
  or officer data. Rhode Island Department of State — Corporate Database and West
  Virginia Secretary of State — Business Organization Search also cover the full
  register, including inactive and dissolved entities. Rhode Island searches match an
  entity's current legal name, so find a renamed company under its current name or check
  the state portal at https://business.sos.ri.gov/CorpWeb/CorpSearch/CorpSearch.aspx .
- **Kentucky assumed and former names:** Kentucky Secretary of State (Business Entity
  Search) records cover the full register, including inactive entities. A search that
  matches an entity's assumed name (DBA) or former name returns the entity under its
  legal name, with a `field_notes` entry naming the name that matched.
- **Rhode Island status:** `entity_status` can be null when the Rhode Island Department
  of State — Corporate Database does not return a current standing for the entity; the
  record carries a `field_notes` entry saying so, and you can verify the entity directly
  at https://business.sos.ri.gov/CorpWeb/CorpSearch/CorpSearch.aspx .
- **Telemetry:** to improve coverage and reliability, this Actor reports anonymous
  usage metrics and diagnostic events to the developer — run outcome counts, the
  states queried, and, only when something goes wrong, the relevant input fields.
  No account identifiers are collected, and telemetry never affects a run.

### FAQ

- **Which states are covered?** Entity search covers 48 states plus DC: AK, AL, AR, CO, CT,
  DC, FL, HI, IA, ID, IL, KY, ME, MN, MS, ND, NJ, NM, NY, OR, PA, RI, TX, VT, WI, and WV by default, plus AZ, CA, DE, GA, IN, LA, MA, MD, MI, MO, MT, NC, NH, NV, OH, OK, SC, SD, TN, UT, VA, WA, and WY when named in `states`. Clean lien joins cover AK, AL, AR, CO, CT, FL,
  ID, KY, MS, NM, OK, RI, SC, and WI; Oklahoma's and South Carolina's entity searches are explicit-request, so their joins are delivered only when the state is named. Extended joins cover CA, DC, HI, IA, MD, MI, MT, NC, NJ, NY, OH, OR, PA, TN, VA, VT, and WA only when
  explicitly requested; Hawaii's, the District of Columbia's and Oregon's joins bill the standard per-record rate. Entities from other covered states return without a lien
  join, clearly flagged with `lien_coverage="no_lien_source"`. The run's `COVERAGE`
  record is the authoritative live list as coverage expands.
- **Why do AZ, CA, DE, GA, IN, LA, MA, MD, MI, MO, MT, NC, NV, OH, OK, SC, SD, TN, UT, VA, and WA bill extended when I name them?** They are
  extended-coverage states billed at the extended per-record rate (see the Pricing
  tab). Keeping them out of the search-everywhere default means you are never
  charged the extended rate for a state you didn't ask about — naming them in
  `states` is the opt-in.
- **Why didn't my Massachusetts search find a company I know is registered?**
  Massachusetts matches from the start of the name, so a search for a word in
  the middle of a company's name will not find it — search the beginning of the
  legal name instead. Records and empty results from MA both say so.
- **Can I use this for a registered agent lookup?** Yes — every completed record
  carries `registered_agent_name` (and the agent's address where the state
  publishes it), so a registered agent lookup by company name is a single call.
- **Can I look up a company's officers and directors?** Where the state publishes
  them, yes — `officers` carries the roster (name and title) on the same record,
  so a corporate officers search costs nothing beyond the entity record. States
  that don't publish officers say so explicitly in `field_notes`.
- **What if a company has no liens?** For a lien-covered state, `liens_checked` is
  `true`, `lien_coverage` is `joined`, and `ucc_summary.total` is `0` with `filings_truncated=false` —
  a confident "no liens found," not a gap. If the state lacks a lien join,
  `liens_checked` is `false`, `lien_coverage` is `no_lien_source`, and the entity
  record includes a coverage note instead.
- **How precise is the entity→lien match?** Filings are attached only when the UCC
  debtor's core legal name matches the entity's, so a lien against a
  similarly-named company is not misattributed. Descriptive words (Company, Trust)
  are kept; only legal-form suffixes (LLC, Inc.) are normalized away for matching.
- **Can I verify a list of companies at once?** Yes — pass up to 20 names in
  `companyNames`; each is resolved independently.
- **Is this official / real-time?** Data comes from the official state registries
  — Secretary of State / corporations-division records and state open-data
  portals; it is as current as those sources publish.

### What this doesn't do

- **No paywalled or login-gated sources.** It uses only free, public registry data;
  it does not cross any state's paid detail lookup.
- **No consumer / personal-PII lookups.** Business-entity public records only — not
  people search, not skip tracing.
- **No document images or filed PDFs** — structured fields, not scanned filings.
- **Not every state yet.** Uncovered states return `not_covered`; it is not a
  50-state guarantee.
- **Not a monitoring service** — it answers a point-in-time query, it does not watch
  an entity for changes.

Use a dedicated people-search or identity-verification tool for individuals, a
document-retrieval service for filed images/PDFs, and a court-records or
enforcement tool for litigation history — this Actor is for the business-entity
record and its UCC lien exposure.

Data is sourced from public government registries and open-data portals and is
provided for lawful business, compliance, and diligence use; verify against the
official source of record before relying on it for a regulatory decision, and use
it in accordance with the Apify Terms of Service and applicable law.

### Related Actors

| Actor | Use it when |
|---|---|
| [Enforcement Record Profiler](https://apify.com/shelvick/enforcement-record-profiler) | you've verified the entity and want its enforcement and violation history from official US sources |
| [County Property Records API](https://apify.com/shelvick/county-property-records) | the entity owns US real estate and you need owner, assessed value, and tax records |
| [Property Deed & Lien Records Search](https://apify.com/shelvick/property-deed-records) | you need what the entity recorded at county recorder offices — deeds, mortgages, and real-estate liens by company name |
| [Company Registry Search — Companies House, SIRENE +9 (KYB)](https://apify.com/shelvick/global-company-registry-search) | the company is registered outside the US and you need its entry in the UK, French, Nordic, Baltic, Swiss, Czech, Polish, Australian or Singapore official register — same normalized record shape, no API key |
| [Property Owner Portfolio — Property Records by Owner Name](https://apify.com/shelvick/property-owner-portfolio) | you need the owner's public-record real-estate footprint — parcel counts and assessed totals by county — rather than registration and UCC lien detail. |

# Actor input Schema

## `companyName` (type: `string`):

Required unless you use Company names (batch). Enter the business / company name to search for, e.g. "Tesla" or "Acme Holdings LLC". Matched against official state business registries. Use Name match mode to control exact vs. partial matching. Directory-style qualifiers a registry never holds ("formerly ...", "dba ...", "a Div. of ...") are removed before the search; the record's message names what was searched.

## `companyNames` (type: `array`):

Required unless you use Company name. Add multiple company names to look up in one run (one per line), e.g. "Acme Holdings LLC" — useful for verifying a list of counterparties. Each name is resolved and billed independently. Directory-style qualifiers a registry never holds ("formerly ...", "dba ...", "a Div. of ...") are removed before the search; the record's message names what was searched.

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

Two-letter US state codes to scope the search, e.g. \["CO","CT","NY"]. Empty searches all default-covered entity registries. Extended entity searches selected only when named here bill the extended per-record rate: AZ, CA, DE, GA, IN, LA, MA, MD, MI, MO, MT, NC, NV, OH, OK, SC, SD, TN, UT, VA, WA. PA, NY, IA, NJ and VT are standard entity states whose lien join bills extended only when delivered; OR, HI and DC are the same shape but their lien join always bills the standard rate. NH and WY are searched only when named but bill the standard rate. Entities with no lien source still return in full. The COVERAGE key-value-store output is the authoritative live list.

## `includeLiens` (type: `boolean`):

When true (default), each resolved entity is joined to UCC / lien filings where available. Set false for an entity-only lookup. Lien joins outside the default clean tier run only for states named in `states`. Clean-tier lien joins cover AK, AL, AR, CO, CT, FL, ID, KY, MS, NM, OK, RI, SC, and WI; Oklahoma's and South Carolina's entity searches are explicit-request, so their joins are delivered only when the state is named. MT, CA, PA, NY, IA, TN, NC, OH, OR, VA, WA, MI, NJ, MD, VT, HI, and DC joins are explicit-request coverage; PA, NY, IA, NJ, and VT bill the extended rate only when the join is delivered, OR, HI, and DC joins bill the standard rate, and the other listed joins bill with their extended entity record. States not named, and states without a lien source, return entity-only with lien_coverage='no_lien_source' (a field note says when naming the state would add liens). The COVERAGE key-value-store output is the authoritative live list.

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

How to match the company name against the registry. 'contains' (default) is broadest — good for discovering all entities sharing a name. 'exact' returns only entities whose legal name equals the query. 'starts' matches names beginning with the query. In 'exact', punctuation and spacing are ignored, but the legal-form suffix must match — LLC, Inc and Corp are different entities; a name typed with no form at all also matches the one registered name that differs from it only by its form.

## `maxRecordsPerCompany` (type: `integer`):

Cap on entity records returned (and billed) per company name, across all searched states. Default 25. Matches past the cap are not returned and the row carries results_truncated=true, so a very low cap can drop the entity you are looking for: in 'contains' mode one name often matches several registered entities in a single state. To bound cost, scope `states` first and use a very low cap only when any one match will do.

## `reportType` (type: `string`):

'standard' (default) returns registry records with their lien joins. 'due_diligence' adds, per resolved company: the registry status read at run time with its timestamp and a normalized standing, a recent-financing summary from the UCC join, a sanctions-list screen, and a recorded-lien bundle. It matches names exactly, allows up to 5 companies per run and defaults to 3 records per company. A record whose status cannot be confirmed as current at run time is returned as the standard record at the standard price, with the reason in its due_diligence field. Not a certificate of good standing, not an identity or TIN check, and not a consumer report.

## Actor input object example

```json
{
  "companyName": "Tesla",
  "companyNames": [],
  "states": [
    "CO",
    "CT"
  ],
  "includeLiens": true,
  "matchMode": "contains",
  "maxRecordsPerCompany": 25,
  "reportType": "standard"
}
```

# Actor output Schema

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

Normalized business-entity records for this run (one per matched entity, joined to UCC lien filings where covered; inspect lien_coverage on each record).

## `output` (type: `string`):

Run-level outcome counts, partial-run status, pending companies, and budget-clamped records.

## `coverage` (type: `string`):

Authoritative entity-search and lien-join coverage used for this run.

# 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 = {
    "companyName": "Tesla",
    "companyNames": [],
    "states": [
        "CO",
        "CT"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("shelvick/business-entity-lien-search").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 = {
    "companyName": "Tesla",
    "companyNames": [],
    "states": [
        "CO",
        "CT",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("shelvick/business-entity-lien-search").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 '{
  "companyName": "Tesla",
  "companyNames": [],
  "states": [
    "CO",
    "CT"
  ]
}' |
apify call shelvick/business-entity-lien-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shelvick/business-entity-lien-search"
        }
    }
}
```

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/xUZKQ16dDFL9qSS5t/builds/y8hEBbJGDHBIhPeH3/openapi.json
