# Chicago Contracts - City Awards & Purchase Orders (`j0401/chicago-contracts`) Actor

City of Chicago awarded contracts and purchase orders (public open data, 186,177 awards, 89,067 PO numbers): vendor, award amount, contract and procurement type, department, approval dates and the contract PDF. Filter by vendor, department, type, amount or date.

- **URL**: https://apify.com/j0401/chicago-contracts.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.03 / 1,000 chicago awarded contract 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 Contracts - City Awards & Purchase Orders

Every contract and purchase order the City of Chicago has awarded, straight
from the City's own open data portal. **~186,000 award records over ~89,000
distinct PO numbers** (as of 2026-09-27), from 1993 to this week.

### Low cost

**From $0.00005 per record, down to $0.00003 at Gold.** Pay only for the
records you actually receive - the whole register stays queryable, you are
never billed for the scan behind it.

### What you get

- **Who** - the vendor name and vendor id, with the vendor's address
- **How much** - the award amount, plus the specification number and the
  purchase order / contract number
- **What** - the purchase order description, the contract type (construction,
  demolition, commodities, delegate agency, ) and the procurement method
  (bid, RFP, sole source, emergency, )
- **Which department** - the awarding department
- **When** - the approval date, plus the contract's start and end dates
- **The document** - a link to the contract PDF where the City published one

### Modes

- **rows** (default) - award records matching your filters
- **aggregate** - counts by department, contract type or vendor state

### Filters

PO / contract number, vendor name, vendor id, department, contract type,
procurement method, vendor city / state / ZIP, a minimum award amount, and a
true approval-date range.

### Example inputs

**Demolition awards above a threshold**

```json
{ "vendorName": "WRECKING", "minAwardAmount": 100000, "maxResults": 25 }
```

**One department's awards this year**

```json
{ "department": "BUILDINGS", "approvedFrom": "2026-01-01", "maxResults": 25 }
```

**Sole-source and emergency awards**

```json
{ "procurementType": "SOLE SOURCE", "approvedFrom": "2026-01-01" }
```

**One contract by number** - a contract appears once per revision, so a number
can return several rows.

```json
{ "poNumber": "162389" }
```

### Example output

**A demolition award** - `vendorName=WRECKING`, `minAwardAmount=100000`:

```json
{
 "platform": "chicago-contracts",
 "source": "chicago-awarded-contracts",
 "corpus": "contracts",
 "recordKind": "contract",
 "mode": "rows",
 "groupKey": "",
 "groupCount": "",
 "groupBy": "",
 "poNumber": "162389",
 "revisionNumber": "0",
 "description": "Demolition Services",
 "specificationNumber": "752095",
 "contractType": "DEMOLITION",
 "procurementType": "MASTER AGREEMENT",
 "department": "DEPARTMENT OF BUILDINGS",
 "vendorName": "LEEWAY WRECKING, INC.",
 "vendorId": "93278213A",
 "address1": "PO BOX 12570  EFT",
 "address2": "",
 "city": "CHICAGO",
 "state": "IL",
 "zip": "60612",
 "awardAmount": "5000000",
 "startDate": "2021-09-01",
 "endDate": "2024-08-31",
 "approvalDate": "2021-10-29",
 "contractPdf": "http://ecm.cityofchicago.org/eSMARTContracts/service/DPSWebDocumentViewer?sid=ESMART&id={B0730A7D-0000-CA1C-B7BA-FF8A51EA8946}",
 "sourceUpdatedAt": "2026-09-24"
}
```

**`mode=aggregate`, `groupBy=department`** - one count row per department:

```
DEPARTMENT OF FAMILY AND SUPPORT SERVICES    30,907
CHICAGO DEPARTMENT OF TRANSPORTATION         21,203
DEPARTMENT OF HUMAN RESOURCES                17,948
CHICAGO DEPARTMENT OF PUBLIC HEALTH          14,358
DEPARTMENT OF BUILDINGS                      11,251
CHICAGO DEPARTMENT OF AVIATION               11,123
```

### Notes on the data, from the source

- **A contract appears once per revision.** 186,177 rows map to 89,067 distinct (both measured 2026-09-27)
  PO numbers because amendments are separate rows (`revision_number` 0 upward;
  89,041 rows are revision 0). The revision history is deliberate and is kept -
  but if you are counting contracts rather than rows, count distinct PO
  numbers, and the two totals above are the honest pair.
- **The award amount is sometimes negative.** 27,549 rows are exactly 0 and
  27,153 are negative - offsetting and adjustment revisions. Summing the raw
  column mixes adjustments into the total, which is why the filter
  (`minAwardAmount`) only ever matches positive amounts.
- **The procurement method is blank on 70% of rows** (130,903 of 186,177) and
  is therefore offered as a filter only, never as an aggregate dimension - a
  "group by procurement method" would return "no value recorded" as its largest
  bucket.
- **A contract PDF exists on 36.5% of rows** (68,005). The link is exposed
  where the City published one, and never promised as universal.
- **The department is blank on 10,629 rows (5.7%)** and the contract type on
  44,598 (24%) - both are still usable as filters, but a share of the register
  carries no value in them.
- **Four data-entry outliers are blanked, not shown as live contract terms:**
  two end dates in 2104 / 3030, and two start dates past 2030 (the source's own
  values). Invalid dates on filter inputs are rejected rather than passed on.
- **Approval dates are true dates**, so the date filters here are real ranges,
  not the year-granularity that text date columns force.

### Notes

- Public open data from data.cityofchicago.org. No login, no scraping.
- The register is updated as awards are approved; every record carries
  `sourceUpdatedAt` so you can see the feed's currency without running
  anything.
- Charges are metered per record delivered, so a targeted query costs a
  fraction of a cent.

# Actor input Schema

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

rows = records matching your filters (default). aggregate = one count row per group.

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

Aggregate dimension, only used when mode=aggregate.

## `poNumber` (type: `string`):

Exact purchase order or contract number. NOTE: a contract appears once per revision, so one number can return several rows. Blank = any.

## `vendorName` (type: `string`):

Vendor-name substring, e.g. 'WRECKING'. Blank = any.

## `vendorId` (type: `string`):

Exact vendor id. Blank = any.

## `department` (type: `string`):

Department substring, e.g. 'BUILDINGS'. Blank = any.

## `contractType` (type: `string`):

Contract-type substring, e.g. 'CONSTRUCTION', 'DELEGATE AGENCY'. Blank = any.

## `procurementType` (type: `string`):

Procurement-method substring, e.g. 'BID', 'RFP', 'SOLE SOURCE'. Blank = any. Blank on 70% of rows in the source.

## `city` (type: `string`):

Vendor city substring. Blank = any.

## `state` (type: `string`):

Two-letter state. Blank = any.

## `zip` (type: `string`):

ZIP prefix. Blank = any.

## `minAwardAmount` (type: `integer`):

Only rows with an award amount at least this, e.g. 100000. Blank = any. ~15% of rows are negative (adjustment revisions) and will not match.

## `approvedFrom` (type: `string`):

YYYY-MM-DD. True date range. Blank = any.

## `approvedTo` (type: `string`):

YYYY-MM-DD. Blank = any.

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

Cap records returned for this run. Default 50 keeps the daily auto-test fast. 0 = no cap (the full matching slice, up to 10,000).

## Actor input object example

```json
{
  "mode": "rows",
  "groupBy": "department",
  "poNumber": "",
  "vendorName": "",
  "vendorId": "",
  "department": "",
  "contractType": "",
  "procurementType": "",
  "city": "",
  "state": "",
  "zip": "",
  "approvedFrom": "",
  "approvedTo": "",
  "maxResults": 50
}
```

# Actor output Schema

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

Chicago Contracts - City Awards & Purchase Orders - 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-contracts").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-contracts").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-contracts --silent --output-dataset

```

## MCP server setup

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

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/sMhdIVOB9srLoAxdw/builds/2gmxaSuPDTyzmA4Xx/openapi.json
