Crime API Reference
Monthly street-level crime and outcome data published by Police.uk.
Base URL: https://api.govdata.dev/v1
Search crime
Returns paginated, newest-first street-level crime records for analysts, civic-data teams, and local publishers. Combine month, category, force, and LSOA filters to power an LSOA-level community safety dashboard or compare recorded public-order incidents across police forces.
/v1/crime
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
month |
string | No | — | Recorded month in YYYY-MM format. |
category |
string | No | — | Exact category label returned by GET /v1/crime/categories. |
force |
string | No | — | Lowercase Police.uk force slug, for example city-of-london-police. |
lsoa_code |
string | No | — | 2021 Lower Layer Super Output Area code; matching is case-insensitive. |
page |
integer | No | 1 | One-based result page. |
per_page |
integer | No | 25 | Results per page; minimum 1, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.govdata.dev/v1/crime?month=2026-06&category=Public%20order&force=city-of-london-police&lsoa_code=E01000914&per_page=2"
uri = URI("https://api.govdata.dev/v1/crime") uri.query = URI.encode_www_form(month: "2026-06", category: "Public order", force: "city-of-london-police", lsoa_code: "E01000914", per_page: 2) req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer YOUR_API_KEY") res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
response = requests.get( "https://api.govdata.dev/v1/crime", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"month": "2026-06", "category": "Public order", "force": "city-of-london-police", "lsoa_code": "E01000914", "per_page": 2} )
const url = new URL("https://api.govdata.dev/v1/crime"); url.search = new URLSearchParams({ month: "2026-06", category: "Public order", force: "city-of-london-police", lsoa_code: "E01000914", per_page: "2" }); const response = await fetch(url, { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{ "data": [ { "persistent_id": "372dea2f064aa7d5cb035b7bed79cc16a3b4de05dc3bc782dd22058a0f87d004", "month": "2026-06", "category": "Public order", "force": "city-of-london-police", "force_name": "City of London Police", "longitude": -0.111596, "latitude": 51.518281, "location": "On or near Chancery Lane", "location_precision": "Approximate anonymised location", "lsoa_code": "E01000914", "lsoa_name": "Camden 028B", "last_outcome_category": "Under investigation", "context": null } ], "meta": { "api_version": "v1", "licence": "Open Government Licence v3.0", "source": "Police.uk", "source_url": "https://data.police.uk/data/" }, "pagination": { "total": 1, "page": 1, "per_page": 2, "total_pages": 1 } }
longitude and latitude identify Police.uk's anonymised snap-point, not the incident site. lsoa_code and lsoa_name describe that snapped location. The example uses real LSOA E01000914 from the bundled Police.uk development fixture.
Error responses
400 — malformed month. Search has no record-specific 404 or semantic 422 branch; valid filters with no matches return an empty 200 response.
{"error":{"code":"invalid_parameter","message":"month must be YYYY-MM.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Stop and search
Returns paginated stop-and-search encounters exactly as published by participating forces. Researchers and public-interest developers can compare searches under a particular power across forces or inspect how published outcomes vary by object searched for.
/v1/crime/stop-and-search
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
force |
string | No | — | Force slug derived from the Police.uk archive filename, for example btp. |
from_month |
string | No | — | Inclusive first archive month in YYYY-MM format. |
to_month |
string | No | — | Inclusive last archive month in YYYY-MM format. |
legislation |
string | No | — | Exact legislation text as published. |
object_of_search |
string | No | — | Exact object-of-search text as published. |
page |
integer | No | 1 | One-based result page. |
per_page |
integer | No | 25 | Results per page; minimum 1, maximum 100. |
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/crime/stop-and-search?force=btp&from_month=2026-06&to_month=2026-06&object_of_search=Controlled%20drugs"
uri = URI("https://api.govdata.dev/v1/crime/stop-and-search") uri.query = URI.encode_www_form(force: "btp", from_month: "2026-06", to_month: "2026-06", object_of_search: "Controlled drugs") res = Net::HTTP.get_response(uri)
response = requests.get("https://api.govdata.dev/v1/crime/stop-and-search", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"force": "btp", "from_month": "2026-06", "to_month": "2026-06", "object_of_search": "Controlled drugs"})
const url = new URL("https://api.govdata.dev/v1/crime/stop-and-search"); url.search = new URLSearchParams({ force: "btp", from_month: "2026-06", to_month: "2026-06", object_of_search: "Controlled drugs" }); const response = await fetch(url, { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{ "data": [{ "month": "2026-06", "force": "btp", "type": "Person search", "date": "2026-05-31T23:01:00Z", "part_of_a_policing_operation": null, "policing_operation": null, "latitude": 51.915645, "longitude": -0.67755, "gender": "Male", "age_range": "18-24", "self_defined_ethnicity": "White - English/Welsh/Scottish/Northern Irish/British", "officer_defined_ethnicity": "White", "legislation": "Misuse of Drugs Act 1971 (section 23)", "object_of_search": "Controlled drugs", "outcome": "A no further action disposal", "outcome_linked_to_object_of_search": true, "removal_of_more_than_just_outer_clothing": false }], "meta": {"api_version":"v1","licence":"Open Government Licence v3.0","source":"Police.uk","source_url":"https://data.police.uk/data/"}, "pagination": {"total":197,"page":1,"per_page":25,"total_pages":8} }
The example is from the byte-verbatim 200-row BTP slice extracted from the June 2026 Police.uk archive; three byte-identical source rows converge under the natural digest, leaving 197 records. month is the archive month; date is the encounter timestamp published in the row and can fall just outside it.
Error responses
400 — malformed or reversed month range. There is no record-specific 404 or semantic 422 branch; valid filters with no matches return an empty 200 response.
{"error":{"code":"invalid_parameter","message":"from_month and to_month must be YYYY-MM in ascending order.","documentation_url":"https://docs.govdata.dev/errors/invalid_parameter"}}
Crime categories
Returns the distinct category labels currently present in GovData, for developers building filter menus or validating category values before a crime search. Use it to populate a community-safety widget without hard-coding Police.uk's vocabulary.
/v1/crime/categories
No parameters required.
Waiting for request...
Parameters
| Name | Type | Required | Default | Description |
|---|
This endpoint has no path, query, or pagination parameters.
curl -H "Authorization: Bearer YOUR_API_KEY" "https://api.govdata.dev/v1/crime/categories"
uri = URI("https://api.govdata.dev/v1/crime/categories") req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer YOUR_API_KEY") res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
response = requests.get("https://api.govdata.dev/v1/crime/categories", headers={"Authorization": "Bearer YOUR_API_KEY"})
const response = await fetch("https://api.govdata.dev/v1/crime/categories", { headers: { Authorization: "Bearer YOUR_API_KEY" } });
Response
{ "data": ["Anti-social behaviour", "Burglary", "Public order", "Robbery", "Vehicle crime", "Violence and sexual offences"], "meta": { "api_version": "v1", "licence": "Open Government Licence v3.0", "source": "Police.uk", "source_url": "https://data.police.uk/data/" } }
This is a data-driven list, so labels appear only when the loaded coverage contains at least one record in that category.
Error responses
There are no endpoint-specific 400, 404, or 422 responses because the endpoint accepts no parameters; an empty dataset returns data: [] with HTTP 200.
Data notes
Coverage and cadence: monthly street-level records for participating police forces in England, Wales and Northern Ireland, with archive coverage back to December 2010. Police.uk normally publishes each month with an approximately two-month lag; GovData imports the monthly bulk archive after publication. Scotland is not covered.
Stop-and-search gaps: PSNI does not publish stop-and-search records in the Police.uk archive, so Northern Ireland has no coverage for this endpoint. Other forces can omit their stop-and-search CSV in individual months; absence means no published file, not necessarily zero searches.
Demographic fields: self-defined ethnicity records the subject's published description, while officer-defined ethnicity records the officer's published assessment. They are distinct source fields, can be blank, and must not be treated as interchangeable or as verified identity data.
Licence and attribution: source Police.uk bulk data, licensed under the Open Government Licence v3.0. Attribute Police.uk when republishing.
Police.uk anonymises locations to nearby snap-points; coordinates are not exact incident locations and an LSOA describes the anonymised point. This is operational police data and is not official statistics. Recording, geocoding, and outcome practices vary between forces, and later publications may revise records.
Some source rows have a blank Crime ID. GovData returns persistent_id: null and uses a deterministic digest only for internal deduplication. Byte-identical blank-ID incidents can therefore merge, and blank-ID crimes cannot be linked to outcomes.