# Oregon Company Registry Scraper — SOS Active Businesses (`ai-lead-gen-tools/us-or-sos`) Actor

Search Oregon's active business registry by name or registry number, or stream new registrations since a date. Returns normalized records: business name, entity type, registry number, formation date, jurisdiction and registered-agent addresses. Public Socrata API (data.oregon.gov), no proxy needed.

- **URL**: https://apify.com/ai-lead-gen-tools/us-or-sos.md
- **Developed by:** [Vladimir Chernykh](https://apify.com/ai-lead-gen-tools) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 company scrapeds

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

## Oregon Company Registry Scraper — SOS Active Businesses

Search every active business registered with the Oregon Secretary of State by
name or registry number, or stream new registrations since a date.

The source is Oregon's own public Socrata publication of the business registry
([`data.oregon.gov`](https://data.oregon.gov/resource/tckn-sxa6.json), dataset
`tckn-sxa6` "Active Businesses - ALL"): 1 583 956 rows, a plain JSON API, no
cookies, no captcha, no proxy.

### What you get

One row per **company**, in a stable shape — every field is always present, and
what Oregon does not publish is `null` rather than missing.

| Field | Source | Notes |
|---|---|---|
| `name`, `legalName` | `business_name` | |
| `entityType` | `entity_type` | published verbatim, never guessed |
| `documentNumber`, `sourceId` | `registry_number` | Oregon's own registry number |
| `jurisdiction` | `jurisdiction` | full state name |
| `formationDate` | `registry_date` | ISO 8601; the oldest entity dates to 1850 |
| `principalAddress` | `associated_name_type = PRINCIPAL PLACE OF BUSINESS` | |
| `mailingAddress` | `associated_name_type = MAILING ADDRESS` | |
| `registeredAgent.address` | `associated_name_type` for the agent | name is not published |
| `status` | — | always `ACTIVE`, see the caveat below |

Always `null` / empty because Oregon does not publish them in this dataset: `ein`,
`dbaName`, `statusDate`, `dissolutionDate`, `officers`, `annualReports`,
`lastEvent`.

### Three modes

```json
{ "mode": "search", "searchTerms": ["NIKE"], "maxResults": 100 }
```

```json
{ "mode": "search", "registryNumbers": ["299818"] }
```

```json
{ "mode": "new", "sinceDays": 7, "maxResults": 500 }
```

`mode: "new"` is the one worth scheduling: it returns only companies registered
after the previous run and skips everything already delivered, using a cursor in
the key-value store. Put the actor on a daily schedule and you get a
new-registrations feed instead of paying for the same companies every day.

### One row per company, not per source row

This is the part that matters for the price. Oregon's dataset is a table of
*associated names*: a company with both a mailing and a principal address appears
as two rows, each carrying one address in `address/city/state/zip`.

The actor folds those rows by `registry_number` **before** saving, so a company
with two addresses becomes one record with both addresses filled. `maxResults`
counts companies. The run summary reports `sourceRows` and `mergedRows` so the
merge is auditable on every run.

### Caveat: active businesses only

The dataset is titled "Active Businesses - ALL" and contains no dissolved or
inactive entities. That is why `status` is the constant `ACTIVE` and
`dissolutionDate` is always `null` — the absence of a company here means it is
not currently registered, not that it never existed. If you need historical
entities, this dataset cannot answer that question.

### Cost and speed

**$2.00 per 1 000 companies** ($0.002 per company record). You are charged per
company saved to the dataset — not per request, not per page, not per source row.

513 bytes and ~229 ms per source row on the Apify platform, 1000 rows per
request. Each saved company is charged once; re-reading a company's second address
row is not charged again, because the merge happens before saving.

The economics are possible without a proxy: `data.oregon.gov` is a public JSON
endpoint, so there is no per-request cost to pass on. For `mode: "new"` a scheduled
run typically costs **one request and no charge at all** when nothing new was
registered — the run summary reports `charged` so you can see it per run.

### Input reference

| Input | Default | Meaning |
|---|---|---|
| `mode` | `search` | `search` uses the terms/IDs; `new` returns the incremental feed |
| `searchTerms` | — | case-insensitive substring match on the business name, min 2 chars |
| `registryNumbers` | — | exact Oregon registry numbers, one request each |
| `maxResults` | 100 | cap on saved companies (this is what you pay for) |
| `sinceDays` | 7 | first-run lookback for `mode: "new"` |
| `sinceDate` | — | explicit `YYYY-MM-DD` start for `mode: "new"` |
| `maxPagesPerTerm` | 10 | caps paging per term (1000 rows per page) |
| `includeRaw` | false | attach the merged source rows |
| `maxConcurrency` | 3 | parallel requests |
| `requestTimeoutSecs` | 30 | per-request timeout |
| `proxyConfiguration` | off | not needed; kept for unusual network setups |

### Output

Company records go to the default dataset (see the **Companies** tab). Run
counters — `pushed`, `skippedSeen`, `companies`, `sourceRows`, `mergedRows`,
`requestFailures`, `truncated`, `cursor`, `sourceTotalRows`, `charged` — are
stored in the key-value store under `SUMMARY`.

# Actor input Schema

## `mode` (type: `string`):

"search" looks companies up by the terms and registry numbers you provide. "new" ignores them and returns companies registered after the last run — put the actor on a schedule and you get a new-registrations feed.

## `searchTerms` (type: `array`):

Company names or fragments. Matching is case-insensitive and finds the term anywhere in the name, so "NIKE" also returns "NIKE RETAIL SERVICES INC". Minimum 2 characters.

## `registryNumbers` (type: `array`):

Exact Oregon registry numbers of the businesses, e.g. "299818". Fastest path: one request per number, no name matching.

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

Hard cap on the number of company records saved to the dataset. Counts companies, not source rows — Oregon publishes several rows per company (one per associated address), and they are merged before saving. Each saved record is charged.

## `sinceDays` (type: `integer`):

Used in "new" mode when no explicit start date is set: how many days to look back on the first run. Later runs continue from the cursor saved in the key-value store.

## `sinceDate` (type: `string`):

Explicit start date in "new" mode, format YYYY-MM-DD. Leave empty to use the saved cursor, or the window above on the first run.

## `maxPagesPerTerm` (type: `integer`):

The source returns at most 1000 rows per request. This caps how many pages a single term may page through, so a very broad term cannot run away.

## `includeRaw` (type: `boolean`):

Attach the merged source rows to every record under the `raw` field. Useful for auditing the address merge, roughly doubles the dataset size.

## `maxConcurrency` (type: `integer`):

Parallel requests to the state's API. Keep it modest to stay polite to a public dataset.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout before a retry is attempted.

## `runBudgetSecs` (type: `integer`):

How long this run may take before it stops and returns what it has. Leave it empty (recommended) and the run uses the whole timeout you set for it. Set a number only to stop earlier. Stopping early is not a failure: the run summary reports it and you get the records found so far.

## `proxyConfiguration` (type: `object`):

Not needed: the dataset is a public JSON API and answers directly from Apify. Enable only if you have a specific reason to route the traffic.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "NIKE"
  ],
  "maxResults": 100,
  "sinceDays": 7,
  "maxPagesPerTerm": 10,
  "includeRaw": false,
  "maxConcurrency": 3,
  "requestTimeoutSecs": 30,
  "runBudgetSecs": 205,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per company — several source rows (one per associated address) are merged into it before saving. Every field is always present; values Oregon does not publish are null, never omitted, so the column set is stable between runs. See the Companies tab for the full table.

## `summary` (type: `string`):

Counters for the run: records pushed, source rows read, how many source rows were merged into an existing company, records skipped as already seen, API requests and failures, whether the result was truncated, the incremental cursor, and how many charge events were emitted.

# 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 = {
    "searchTerms": [
        "NIKE"
    ],
    "runBudgetSecs": 205
};

// Run the Actor and wait for it to finish
const run = await client.actor("ai-lead-gen-tools/us-or-sos").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 = {
    "searchTerms": ["NIKE"],
    "runBudgetSecs": 205,
}

# Run the Actor and wait for it to finish
run = client.actor("ai-lead-gen-tools/us-or-sos").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 '{
  "searchTerms": [
    "NIKE"
  ],
  "runBudgetSecs": 205
}' |
apify call ai-lead-gen-tools/us-or-sos --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ai-lead-gen-tools/us-or-sos"
        }
    }
}
```

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/kThPPcu88IHEhiLnf/builds/h3zue5qC6GR2IJaow/openapi.json
