# Chicago Business Licenses - Full History & Active (`j0401/chicago-business-licenses`) Actor

Chicago business licenses (public open data, 1.2M records, full history since 2002): every license event with licensee and DBA, address / ward / community area, class, business activity and the application-to-expiration lifecycle. Filter by class/status/name/ward/date or pull the live slice.

- **URL**: https://apify.com/j0401/chicago-business-licenses.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.06 / 1,000 chicago business license records

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

## Chicago Business Licenses - Full History, Since 2002

The City of Chicago publishes its **entire business-license register** - every license event back to 2002, live - as open data. This actor turns that file into a **charged-per-record business-intelligence tool** over 1.2 million records.

**Built for:** B2B sales and territory teams finding businesses by class and neighborhood, commercial real-estate and retail-site research, food-and-beverage and liquor diligence, vendor onboarding (`is this restaurant actually licensed?`), and market analysis of where Chicago business is opening and closing.

### What it covers

**1,206,033 license records** since 2002, live (new issuances land within the week). This is the **full history**, not the current-license view: every original issue, renewal, location change and expiration is a row.

The register is dominated by a few classes:

| License class | Records |
|---|---|
| Limited Business License | 482,012 |
| Retail Food Establishment | 187,525 |
| Regulated Business License | 73,484 |
| Tobacco | 58,113 |
| Consumption on Premises (incidental) | 41,030 |
| Home Occupation / Home Repair / Tavern / Package Goods / Manufacturing | the long tail |

Each record carries the business, the place, and the full application lifecycle:

- the **legal name** and the **DBA** (they differ - `PETSMART LLC` doing business as `PETSMART, Inc. #2525`)
- the **address** with **ward**, **community area**, **neighborhood**, police district and latitude/longitude
- the **license code and class**, plus the **business-activity array** (a single license carries a pipe-delimited set of activities, e.g. "Provide Animal Grooming Services | Operate a Pet Shop / Sales of Animals | Retail Sales of General Merchandise")
- **status**, and the full timeline: application created -> requirements complete -> payment -> conditional approval -> approved for issuance -> license start -> **date issued** -> **expiration**

Licensing clusters where Chicago business is: **ward 42** (the Loop and Near North, 104,268 records), ward 34, ward 27, ward 2 - and by community area, **Near North Side** (97,454), **Loop** (92,223), Near West Side, West Town, Lake View.

### The live slice is 4.6% of the file

Only **~54,000 records are live right now** - which is the single most important thing to understand about this dataset. A license is active when it is **not terminal** (`license_status` AAI or AAC) **and** its **expiration date is still ahead**. Read the status letters alone and you get 1.2 million "active" rows; read the file as "current licenses" and you are 95% wrong. (This figure drifts down as expirations pass - treat it as an order of magnitude, not a constant.)

So the actor separates the two questions cleanly. `rows` (the default) gives you the **full history** - every license a business ever held - while `active` gives you **who is licensed right now**, and `recent` gives **this month's issuances**. Each renewal re-appears as its own row, so "every license this business held" and "is this business licensed today" are genuinely different queries, both supported.

One business can hold several live licenses at once, so the live slice is counted in licences, not businesses: the ~54,000 active rows resolve to roughly **37,000 distinct businesses**. Both counts are in the output - `licenseNumber` and `accountNumber` - so you can pick the unit your question is about.

Status is the city's 5 exact codes: **AAI** (active) 1,125,751 / **AAC** (active, cancellation pending) 78,804 / **REV** (revoked) 1,464 / REA / INQ. Application type is 6: RENEW 917,580 / ISSUE 269,841 / C\_LOC (location change) / C\_EXPA / C\_SBA / C\_CAPA.

### Typical questions

- "**Every active Tavern** in Chicago, with address and expiration." (~774 active)
- "Is **this restaurant** currently licensed - and what is its full license history?"
- "Every license held by **this business name**."
- "**New businesses** licensed in the last 30 days."
- "What is licensed in **ward 42** / **the Loop** right now?"
- "**Food-and-beverage** licensing across the city: aggregate by class, ward or community area."
- "Who holds a **Tobacco** or **Package Goods** license - retail diligence."

### Inputs

| Input | What it does |
|---|---|
| `mode` | `rows` (full history, default) / `active` (live now) / `recent` (last 30 days) / `aggregate` |
| `licenseClass` | the class (fuzzy): 'Retail Food Establishment', 'Tavern', 'Package Goods', 'Peddler', 'Valet Parking', 'Tobacco', ... |
| `status` | exact code: AAI / AAC / REV / REA / INQ |
| `applicationType` | exact: RENEW / ISSUE / C\_LOC / C\_EXPA / C\_SBA / C\_CAPA |
| `licenseNumber` | exact number - all events for that license |
| `name` / `address` | business/DBA name and street (fuzzy) |
| `ward` / `communityArea` | exact ward number; fuzzy community-area name |
| `issuedFrom/To`, `expiresFrom/To` | issue and expiration windows |
| `groupBy` | aggregate over licenseClass / status / applicationType / ward / communityArea / issueYear |
| `maxResults` | cap records (default 50) |

**Default run = the 50 most recently issued rows** (mode defaults to `rows`) - fast for the daily auto-test. For "who is licensed now" use `active`; for a broad view use `aggregate`.

### Low cost

**From $0.00006 per record** - billed only for the rows you use, at the low end of the store. A street-level look at one neighborhood, or a whole food-and-beverage sweep, costs pennies.

The file is the trap. **"Active" is a two-column question** - a status code and an expiration date - and taking either alone is wrong on 95% of the file; the register is *history*, so a business appears once per renewal and a naive "licenses in Chicago" count is inflated by decades of past terms. **The status letters are opaque** (AAI, AAC are not obviously "active"), and **the same account number recurs across events**, so joining on it to "get the business" silently multiplies rows. **The activity array and the class name are free text** (~25 headline classes over 150 distinct code values), so a class filter lives or dies on matching the description the city actually writes. Normalizing all of that into a register where `active` really means active and a `licenseClass` / `ward` query returns exactly the businesses you mean is the actual product. Every pull is integrity-checked against the file's known shape, so a degraded source fails loudly instead of returning bad rows.

### Source

- [Chicago Open Data: Business Licenses](https://data.cityofchicago.org/Community-Economic-Development/Business-Licenses/r5kz-chrr) - the City of Chicago's published license register. Reflects the city's record as of each refresh; not a verification of any business's current standing.

# Actor input Schema

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

rows = license rows matching your filters, most recent first (the default; includes past terms). active = only licenses live right now (status AAI/AAC with a future expiration). recent = issued in the last 30 days. aggregate = one count row per group (see groupBy).

## `licenseClass` (type: `string`):

The license class (fuzzy), e.g. 'Retail Food Establishment', 'Tavern', 'Package Goods', 'Peddler', 'Valet Parking', 'Tobacco', 'Limited Business'. Blank = every class.

## `status` (type: `string`):

Exact code: AAI (active), AAC (active, cancellation pending), REV (revoked), REA, INQ. Blank = any. Note the live split also needs the expiration date - use mode=active.

## `applicationType` (type: `string`):

Exact: RENEW, ISSUE, C\_LOC (location change), C\_EXPA, C\_SBA, C\_CAPA. Blank = any.

## `licenseNumber` (type: `string`):

Exact license number - all events for that license.

## `name` (type: `string`):

Fuzzy match on the legal name or the DBA name, e.g. 'PETSMART' or 'TAVERN'.

## `address` (type: `string`):

Street address (fuzzy), e.g. 'WACKER'.

## `ward` (type: `string`):

Chicago ward number, exact (e.g. 42).

## `communityArea` (type: `string`):

Community-area name (fuzzy), e.g. 'LOOP' or 'LAKE VIEW'.

## `issuedFrom` (type: `string`):

Only licenses issued on/after this date (YYYY-MM-DD).

## `issuedTo` (type: `string`):

Only licenses issued before this date. ISO date, YYYY-MM-DD (e.g. 2026-01-01).

## `expiresFrom` (type: `string`):

Only licenses expiring on/after this date - e.g. a renewal pipeline.

## `expiresTo` (type: `string`):

Only licenses expiring before this date. ISO date, YYYY-MM-DD (e.g. 2026-01-01).

## `groupBy` (type: `string`):

Which dimension to aggregate over. licenseClass -> the business mix; ward / communityArea -> where licensing clusters; applicationType -> new vs renewal; issueYear -> the pipeline over time.

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

Cap the number of records pushed (0 = up to ~10k per run; each record is metered individually, so there is no per-run charge cap). An aggregate has at most a few hundred groups.

## Actor input object example

```json
{
  "mode": "rows",
  "licenseClass": "",
  "status": "",
  "applicationType": "",
  "licenseNumber": "",
  "name": "",
  "address": "",
  "ward": "",
  "communityArea": "",
  "issuedFrom": "",
  "issuedTo": "",
  "expiresFrom": "",
  "expiresTo": "",
  "groupBy": "licenseClass",
  "maxResults": 50
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

Chicago business license records or aggregates - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/chicago-business-licenses").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/chicago-business-licenses").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 '{}' |
apify call j0401/chicago-business-licenses --silent --output-dataset

```

## MCP server setup

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

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/ITiadxUBY2oynsbi4/builds/gP3rjfTS2AR5bxOVx/openapi.json
