# New Business Leads – Newly Registered LLCs & Corporations (`staticxviper/new-business`) Actor

Recently formed entities from verified Secretary of State open-data feeds. Website and email enrichment is optional and billed only when a value is found.

- **URL**: https://apify.com/staticxviper/new-business.md
- **Developed by:** [CJ Moulton](https://apify.com/staticxviper) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 returned entities

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

## New Business Entity Filings

Recently formed entities from verified Secretary of State and state business open-data feeds. Issue #128. The Actor imports `scripts/lead_automation/new_business_pipeline.py`, `new_business_dedupe.py`, `new_business_website.py`, and `new_business_email.py`. Those CLIs are unchanged.

Low-volume research. Each feed is requested with a polite delay. The default pause is 3 seconds and jitters up to 7, which covers the Crawl-delay of 1 on the SODA robots files.

Coverage is narrow on purpose. A state is queried only after its open-data feed is verified in `scripts/lead_automation/new_business_sources.json`. Portals that require login or a challenge are not called.

### What it does

| State | Source | Endpoint |
|-------|--------|----------|
| NY | NY DOS active corporations | `https://data.ny.gov/resource/n9v6-gdp6.json` |
| PA | Pennsylvania registered businesses | `https://data.pa.gov/resource/xvd7-5r2c.json` |
| CO | Colorado business entities (Good Standing) | `https://data.colorado.gov/resource/4ykn-tg5h.json` |

Rows are filtered by `formedSince`, optional counties, and optional entity types, then deduped with the same `dedupe_new_businesses` rules as the CLI. Same state and entity id collapse. A shared name alone does not merge.

`enrichWebsite` accepts an email the registry already published and, when the row has a website, fetches that site if robots.txt allows it. Google web search (`/search`) is not requested. There is no browser and no stealth plugin.

Access ladder, cheapest path first:

1. Direct HTTP.
2. Apify Proxy datacenter (`groups-DATACENTER`) only after a real block (HTTP 401, 403, 429, or a challenge page).
3. A CAPTCHA solve only when that proxy retry is still a challenge, `allowCaptcha` is true, and `maxSpendUsd` covers the solve.

A robots disallow is not retried through a proxy. If the run is still blocked and the proxy budget is gone, or CAPTCHA is off, or no solver is configured, the Actor pushes one `blocked` row and stops. It exits 0. This package does not ship a third-party solver key, so `allowCaptcha: true` still stops unpaid until a solver is configured.

### Input

`fixtures/INPUT.json`:

```json
{
  "states": ["NY"],
  "counties": [],
  "formedSince": "2026-09-01",
  "entityTypes": [],
  "enrichWebsite": false,
  "maxResults": 50,
  "maxRequests": 12,
  "delaySeconds": 3,
  "maxSpendUsd": 5,
  "maxProxyCostUsd": 0.5,
  "allowCaptcha": false,
  "headless": true
}
```

`states` is required. `formedSince` defaults to 365 days before the run. `entityTypes` accepts labels such as `LLC` (matched to "limited liability company"). `maxResults` defaults to 50 and cannot exceed 200. `maxSpendUsd` defaults to 5 and cannot exceed 100. `maxProxyCostUsd` defaults to 0.50 and is clamped to the spend cap. `allowCaptcha` defaults to false. `headless` is kept for the shared Actor contract and has no effect.

### Output

One dataset item per entity, plus a status row when a state is unsupported, blocked, disallowed, or empty. Key-value `SUMMARY` has found, billedBusiness, billedEnrichment, proxyCharges, captchaCharges, mergedDuplicates, and spendUsd.

```json
{
  "entity_id": "EXAMPLE7931947",
  "name": "Example Reid LLC",
  "formation_date": "2026-09-14",
  "type": "DOMESTIC LIMITED LIABILITY COMPANY",
  "address": "NY",
  "county": "Montgomery",
  "state": "NY",
  "website": "",
  "email": "",
  "source_url": "https://data.ny.gov/resource/n9v6-gdp6.json?dos_id=EXAMPLE7931947",
  "sourceStatus": "found",
  "ok": true,
  "error": "",
  "scrapedAt": "2026-10-24T21:00:00+00:00",
  "enrichmentFound": false
}
```

NY location fields are often blank in the DOS extract, so `address` may be only the state. That is the feed, not a geocode. `enrichmentFound` is true only when the enrichment step verified a website or accepted an email.

| sourceStatus | Meaning | business event | enrichment event |
|--------------|---------|----------------|------------------|
| `found` | An entity was returned | yes | only when `enrichmentFound` is true |
| `not_found` | The feed answered and nothing matched | no | no |
| `unsupported` | No verified open-data feed for that state | no | no |
| `blocked` | Direct request was rejected and the run could not continue inside the caps | no | no |
| `robots_disallowed` | robots.txt disallows the feed | no | no |
| `error` | The fetch failed, the request cap was hit, or the input was incomplete | no | no |

### Limitations

- Verified feeds today are NY, PA, and CO. Delaware, Florida, New Jersey, and Texas portals in the source file are not verified open data and are not requested.
- Philadelphia business licenses and Google Maps are not entity-formation feeds and are not queried.
- Colorado results are Good Standing only, because that is the feed's `extra_where`.
- NY addresses are often the service-of-process blank. County is the reliable place field on that extract.
- Enrichment does not invent a website. It only reads a website or email the registry row already has. A New York row with neither stays unenriched.
- `maxResults` applies after dedupe. `truncated` in `SUMMARY` is true when more matches existed than the cap.
- Once `maxSpendUsd` cannot cover the next event, later entities are saved without a success charge. Enrichment fields on those rows are omitted so the add-on is not given away.
- Empty, failed, unsupported, and blocked runs do not emit `business` or `enrichment`.

### Robots and terms

robots.txt is fetched for each host and evaluated with `scripts/lead_automation/crm_enrich_robots.py` (longest match, `User-agent: *`). The verified SODA feeds allow `/resource/` and publish Crawl-delay 1. A shorter `Disallow: /` does not hide `/resource/` when a longer `Allow` exists. Website enrichment uses the same check. A disallowed site is not fetched and is not retried through a proxy.

Terms noted when the feeds were verified (2026-10-05):

- New York: public SODA API. The extract is active entities, monthly, and is not a legal status certificate.
- Pennsylvania: PA Open Data SODA. No login and no per-record fee on this extract.
- Colorado: Colorado Information Marketplace publishes the SOS dataset for public reuse. The query keeps Good Standing rows only.

### Pricing

Pay per event. Prices are the starting Store rates in `.actor/pay_per_event.json`.

| Event | Price | When it is charged |
|-------|-------|--------------------|
| `business` (primary) | $0.05 | A formed entity is returned |
| `enrichment` | $0.03 | `enrichWebsite` found a website or email on that entity |
| `proxy` | $0.01 | One request was sent through Apify Proxy after a direct block |
| `captcha` | $1.00 | A solver returned a token after the proxy retry was still a challenge |

Direct requests are free. A challenge with `allowCaptcha` false, or with no solver configured, stores a `blocked` row and does not emit `captcha`. Dataset items are not a charge event. The platform still applies its start event. Memory default is 1024 MB and the default timeout is 900 seconds.

Proxy uses the datacenter group only, and only after a block. The proxy password is the platform `APIFY_PROXY_PASSWORD`. It is not logged.

### Categories

Set in the Apify Console, not in `actor.json`. Suggested: Business, Other.

### Local run

```bash
cd scripts/apify-scripts/new-business
mkdir -p storage/key_value_stores/default
cp fixtures/INPUT.json storage/key_value_stores/default/INPUT.json
apify run --purge
```

Publish with `APIFY_TOKEN` (`apify push`). That token is not `APIFY_API_KEY`. See `scripts/apify-scripts/README.md`.

# Changelog

This Actor's version history is a separate document: https://apify.com/staticxviper/new-business/changelog.md

# Actor input Schema

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

Two-letter codes. Verified open-data feeds: NY (DOS active corporations), PA (registered businesses), CO (business entities in Good Standing). New York, Pennsylvania, and Colorado are also accepted.

## `counties` (type: `array`):

Optional. Case-insensitive county names. A row with no county is omitted when this list is set. Leave empty for every county the feed publishes.

## `formedSince` (type: `string`):

Keep entities formed on or after this date (YYYY-MM-DD). Default is 365 days before the run.

## `entityTypes` (type: `array`):

Optional. Matched against the feed's entity type. LLC matches a limited liability company. Leave empty for every type.

## `enrichWebsite` (type: `boolean`):

When true, accept a registry email and fetch a registry website if robots.txt allows it. Google web search is not used. The enrichment event is charged only when a website or email is found.

## `maxResults` (type: `integer`):

Cap on entities pushed for this run, after dedupe. Default 50, maximum 200.

## `maxRequests` (type: `integer`):

Cap on requests, including robots.txt and a proxy retry. Default 12, maximum 40.

## `delaySeconds` (type: `number`):

Seconds to wait before each request after the first. Default 3, which covers the Crawl-delay of 1 published by the SODA feeds. The pause jitters up to 7 seconds when the delay is lower than that.

## `maxSpendUsd` (type: `number`):

Stop charging when the next business, enrichment, proxy, or CAPTCHA event would pass this amount. Default 5, maximum 100. Later entities are saved and are not billed.

## `maxProxyCostUsd` (type: `number`):

Cap on proxy events alone. Default 0.50. A direct request is always tried first. Proxy is used only after a block, and only when this cap and maxSpendUsd both still have room.

## `allowCaptcha` (type: `boolean`):

Default false. When false, a challenge after the proxy retry pushes a blocked row and exits without a solve charge. When true, a solve is attempted only if the proxy retry is still a challenge and maxSpendUsd covers the captcha event. This Actor does not ship a paid solver, so an unconfigured solve pushes blocked and is not billed.

## `headless` (type: `boolean`):

Kept for the shared Actor contract. This Actor does not launch a browser.

## Actor input object example

```json
{
  "states": [
    "NY"
  ],
  "formedSince": "2026-09-01",
  "enrichWebsite": false,
  "maxResults": 50,
  "maxRequests": 12,
  "delaySeconds": 3,
  "maxSpendUsd": 5,
  "maxProxyCostUsd": 0.5,
  "allowCaptcha": false,
  "headless": true
}
```

# Actor output Schema

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

Dataset containing all scraped items

## `files` (type: `string`):

Key-value store containing the SUMMARY record 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 = {
    "states": [
        "NY"
    ],
    "formedSince": "2026-09-01"
};

// Run the Actor and wait for it to finish
const run = await client.actor("staticxviper/new-business").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 = {
    "states": ["NY"],
    "formedSince": "2026-09-01",
}

# Run the Actor and wait for it to finish
run = client.actor("staticxviper/new-business").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 '{
  "states": [
    "NY"
  ],
  "formedSince": "2026-09-01"
}' |
apify call staticxviper/new-business --silent --output-dataset

```

## MCP server setup

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

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/3ek2zo4ph2kpg7zCX/builds/2JIYthtvssiqZapXl/openapi.json
