...
Latest data month
...
Stop and search records (Jun 2020–present)
...
VAWG offence records (Jan 2014–present)
...
Homicide records (from 2003)
Charts
Monthly recorded crime
Total offences across all MPS boroughs, Aug 2023–Jul 2025
Knife crime
Monthly knife crime offences, last 3 years
Crime by category
Top 10 major offence categories, Aug 2023–Jul 2025
Stop and search by ethnicity
Officer-defined ethnicity, records where field is populated
Crime by borough
Total recorded offences by MPS borough, Mar 2024–Feb 2026
Data preview
Sample rows from recorded-crime-borough.parquet — violence against the person, selected boroughs, last 3 months.
The full file has 1,015 rows and 27 columns (3 key columns + 24 monthly count columns).
Datasets
recorded-crime-borough.parquet
Borough-level crime counts, last 24 months. Major + minor offence categories.
Download
recorded-crime-ward.parquet
Ward-level crime counts with major + minor categories, last 24 months.
Download
stop-search.parquet
Individual stop and search records. Ethnicity, legislation, object of search, outcome.
Download
monthly-crime-dashboard.parquet
Knife crime, domestic abuse, hate crime, gun crime — monthly totals by borough.
Download
vawg-offences.parquet
Violence against women and girls offence counts by type, borough, month.
Download
custody.parquet
Custody suite records: arrests by ethnicity, disposal type, strip searches.
Download
thorough-searches.parquet
More thorough and intimate part searches (MTIPS). Ethnicity, age, outcome.
Download
Usage
import polars as pl
REPO = "https://raw.githubusercontent.com/fenneh/london-crime-data/main/data"
# Direct download — no clone needed
df = pl.read_parquet(f"{REPO}/recorded-crime-borough.parquet")
# Geographic breakdown files are in wide format.
# Unpivot to get one row per (borough, category, month).
month_cols = [c for c in df.columns if c.isdigit()]
long = df.unpivot(
on=month_cols,
index=["majortext", "minortext", "boroughname"],
variable_name="month",
value_name="count",
)
# Knife crime by borough
knife = long.filter(pl.col("minortext").str.contains("KNIFE|BLADE|WEAPON"))
print(knife.group_by("boroughname").agg(pl.col("count").sum()).sort("count", descending=True))