# Chicago 311 Complaints & Service Requests (`muhammadafzal/chicago-311-complaints-scraper`) Actor

Extract live Chicago 311 service requests by date, type, status, ward, community area, ZIP code, or request number. Returns address, department, dates, coordinates, and City source links.

- **URL**: https://apify.com/muhammadafzal/chicago-311-complaints-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 311 complaint returneds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Chicago 311 Complaints Scraper

Extract current Chicago 311 service requests from the City of Chicago open-data portal for civic research, operations analysis, neighborhood reporting, and service-level monitoring.

The Actor reads the City's unified **311 Service Requests** dataset (`v6vf-nfxy`) directly. It supports exact filters, stable multi-page traversal, typed records, duplicate exclusion, bounded retries, and a machine-readable `OUTPUT` summary. No account or API key is required; an optional Socrata app token raises source rate limits for large runs.

### What it extracts

| Field | Meaning |
| --- | --- |
| `serviceRequestNumber` | Unique City request ID, such as `SR26-01737096` |
| `serviceRequestType` | City-defined complaint or request category |
| `status`, `origin` | Workflow status and submission channel |
| `createdDate`, `lastModifiedDate`, `closedDate` | Chicago local source timestamps |
| `createdDepartment`, `ownerDepartment` | Creating and initially responsible departments |
| `streetAddress`, `zipCode` | Public source address and postal code |
| `ward`, `communityArea` | Chicago administrative geography |
| `policeDistrict`, `policeBeat` | Police geography when present |
| `latitude`, `longitude` | Source coordinates when the City publishes them |
| `isDuplicate`, `parentServiceRequestNumber` | City duplicate and parent relationships |
| `sourceUrl`, `scrapedAt` | Record query URL and extraction timestamp |

The dataset contains service requests, not only code-enforcement complaints. Examples include potholes, sanitation issues, street lights, graffiti, aircraft noise, and other 311 categories.

### When to use it

Use this Actor to:

- monitor recent 311 activity by ward, community area, or ZIP code;
- export complaint histories for civic and neighborhood analysis;
- compare open and completed requests by category or submission channel;
- retrieve exact service request numbers for downstream workflows;
- build maps or dashboards from normalized address and coordinate fields.

Do not use it to submit a new 311 request, retrieve private caller information, infer who made a complaint, or treat a request as proof that an alleged condition occurred. The Actor only returns fields published by the City.

### Input

All filters are optional and combine with `AND`. Multiple values inside one list combine with `OR`.

```json
{
  "maxResults": 250,
  "startDate": "2026-01-01",
  "endDate": "2026-01-31",
  "serviceRequestTypes": ["Sanitation Code Violation"],
  "statuses": ["Open", "Completed"],
  "wards": [42],
  "includeDuplicates": false,
  "sortDirection": "newest"
}
```

Date filters use `YYYY-MM-DD` and apply to the source `CREATED_DATE`. Request types, statuses, and origins are exact City values. If an exact text filter produces no records, first run a small unfiltered query to inspect current vocabulary.

For a direct ID lookup:

```json
{
  "maxResults": 2,
  "serviceRequestNumbers": ["SR26-01737096", "SR26-01737095"],
  "includeDuplicates": true
}
```

Large anonymous Socrata queries have lower rate limits. Supply your own `socrataAppToken` only when needed; the input is marked secret and is never written to logs or `OUTPUT`.

### Output

Each default-dataset item is one normalized request:

```json
{
  "serviceRequestNumber": "SR26-01737096",
  "serviceRequestType": "Aircraft Noise Complaint",
  "serviceRequestShortCode": "AVN",
  "status": "Completed",
  "origin": "Internet",
  "createdDate": "2026-08-27T00:14:49.000",
  "lastModifiedDate": "2026-08-27T00:14:50.000",
  "closedDate": "2026-08-27T00:14:50.000",
  "ownerDepartment": "Aviation",
  "streetAddress": "10510 W ZEMKE RD",
  "city": "Chicago",
  "state": "Illinois",
  "zipCode": "60666",
  "ward": null,
  "communityArea": null,
  "latitude": null,
  "longitude": null,
  "isDuplicate": false,
  "isLegacyRecord": false,
  "sourceDatasetId": "v6vf-nfxy",
  "sourceUrl": "https://data.cityofchicago.org/resource/v6vf-nfxy.json?sr_number=SR26-01737096",
  "scrapedAt": "2026-08-27T01:00:00.000Z"
}
```

Optional fields not published for a request are explicit `null`. Diagnostics do not pollute the result dataset. Read the default key-value store record `OUTPUT` for the outcome (`DATA`, `EMPTY`, `REJECTED`, or `FAILED`), filters, delivered count, confirmed custom-event count, and warnings.

### API and agent usage

Run the Actor through the Apify API, client SDK, schedules, webhooks, Make, Zapier, or Apify's MCP server. The output schema exposes:

- `complaints`: default dataset items;
- `summary`: the `OUTPUT` key-value-store record.

For agent calls, set `maxResults` explicitly to control both response size and cost. A one-record lookup is the cheapest way to verify an ID or workflow.

### Pricing

| Event | Price | When charged |
| --- | ---: | --- |
| Actor start | $0.0001 | Once when the run initializes |
| 311 complaint returned | $0.001 | After one valid record is saved |

Examples: 100 returned complaints cost up to **$0.1001** in Actor events; 1,000 cost up to **$1.0001**. Empty and rejected runs have no result-event charges, but the start event still applies. Platform usage is intended to be included in event prices; the owner should verify the live pricing screen before publication.

The Actor stops promptly after Apify reports that the run's maximum total charge has been reached. Because delivery precedes charging, the final saved item at the cap can be uncharged rather than billing for unavailable data.

### Reliability and limits

- Source: City of Chicago unified 311 Service Requests dataset `v6vf-nfxy`.
- Pagination is keyset-based on created date and request number, avoiding deep offsets.
- Requests retry HTTP 429, transient server errors, and network timeouts with bounded exponential backoff.
- Records are deduplicated by service request number within a run.
- `maxResults` is limited to 10,000 per run to bound time and cost.
- The City updates records over time. A request's status or other fields can change after extraction.
- Chicago timestamps are returned as local source timestamps without an invented UTC offset.
- Source availability and anonymous API throttling are controlled by the City/Socrata, not this Actor.

### Legal and responsible use

This Actor accesses publicly available government data. Follow the City of Chicago data portal's terms, attribution, and applicable privacy rules. Street addresses can be sensitive in aggregate; minimize retention, avoid re-identification, and do not use 311 allegations to harass, discriminate against, or profile individuals. This Actor is not affiliated with or endorsed by the City of Chicago.

# Actor input Schema

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

Use this to cap returned complaint records and cost. Enter 1–10000; for example 100. This is not a page-size setting.

## `startDate` (type: `string`):

Use this to include requests created on or after a Chicago calendar date. Format YYYY-MM-DD, for example 2026-01-01. Leave blank for no lower date bound.

## `endDate` (type: `string`):

Use this to include requests created through a Chicago calendar date. Format YYYY-MM-DD, for example 2026-01-31. Leave blank for no upper date bound.

## `serviceRequestTypes` (type: `array`):

Use this for exact City service-request type names, for example Sanitation Code Violation or Pothole in Street. Multiple values use OR; leave empty for all types.

## `statuses` (type: `array`):

Use this for exact request statuses, for example Open or Completed. Multiple values use OR; leave empty for every status.

## `origins` (type: `array`):

Use this for exact submission channels, for example Phone, Internet, or Mobile Device. Multiple values use OR; leave empty for all origins.

## `zipCodes` (type: `array`):

Use this to restrict results to Chicago ZIP codes, for example 60614. Multiple values use OR; this is not a radius search.

## `wards` (type: `array`):

Use this to restrict results to City ward numbers 1–50, for example 42. Multiple values use OR; leave empty for every ward.

## `communityAreas` (type: `array`):

Use this to restrict results to Chicago community-area numbers 1–77, for example 8. Multiple values use OR; leave empty for all areas.

## `serviceRequestNumbers` (type: `array`):

Use this to fetch exact request IDs, for example SR26-00012345. Multiple values use OR and still respect every other filter.

## `includeDuplicates` (type: `boolean`):

Use this when duplicate child requests are analytically meaningful. Default false excludes records the City marks as duplicates; it does not deduplicate similar addresses.

## `sortDirection` (type: `string`):

Use newest to return recent requests first or oldest for chronological exports. The default is newest; this controls created date, then request number.

## `socrataAppToken` (type: `string`):

Use this only to raise City data-portal rate limits. Paste your Socrata app token; public extraction works without one. This is not an Apify API token.

## Actor input object example

```json
{
  "maxResults": 100,
  "startDate": "",
  "endDate": "",
  "serviceRequestTypes": [],
  "statuses": [],
  "origins": [],
  "zipCodes": [],
  "wards": [],
  "communityAreas": [],
  "serviceRequestNumbers": [],
  "includeDuplicates": false,
  "sortDirection": "newest"
}
```

# Actor output Schema

## `complaints` (type: `string`):

One normalized City of Chicago 311 service-request record per dataset item.

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

Outcome classification, delivered and billed counts, filters, source, and warnings.

# 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("muhammadafzal/chicago-311-complaints-scraper").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("muhammadafzal/chicago-311-complaints-scraper").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 muhammadafzal/chicago-311-complaints-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/6LGItGF7bNzipkgKd/builds/mRtv2Dug11YVQYVzs/openapi.json
