# NY DMV Traffic Tickets - Violations & Enforcement Data (`j0401/ny-dmv-tickets`) Actor

New York DMV adjudicated traffic tickets (public open data, 13.4M tickets, 2022-2026): charged code and description, year/month/weekday, driver age and gender, licence state, police agency and court. No names, addresses or plates.

- **URL**: https://apify.com/j0401/ny-dmv-tickets.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 ny dmv ticket 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

## NY DMV Traffic Tickets - Violations & Enforcement Data

Every traffic and parkway violation the New York State Department of Motor Vehicles has adjudicated and closed out, **13,402,132** tickets, covering 2022 through 2026.

Both of the DMV's enforcement streams are in here: the regular court disposition stream (`TSLED`, 10,089,197 tickets) and the New York City and Rochester Traffic Violations Bureau administrative stream (`TVB`, 3,312,935).

### Low cost

**From $0.00005 per record, down to $0.00003 at Gold** Pay-per-event: you are charged per record delivered, and nothing for the query.

### What you get

| Field | Meaning |
|---|---|
| `violationCode` / `violationDescription` | What was charged - `1180D` "SPEED IN ZONE" 1,565,830 / `5091` 853,170 / `1110A` "DISOBEYED TRAFFIC DEVICE" 844,218 / `306B` "UNINSPECTED MOTOR VEHICLE" 827,665 and 1,017 more codes |
| `violationYear` / `violationMonth` / `violationWeekday` | When |
| `ageAtViolation` / `gender` | The driver, as recorded - age, and `M` 9,817,676 / `F` 3,269,088 / `C` 203,913 / `U` 111,455 |
| `licenseState` | Where the licence was issued - `NEW YORK` 10,572,397 / `NEW JERSEY` 381,709 / `PENNSYLVANIA` 218,174 / `CONNECTICUT` 167,948 and 66 more |
| `policeAgency` | Who wrote it - 902 agencies, from `NEW YORK CITY POLICE DEPARTMENT` 3,266,421 down to village departments |
| `court` | Where it was disposed of - 1,392 courts, including the TVB bureaus (`Bronx TVB` 512,157 / `Brooklyn South TVB` 493,086) and county traffic agencies |
| `source` | Which stream - `TSLED` or `TVB` |

### Modes

- **`rows`** (default) - tickets matching your filters.
- **`aggregate`** - one count row per group: by year, month, weekday, driver gender or enforcement stream.

### Example inputs

**One violation code** - `code` is a substring match on the charged code, so `1180D` is speeding in a zone.

```json
{ "code": "1180D", "maxResults": 100 }
```

**One adjudication stream** - `source` splits the court dispositions (`TSLED`) from the Traffic Violations Bureau (`TVB`).

```json
{ "source": "TVB", "maxResults": 100 }
```

**Young drivers on one weekday** - a `ageFrom` / `ageTo` range fences out the junk ages (`-1`, and birth years that landed in the column) automatically.

```json
{ "ageFrom": 16, "ageTo": 25, "weekday": "SATURDAY", "maxResults": 100 }
```

**The register counted by year** - `mode=aggregate` returns one row per group and every group, not a truncated top-N.

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

**Which courts handle the most** - `court` matches the court name; `mode=aggregate` with `groupBy=year` counts them by year.

```json
{ "court": "TVB", "mode": "aggregate", "groupBy": "year" }
```

### What this dataset does not contain

There is **no name, no address, no licence number and no plate**.

A person appears in this register only as *(age at the time, gender, state that issued the licence)*. That is not an omission on our part - the DMV publishes this file with those fields already removed, and cells small enough to identify an individual are suppressed upstream before publication.

So this is an **enforcement-pattern dataset, not a person-lookup dataset**. It answers questions like which violation codes are rising, how enforcement is distributed across agencies and courts, whether age or weekday correlates with violation type, and how the two adjudication streams differ. It cannot answer "has this driver been ticketed" and it cannot be joined to an individual.

### Three things about the fields

**The age column carries junk values.** The floor is **-1** and the ceiling is **2024** - a birth year has landed in the age column. 206,448 rows are recorded as zero or less and 10,863 are above 100. Every age range you supply fences those out automatically, so `ageFrom=16` cannot be swamped by 'unknown' rows recorded as -1.

**There is no calendar day.** The source publishes a year, a month and a weekday, and nothing finer. Day-level or "last 30 days" filtering is impossible on this register and the actor does not pretend otherwise - you get a year, a month and a weekday.

**Two columns are deliberately not offered as aggregates.** `policeAgency` has 902 distinct values and `court` has 1,392. Both are available as filters (exact or substring), but a "top N agencies" aggregate over them would silently drop most of the tail, so neither is offered as a grouping dimension. The dimensions you can group by are all genuinely small.

### The window is rolling

This is not a historical archive. The dataset's own title is *"Traffic Tickets Issued: Four Year Window"* - the State keeps a rolling window and drops older years as new ones arrive. Today the register covers 2022-2026:

```
2022   2,584,657      2025   3,020,647
2023   2,851,858      2026   2,032,774
2024   2,912,196
```

If you need a violation from 2019, it is not in the source and it is not in this actor.

### Source

> **Counts below are a live snapshot** - the register is refreshed monthly, so exact figures move between reads. The order of magnitude and the ratios are stable.

New York State Department of Motor Vehicles, published on `data.ny.gov` as [Traffic Tickets Issued: Four Year Window](https://data.ny.gov/d/q4hy-kbtf) - public open data, no login and no key. Note that it is refreshed **monthly**, not daily, unlike most registers in this series; the newest month present reflects that cadence.

### Output

Every record carries the same key set regardless of mode - the ticket fields plus the aggregate columns (`groupKey`, `groupCount`, `groupBy`), which are `""` outside aggregate mode. Every record carries `sourceUpdatedAt`, the date the State last rebuilt the register.

### Related actors

- **NYC OATH Hearings** - where New York City agencies' own summonses are adjudicated.
- **NYC DOB Permits** - the building permits whose violations show up in that tribunal.

### Example output

**One ticket** - `code=1225C2A` returns the record below. Every record carries the ticket fields plus the aggregate columns (`groupKey`, `groupCount`, `groupBy`), which are `""` outside aggregate mode:

```json
{
  "platform": "ny-dmv-tickets",
  "source": "TVB",
  "mode": "rows",
  "groupKey": "", "groupCount": "", "groupBy": "",
  "violationCode": "1225C2A",
  "violationDescription": "OPERATING MOTOR VEHICLE WITH MOBILE PHONE",
  "violationYear": "2025",
  "violationMonth": "12",
  "violationWeekday": "WEDNESDAY",
  "ageAtViolation": "37",
  "gender": "M",
  "licenseState": "PENNSYLVANIA",
  "policeAgency": "NEW YORK CITY POLICE DEPARTMENT",
  "court": "Queens South TVB",
  "sourceUpdatedAt": "2026-09-06"
}
```

**`mode=aggregate`, `groupBy=source`** - one row per adjudication stream:

```
TSLED   10,089,197
TVB      3,312,935
```

**Speeding in a zone** - `code=1180D`:

```
1180D | 2026 | 9 | SPEED IN ZONE | NEW YORK CITY POLICE DEPARTMENT
```

**Violations by weekday** - `mode=aggregate`, `groupBy=weekday`:

```
WEDNESDAY   2,110,004     SATURDAY   1,776,187
THURSDAY    2,102,764     MONDAY     1,725,532
TUESDAY     2,087,393     SUNDAY     1,551,489
FRIDAY      2,048,763
```

# Actor input Schema

## `code` (type: `string`):

Substring match on the charged violation code, e.g. '1180D' for speeding. 1,021 codes exist. Blank = any.

## `description` (type: `string`):

Substring match on the plain-English description, e.g. 'SPEED', 'UNREGISTERED', 'REDLIGHT'. Blank = any.

## `agency` (type: `string`):

Substring match on the agency that wrote the ticket, e.g. 'STATE POLICE', 'NEW YORK CITY'. 902 agencies exist. Blank = any.

## `court` (type: `string`):

Substring match on the court that disposed of the ticket, e.g. 'TVB', 'Nassau County'. 1,392 courts exist. Blank = any.

## `licenseState` (type: `string`):

Substring match on the state that issued the licence, e.g. 'NEW YORK', 'NEW JERSEY'. Blank = any.

## `gender` (type: `string`):

Driver gender as recorded. 'C' and 'U' are the source's own codes. Blank = any.

## `source` (type: `string`):

'TSLED' = court dispositions (10.09M). 'TVB' = NYC / Rochester Traffic Violations Bureau administrative adjudication (3.31M). Blank = both.

## `weekday` (type: `string`):

Day of week the violation occurred. Blank = any.

## `month` (type: `string`):

Calendar month (1-12) the violation occurred. The source has no calendar day, so day-level filtering is not possible. Blank = any.

## `year` (type: `string`):

Year of violation. The register covers a rolling window only: 2022-2026. Blank = any.

## `ageFrom` (type: `integer`):

Youngest driver age to include. Values recorded as -1 (unknown) and impossible values above 100 are always excluded from a range. Blank = any.

## `ageTo` (type: `integer`):

Oldest driver age to include. Blank = any.

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

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

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

Which dimension to aggregate over (mode=aggregate). Blank = year. Only low-cardinality dimensions are offered: the police agency (902 values) and court (1,392) columns are filters only, because a top-N of either would silently drop most of the tail. Every group is returned.

## `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
{
  "code": "",
  "description": "",
  "agency": "",
  "court": "",
  "licenseState": "",
  "gender": "",
  "source": "",
  "weekday": "",
  "month": "",
  "year": "",
  "mode": "rows",
  "groupBy": "",
  "maxResults": 50
}
```

# Actor output Schema

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

NY DMV traffic violation tickets - 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/ny-dmv-tickets").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/ny-dmv-tickets").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/ny-dmv-tickets --silent --output-dataset

```

## MCP server setup

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

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/xs33i4KXY5XUZBs3l/builds/firB6MMILwgjpe82A/openapi.json
