# Chicago Contract Recompete & Extension Radar (`lukehunter/chicago-contract-expiration-radar`) Actor

Find expiring City of Chicago incumbent contracts, filterable by expiration window, department and minimum award amount, with revision-safe extension history for recompete prospecting.

- **URL**: https://apify.com/lukehunter/chicago-contract-expiration-radar.md
- **Developed by:** [Luke Hunter](https://apify.com/lukehunter) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$15.00 / 1,000 contract opportunities

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Contract Recompete & Extension Radar

**For public-sector capture teams and sellers who need a City of Chicago incumbent-contract renewal watchlist.** One run returns contracts approaching their latest published end date, with the incumbent vendor, department, revision count and whether the City has already extended them.
Pay-per-result: **$0.015 per contract opportunity — 100 contracts = $1.50.** You pay only for results delivered, capped by `maxItems`.
Try it free with Apify's monthly platform credit.

This Actor collapses the City's revision history to one current row per purchase order, compares the original and latest end dates, counts revisions, exposes the incumbent and latest contract PDF when published, and returns a rolling expiration pipeline ready for CRM or scheduled alerts.

**Defensible USP:** competing Chicago Actors primarily extract/filter contract records. This Actor performs revision-history intelligence: the highest numeric revision controls the current expiration, stale earlier end dates are never treated as current, and each result shows `originalEndDate`, `endDate`, `revisionCount`, `derivedExtensionDays`, and `derivedWasExtended`. That answers a more valuable question: **“What incumbent work is actually nearing its current end date, and has it already been extended?”**

### Quick start (2 minutes)

1. Open the **Input** tab (no City of Chicago account or key needed — the source is public).
2. Use this prefill for high-value technology contracts nearing their latest end date:

```json
{"daysAhead":180,"minAwardAmount":100000,"department":"DEPARTMENT OF TECHNOLOGY AND INNOVATION","maxItems":250}
```

3. Click **Start**. Export the resulting watchlist to CSV/JSON, or pull it via API into your CRM.

### Use cases

- **Incumbent displacement:** find contracts nearing their current published end date, with the incumbent vendor and department attached.
- **Extension-aware capture planning:** distinguish an untouched expiration from a contract whose end date has already moved by months or years.
- **Account prioritization:** filter by original published award amount, department, or procurement type before reviewing the contract PDF.
- **Competitive account research:** see revision count, latest approval date, and extension history before spending time on outreach.

### Why this is different from a Chicago contract scraper

Raw extraction is useful for historical analysis. Recompete prospecting has a different failure mode: an old revision can show an end date that is no longer current. The City dataset contains modifications and extensions, so treating every revision as an opportunity creates stale signals. This Actor fetches the revision history, groups by purchase order, and lets the latest numeric revision control eligibility.

The Actor does **not** claim an expiring contract will be rebid. Expiration and extension history are evidence for prospecting; they are not a City forecast.

### Input

| Field | Default | Meaning |
|---|---:|---|
| `daysAhead` | 180 | Latest published end date must fall 1–730 days ahead |
| `minAwardAmount` | 0 | Minimum amount on the earliest numeric revision; $0 may mean Depends Upon Requirements |
| `department` | null | Exact City department filter |
| `procurementType` | null | Exact source procurement-type filter |
| `maxItems` | 1000 | Hard output and billing cap, max 10,000 |

Example — high-value technology contracts nearing their latest end date:

```json
{"daysAhead":180,"minAwardAmount":100000,"department":"DEPARTMENT OF TECHNOLOGY AND INNOVATION","maxItems":250}
```

### Output

Source facts and Actor-derived values are deliberately separated.

| Field | Meaning |
|---|---|
| `purchaseOrderNumber`, `specificationNumber` | City identifiers |
| `vendorName`, `vendorId`, `department` | Published incumbent/buyer facts from the latest numeric revision |
| `sourceOriginalAwardAmount` | Award amount on the earliest numeric revision; source fact, not a computed contract value |
| `sourceLatestRevisionAwardAmount` | Award amount on the latest numeric revision; source fact and may represent a modification |
| `originalEndDate` | End date on earliest numeric revision |
| `endDate` | End date on latest numeric revision; controls inclusion |
| `latestApprovalDate` | Approval date on latest numeric revision |
| `revisionCount` | Count of numeric revisions observed for the purchase order |
| `daysUntilExpiration` | **Derived:** calendar days to latest published end date |
| `derivedExtensionDays` | **Derived:** latest end date minus original end date |
| `derivedWasExtended` | **Derived:** true only when that difference is positive |
| `derivedPrioritySignal` | **Derived:** <=30, <=90, or within requested window |
| `contractPdfUrl` | Latest-revision City contract PDF URL when published |
| `sourceDatasetTotal` | Total source rows observed during the run |

Illustrative shape (not a claimed live row):

```json
{
  "purchaseOrderNumber":"108283",
  "vendorName":"SAFEWARE",
  "revisionCount":3,
  "sourceOriginalAwardAmount":16270800,
  "originalEndDate":"2024-08-14T00:00:00.000Z",
  "endDate":"2027-08-17T00:00:00.000Z",
  "derivedExtensionDays":1098,
  "derivedWasExtended":true
}
```

The dates/vendor in that example mirror a publicly verifiable City contract history, but the amount semantics remain exactly as the dataset publishes them; the Actor does not invent a “current contract value.”

| Field | Value |
|---|---|
| `purchaseOrderNumber` | `108283` |
| `vendorName` | `SAFEWARE` |
| `revisionCount` | `3` |
| `sourceOriginalAwardAmount` | `16270800` |
| `originalEndDate` | `2024-08-14T00:00:00.000Z` |
| `endDate` | `2027-08-17T00:00:00.000Z` |
| `derivedExtensionDays` | `1098` |
| `derivedWasExtended` | `true` |

(Illustrative shape, as noted above — not a claimed single live row.)

### Source integrity

Source: official City of Chicago **Contracts** dataset, Socrata resource `rsxa-ify5`. The City describes it as contracts and modifications since 1993, maintained by Procurement Services and updated daily. The source explicitly warns that only blanket contracts have FMPS end dates, $0 can mean Depends Upon Requirements, and negative modification amounts can delete scope/money. Those caveats are why this Actor avoids a black-box opportunity score or invented current-value metric.

Legacy/non-numeric revision histories are excluded fail-closed because the Actor cannot safely determine “latest” from them. A latest revision with no end date is also excluded; the Actor never borrows an older end date.

### Run it weekly

This is built for a recurring renewal watchlist, not a one-off pull:

1. Set your `department`/`minAwardAmount`/`daysAhead` filters, then click **Schedule** on the run page (or create one under **Schedules** in the Apify Console).
2. Run it weekly (or daily on a shorter horizon) with the same filters each time.
3. Keep `purchaseOrderNumber` as your dedupe/tracking key across runs. Changes in `latestRevisionNumber`, `endDate`, `derivedExtensionDays`, or `latestApprovalDate` between runs are your review trigger — they mean the City touched that contract since your last pull.
4. This Actor returns the current snapshot each run; it does not itself persist or diff runs, so keep results in your CRM/sheet to compare over time.

### Reliability and billing

The source is paginated with bounded retry/backoff, request timeouts, and a hard pagination ceiling. Empty matching output fails loudly. Data is pushed before billing; the platform-confirmed `chargedCount` is authoritative; migration-safe pre-charge state prevents duplicate charging; post-charge logging errors cannot fail a billed run.

### Pricing

**$0.015 per contract opportunity** (100 contracts = $1.50), charged only for contracts actually delivered. `maxItems` is the hard result and billing cap; filtered records, retries and empty runs are not charged.

### FAQ

**Is this a bid or solicitation feed?** No. It is an incumbent contract expiration/extension signal built from awarded-contract history.

**Does an end date mean a recompete is coming?** No. The City may renew, extend, rebid, replace, or take no comparable action.

**Why fetch revision history?** Because a superseded revision can contain an old end date. For capture work, the current latest-revision end date is the safer signal.

**Why are some contracts absent?** Standard contracts may not have FMPS end dates, and ambiguous nonnumeric revision histories are excluded fail-closed.

**What should I monitor on recurring runs?** Purchase order, latest revision, latest end date, extension days, approval date, incumbent, and contract PDF.

### Related Actors

Other data tools from the same developer, built to the same standard: official or public sources, hard cost caps, and honest documentation of limits.

- **[Federal Register Rule Effective-Date Radar](https://apify.com/lukehunter/federal-rule-effective-date-radar)**: final federal rules about to take effect, with citations, dockets and urgency.
- **[Bank Branch Closings & Merger Radar (FDIC)](https://apify.com/lukehunter/bank-branch-merger-radar)**: new US bank branch closings, openings and mergers from the FDIC.
- **[Chicago New Business License Radar](https://apify.com/lukehunter/chicago-new-business-license-radar)**: newly licensed Chicago businesses as opening leads, grouped by category.
- **[NYC FDNY Contractor Leads & Hearing Radar](https://apify.com/lukehunter/fdny-violation-radar)**: fresh NYC fire-code summonses routed by remediation trade, with hearing dates.
- **[RCRA Enforcement Response Leads](https://apify.com/lukehunter/rcra-enforcement-radar)**: new EPA hazardous-waste (RCRA) enforcement actions and penalties, by state.
- **[Emerging Federal Contractor Radar](https://apify.com/lukehunter/emerging-federal-contractor-radar)**: companies that just won their first federal contracts, from USAspending.gov.
- **[Hospital Price Transparency Enforcement Leads](https://apify.com/lukehunter/hospital-price-transparency-enforcement-leads)**: hospitals with recent CMS price transparency warning notices, CAP requests and CMP notices.
- **[Hospital Ownership Change Radar](https://apify.com/lukehunter/hospital-chow-radar)**: hospitals that just changed owner, with buyer, seller and effective date from CMS filings.
- **[New Healthcare Organization NPI Radar](https://apify.com/lukehunter/new-healthcare-organization-npi-radar)**: newly registered healthcare organizations from CMS's weekly NPI file, by provider type.
- **[County Multifamily Building Permits Surge Radar](https://apify.com/lukehunter/county-multifamily-permit-surge-radar)**: U.S. counties where 5+ unit apartment permits are surging year over year.
- **[Zillow ZIP Code Property Search Scraper](https://apify.com/lukehunter/zillow-zip-scraper)**: for-sale listings by ZIP code with prices, price cuts and days on Zillow.
- **[Walmart Category Scraper](https://apify.com/lukehunter/walmart-category-scraper)**: product names, prices, was-prices and ratings from Walmart category pages.

# Actor input Schema

## `daysAhead` (type: `integer`):

Return latest-revision contracts ending within this many days, 1-730, default 180.

## `minAwardAmount` (type: `number`):

Minimum award amount in USD published on the earliest numeric revision, 0 or more, default 0. Zero can represent Depends Upon Requirements contracts.

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

Optional City department filter (case-insensitive exact match against the source's own department name), e.g. "DEPARTMENT OF TECHNOLOGY AND INNOVATION".

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

Optional procurement type filter (case-insensitive exact match against the source's own procurement\_type value, as published by the City).

## `maxItems` (type: `integer`):

Hard result and billing cap, 1-10000, default 1000.

## Actor input object example

```json
{
  "daysAhead": 180,
  "minAwardAmount": 0,
  "maxItems": 1000
}
```

# Actor output Schema

## `dataset` (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("lukehunter/chicago-contract-expiration-radar").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("lukehunter/chicago-contract-expiration-radar").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 lukehunter/chicago-contract-expiration-radar --silent --output-dataset

```

## MCP server setup

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

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/jMKNYPyNzkYYZYm6L/builds/WKb7AmeRybAY5lfFX/openapi.json
