# Illinois Property Transfers - PTAX-203 Deed Records (`j0401/il-ptax-203`) Actor

Every Illinois real-estate transfer declaration filed with the State (public open data, 2.93M deeds, 2014-today): address, parcel index number, instrument type, consideration paid, transfer tax split State vs. county, and the named buyer and seller. Filter by county, buyer, seller, parcel or amount.

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

## Pricing

from $0.03 / 1,000 illinois property transfer declaration 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

## Illinois Property Transfers - PTAX-203 Deed & Buyer/Seller Records

Every Illinois real-estate transfer declaration filed with the State, straight
from the State's own register. **2.93 million declarations**, 2014 to today,
refreshed as counties file.

### 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

The PTAX-203 is the statutory form filed at a property transfer, and this is
what it contains:

- **Who** - the **named seller and the named buyer**, as written on the
  declaration, plus the selling/buying entity where one is named
- **What** - the full address, the Property Index Number (PIN) and the number
  of parcels in the transfer
- **How** - the instrument type (warranty deed, trustee's deed, quit claim,
  executor's deed, judicial sale, tax deed, ...) and the instrument date
- **When** - the date the county recorded it
- **The money** - full consideration, personal property excluded, the
  deductions taken, outstanding mortgage, the resulting **taxable
  consideration**, the number of tax stamps, and **the transfer tax split into
  State and county portions**
- **The property** - the stated current use, the intended use, whether it was
  the buyer's principal residence, and the legal description
- **Why it transferred** - the statutory checkboxes: property was advertised
  for sale (2.06M), no change in use (2.44M), homestead exemption claimed
  (615,943), bank REO (78,419), sale between related parties (51,222), short
  sale (11,041), court-ordered sale, condemnation, auction and more

Named buyer and seller at the deed level is scarce in public property data.
That is the core of this dataset.

### Filter it

- **By county** - `county="Cook"` is 1.44M of the 2.93M; DuPage, Lake, Will and
  Kane follow. All 102 counties.
- **By party** - `buyer="LLC"`, `seller="SMITH"`.
- **By parcel** - `pin="12-07-426-034"` returns that parcel's full transfer
  history, one deed per sale.
- **By instrument** - warranty deeds, trustee deeds (foreclosure trustees),
  quit claims, executor's deeds, tax deeds.
- **By address, city, ZIP or township.**
- **By consideration range** - e.g. every transfer above $1,000,000.
- **By date** - recording range, or last N days.

### Example inputs

**One parcel's transfer history** - `pin` accepts the full PIN or a fragment.

```json
{ "pin": "12-07-426-034" }
```

**What one county has been recording** - `county` with a recording-date window.

```json
{ "county": "Winnebago", "recordedFrom": "2026-01-01", "maxResults": 25 }
```

**The largest transfers** - `minAmount` is in dollars.

```json
{ "minAmount": 1000000, "maxResults": 25 }
```

**Which instruments are being recorded** - `mode=aggregate` returns one count row per group.

```json
{ "mode": "aggregate", "groupBy": "instrumentType" }
```

### Or roll it up

Set `mode=aggregate` for a count by **county**, **instrument
type**, **current use** or **year**. Every group comes back - this mode is not
cut off by `maxResults`.

### Then answer real questions

- Which entity has been buying property in Cook County, and how much have they
  spent?
- What is the deed-level transfer history of one parcel, sale by sale?
- How many trustee's deeds - i.e. foreclosure transfers - were recorded in a
  county last quarter?
- What is the distribution of sale prices in a ZIP code?

### Example output

**One declaration** - `pin=12-07-426-034` returns the declarations against that parcel:

```json
{
 "platform": "il-ptax-203",
 "source": "illinois-real-estate-transfer",
 "mode": "rows",
 "groupKey": "",
 "groupCount": "",
 "groupBy": "",
 "declarationId": "20260910187184",
 "documentNumber": "2026024854",
 "dateRecorded": "2026-09-18T00:00:00.000",
 "instrumentDate": "2026-09-10T00:00:00.000",
 "instrumentType": "Warranty Deed",
 "instrumentTypeOther": "",
 "county": "Winnebago",
 "township": "Rockford",
 "city": "ROCKFORD",
 "zip": "611140000",
 "street": "2942 LA SALLE AVE",
 "fullAddress": "2942 LA SALLE AVE ROCKFORD, IL 611140000",
 "primaryPin": "12-07-426-034",
 "totalParcels": "1",
 "additionalPins": "false",
 "lotSizeOrAcreage": "",
 "unit": "",
 "currentUse": "B",
 "currentUseNumber": "0",
 "intendedUse": "B",
 "principalResidence": "false",
 "fullConsideration": "442000.0",
 "personalProperty": "0.0",
 "netConsideration": "442000.0",
 "otherConsideration": "0.0",
 "outstandingMortgage": "0.0",
 "taxableConsideration": "442000.0",
 "stateExemption": "",
 "numberOfTaxStamps": "884.0",
 "stateTax": "442.0",
 "countyTax": "221.0",
 "totalTransferTax": "663.0",
 "sellerName": "COVENT GARDEN HOLDING, LLC - 2942 LASALLE PROTECTED SERIES LLC",
 "sellerOrganization": "COVENT GARDEN HOLDING, LLC - 2942 LASALLE PROTECTED SERIES LLC",
 "buyerName": "LUIS M.  RODRIGUEZ",
 "buyerOrganization": "",
 "escrowNumber": "261352",
 "preparerName": "DEREK SEYLLER - SEYLLER LAW GROUP, LLC",
 "legalDescription": "PARCEL I:\nLOTS THIRTY-ONE (31) AND THIRTY-TWO (32) IN BLOCK SEVEN (7) AS DESIGNATED UPON THE PLAT OF HARLEM HILLS, BEING A SUBDIVISION OF PART OF SECTION 7, TOWNSHIP 44 NORTH, RANGE 2 EAST OF THE THIRD PRINCIPAL MERIDIAN, THE PLAT OF WHICH SUBDIVISION IS RECORDED JULY 2, 1928 IN BOOK 18 OF PLATS ON PAGES 28 AND 29 AS DOCUMENT NO. 275407 IN THE RECORDERS OFFICE OF WINNEBAGO COUNTY, ILLINOIS; SITUATED IN THE COUNTY OF WINNEBAGO AND THE STATE OF ILLINOIS.\n\nPARCEL II:\nTHE PARK AREA, 40 FEET IN WIDTH, LYING SOUTHERLY OF AND ADJOINING LOT THIRTY-THREE (33) IN BLOCK SEVEN (7) AS DESIGNATED UPON THE PLAT OF HARLEM HILLS, BEING A SUBDIVISION OF PART OF SECTION 7, TOWNSHIP 44 NORTH, RANGE 2 EAST OF THE THIRD PRINCIPAL MERIDIAN, THE PLAT OF WHICH SUBDIVISION IS RECORDED JULY 2, 1928 IN BOOK 18 OF PLATS ON PAGES 28 AND 29 AS DOCUMENT NO. 275407 IN THE RECORDERS OFFICE OF WINNEBAGO COUNTY, ILLINOIS; SITUATED IN THE COUNTY OF WINNEBAGO AND THE STATE OF ILLINOIS.",
 "propertyAdvertised": "true",
 "noChanges": "true",
 "saleBetweenRelated": "false",
 "transferOf100": "false",
 "courtOrderedSale": "false",
 "saleInLieuOf": "false",
 "condemnation": "false",
 "shortSale": "false",
 "bankReo": "false",
 "auctionSale": "false",
 "homesteadExemption": "false",
 "is203AAttached": "false",
 "is203BAttached": "false",
 "installmentAggregate": "",
 "installmentTotalTax": "",
 "sourceUpdatedAt": "2026-09-21"
}
```

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

```
Warranty Deed         ~1.84M
(blank)                 ~410k
Trustee Deed            ~286k
Special Warranty Deed   ~213k
Quit Claim Deed          ~59.1k
Executor Deed            ~39.8k
```

### Notes on the data, from the source

- **The source is 183 columns wide and most of them are empty.** The
  installment-contract forms (PTAX-203-A and PTAX-203-B) are attached on 33,803
  and 6,390 declarations - 1.15% and 0.22%. This actor exposes the columns that
  are actually populated on ordinary transfers, including the full set of
  statutory transfer checkboxes, plus the two "an A/B form is attached" flags
  and their totals.
- **331,437 transfers state a $0 consideration** - a nominal or unstated price,
  mostly corrective and intra-family deeds. It is a value the filer wrote, so it
  is returned as-is; set a minimum amount to exclude them.
- **The current-use column is a single letter A-K** and the State publishes no
  legend for it. It is returned as the raw code with no interpretation attached.
- **Instrument type is matched exactly.** 'Warranty Deed' and 'Special Warranty
  Deed' are different instruments and are never merged. ~410k declarations
  (14%) named no instrument at all.
- **The recording date is the reliable one** - complete and sentinel-free on all
  2.93M rows, which is why it is the sorting and filtering axis. The deed's own
  instrument date is also complete but carries 227 clerical records dated before
  1990, down to 1900.

# Actor input Schema

## `county` (type: `string`):

Substring match on the county where the property sits, e.g. 'Cook' (1.44M of the 2.93M), 'DuPage', 'Lake', 'Will'. 102 counties in total. Blank = any.

## `buyer` (type: `string`):

Substring match on the named buyer as written on the declaration. Blank = any.

## `seller` (type: `string`):

Substring match on the named seller as written on the declaration. Blank = any.

## `pin` (type: `string`):

Substring match on the Property Index Number, e.g. '12-07-426-034'. Use it to pull the transfer history of one parcel. Blank = any.

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

Substring match on the property's full address as recorded, e.g. 'ROCKFORD', 'LA SALLE'. Blank = any.

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

Substring match on the city on the declaration. Blank = any.

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

Substring match on the property ZIP, e.g. '61101'. Blank = any.

## `township` (type: `string`):

Substring match on the township. 1,570 distinct values - too fine-grained to aggregate over, so it is a filter only. Blank = any.

## `instrumentType` (type: `string`):

Exact instrument, matched exactly and never as a substring - 'Warranty Deed' and 'Special Warranty Deed' are separate instruments. Warranty deeds are 1.84M of the 2.93M, trustee deeds 286K, special warranty deeds 213K. 'Other' is the filer's own word. Blank = any; 409,739 declarations (14%) named no instrument and come back only when this is left blank.

## `currentUse` (type: `string`):

The single letter the filer entered for current use, exactly as the State publishes it. The source ships no legend for these codes, so no label is attached here. 'B' is by far the most common (2.12M). Blank = any; 392,991 declarations left it blank.

## `declarationId` (type: `string`):

Substring match on the State's declaration identifier for a single-record lookup. Blank = any.

## `documentNumber` (type: `string`):

Substring match on the county recorder's document number. Blank = any.

## `recordedFrom` (type: `string`):

Earliest recording date, YYYY-MM-DD. The recording date is complete and free of sentinels across all 2.93M rows, so it is the reliable axis. Blank = any.

## `recordedTo` (type: `string`):

Latest recording date, YYYY-MM-DD. Blank = any.

## `minAmount` (type: `number`):

Smallest stated full consideration in dollars. Note that 331,437 transfers state $0 - a nominal or unstated price, mostly corrective and intra-family deeds. Set 1 to exclude them. Blank = no lower bound.

## `maxAmount` (type: `number`):

Largest stated full consideration in dollars. Blank = no upper bound.

## `recentDays` (type: `integer`):

Only declarations recorded within the last N days. Ignored when 'Recorded from' is set. 0 = off.

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

rows = declarations matching your filters (default). aggregate = one count row per group (see groupBy).

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

Which dimension to aggregate over (mode=aggregate). Blank = county. Every group is returned - aggregate mode is not cut off by maxResults. Township is deliberately absent: 1,570 values is a raw geography, not a rollup.

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

Cap the number of records pushed in rows mode (0 = default 50; up to 2,000 per run). Each record is metered individually, so there is no per-run charge cap. Aggregate mode returns every group.

## Actor input object example

```json
{
  "county": "",
  "buyer": "",
  "seller": "",
  "pin": "",
  "address": "",
  "city": "",
  "zip": "",
  "township": "",
  "instrumentType": "",
  "currentUse": "",
  "declarationId": "",
  "documentNumber": "",
  "recordedFrom": "",
  "recordedTo": "",
  "recentDays": 0,
  "mode": "rows",
  "groupBy": "",
  "maxResults": 50
}
```

# Actor output Schema

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

Illinois PTAX-203 transfer declarations - 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/il-ptax-203").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/il-ptax-203").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/il-ptax-203 --silent --output-dataset

```

## MCP server setup

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

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/H5aFSgrms6kkWQNqQ/builds/JzCtakPaaB2f43yA1/openapi.json
