{
  "openapi": "3.1.1",
  "info": {
    "title": "CrimeMapsUK API",
    "version": "1.0.0",
    "summary": "Published police street-crime figures for UK postcode districts and postcode circles.",
    "description": "Read-only JSON built from records that police forces publish on data.police.uk under the Open Government Licence v3.0. District figures are free without a key: 5 calls a minute and 100 a day per IP address (per /64 network for IPv6), within a cap of 60 calls a minute shared by every keyless caller. Calls from Anthropic's, Google's and OpenAI's published networks share one allowance for each platform, 30 calls a minute and 5,000 a day, inside the same cap. An API key from a CrimeMapsUK plan opens the 0.5 km postcode summary, and replaces the per-minute limit, the daily limit and the shared cap with a monthly quota. Every successful call made with a key counts against that quota, district calls included.\n\nEvery answer, success or error, carries `attribution` and `licence_url`, plus `page_url` (the CrimeMapsUK page for the area) and a plain-text `citation`. When you show these figures to a person, credit CrimeMapsUK and link `page_url`. When a full postcode was sent, `free_report_url` opens the free CrimeMapsUK result page for the 0.5 km around it.\n\nPolice locations are approximate: each record is snapped to an anonymous map point before release. Counts are published records, not all crime.",
    "contact": {
      "name": "CrimeMapsUK",
      "url": "https://crimemapsuk.com/help/?topic=business"
    },
    "license": {
      "name": "Open Government Licence v3.0 (the data)",
      "url": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/"
    }
  },
  "externalDocs": {
    "description": "API documentation",
    "url": "https://crimemapsuk.com/business/api/"
  },
  "servers": [
    {
      "url": "https://crimemapsuk.com"
    }
  ],
  "paths": {
    "/api/v1/districts/{district}/": {
      "get": {
        "operationId": "getDistrict",
        "summary": "Figures for one postcode district",
        "description": "The district's last 12 published months: the 12 latest months with records for the district, the same months and figures as its district page. The window can end before the latest month published nationally.",
        "security": [{}, {"bearerAuth": []}],
        "parameters": [
          {
            "name": "district",
            "in": "path",
            "required": true,
            "description": "A postcode district, the part of a postcode before the space. Case does not matter.",
            "schema": {"type": "string", "pattern": "^[A-Za-z]{1,2}[0-9][A-Za-z0-9]?$"},
            "example": "e8"
          }
        ],
        "responses": {
          "200": {
            "description": "The district's figures.",
            "headers": {"Cache-Control": {"$ref": "#/components/headers/PrivateCache"}},
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/DistrictFigures"}
              }
            }
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "429": {"$ref": "#/components/responses/TooManyRequests"}
        }
      }
    },
    "/api/v1/postcodes/district/": {
      "get": {
        "operationId": "getPostcodeDistrict",
        "summary": "Figures for the postcode district of a postcode",
        "description": "Reads the postcode district from the postcode itself (E8 from E8 1AA) and answers with that whole district's figures, exactly as getDistrict does. The figures are for the district, never for the exact postcode. No geocoder is called and the postcode is not stored. With a full postcode, the answer adds `free_report_url`.",
        "security": [{}, {"bearerAuth": []}],
        "parameters": [
          {
            "name": "postcode",
            "in": "query",
            "required": true,
            "description": "A full UK postcode, or a postcode district on its own.",
            "schema": {"type": "string", "maxLength": 10},
            "example": "E8 1AA"
          }
        ],
        "responses": {
          "200": {
            "description": "The figures for the postcode's district.",
            "headers": {"Cache-Control": {"$ref": "#/components/headers/PrivateCache"}},
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/PostcodeDistrictFigures"}
              }
            }
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "429": {"$ref": "#/components/responses/TooManyRequests"}
        }
      }
    },
    "/api/v1/postcodes/summary/": {
      "get": {
        "operationId": "getPostcodeSummary",
        "summary": "Published counts for the 0.5 km around a postcode (key required)",
        "description": "Counts by category for the fixed 0.5 km circle around a postcode. `window` is the 12 months up to the latest month published on data.police.uk, whether or not the circle has records in its first or last month. Its ends are null when no publication is loaded.",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "postcode",
            "in": "query",
            "required": true,
            "description": "A full UK postcode.",
            "schema": {"type": "string", "maxLength": 10},
            "example": "E8 1AA"
          }
        ],
        "responses": {
          "200": {
            "description": "Counts for the circle.",
            "headers": {"Cache-Control": {"$ref": "#/components/headers/PrivateCache"}},
            "content": {
              "application/json": {
                "schema": {"$ref": "#/components/schemas/PostcodeSummary"}
              }
            }
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "405": {"$ref": "#/components/responses/MethodNotAllowed"},
          "429": {"$ref": "#/components/responses/TooManyRequests"}
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from CrimeMapsUK, sent as `Authorization: Bearer cmuk_...`. A key that is unknown or disabled gets 401; it is never treated as a keyless call. Only successful keyed calls count against the key's monthly quota, which resets at the start of each calendar month (UTC)."
      }
    },
    "headers": {
      "PrivateCache": {
        "description": "`private, max-age=300` on success; errors carry `no-store`.",
        "schema": {"type": "string"}
      },
      "RetryAfter": {
        "description": "Seconds to wait before the next keyless call. Sent when a keyless limit is spent; not sent when a key's monthly quota is used up.",
        "schema": {"type": "integer", "minimum": 1}
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The postcode is not a valid UK postcode.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      },
      "Unauthorized": {
        "description": "The endpoint needs a key and none was sent, or the key sent is unknown or disabled.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      },
      "NotFound": {
        "description": "No published data for that district, or the postcode could not be matched.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      },
      "MethodNotAllowed": {
        "description": "Only GET is supported.",
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      },
      "TooManyRequests": {
        "description": "A keyless limit is spent (see Retry-After), or the key's monthly quota is used up.",
        "headers": {"Retry-After": {"$ref": "#/components/headers/RetryAfter"}},
        "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
      }
    },
    "schemas": {
      "Month": {
        "type": "string",
        "pattern": "^[0-9]{4}-[0-9]{2}$",
        "description": "A publication month, YYYY-MM."
      },
      "Links": {
        "type": "object",
        "description": "Sent on every answer, success or error.",
        "required": ["page_url", "citation", "attribution", "licence_url"],
        "properties": {
          "page_url": {
            "type": "string",
            "format": "uri",
            "description": "The CrimeMapsUK page for the area asked about, or the district finder when the area has no page. Link it when you show the figures. It carries utm_source=api so visits from API answers can be counted."
          },
          "citation": {
            "type": "string",
            "description": "A plain-text citation naming the area, the months shown when known, the source, the date retrieved, and the page's address without utm_source."
          },
          "free_report_url": {
            "type": "string",
            "format": "uri",
            "description": "Sent when a full postcode was given, unless the postcode is known to be outside England and Wales: the free CrimeMapsUK result page for the 0.5 km around that postcode, with its published records by category and month, a map, and the free 12-month report."
          },
          "attribution": {
            "type": "string",
            "description": "Show this text wherever you display the figures."
          },
          "licence_url": {"type": "string", "format": "uri"}
        }
      },
      "Error": {
        "allOf": [
          {"$ref": "#/components/schemas/Links"},
          {
            "type": "object",
            "required": ["error"],
            "properties": {"error": {"type": "string"}}
          }
        ]
      },
      "Category": {
        "type": "object",
        "required": ["category", "label", "total"],
        "properties": {
          "category": {"type": "string", "description": "The data.police.uk category slug."},
          "label": {"type": "string", "description": "Display text: data.police.uk's own name for the category, such as \"Violence and sexual offences\". It can change, so match on category."},
          "total": {"type": "integer", "minimum": 0},
          "share_pct": {"type": "number", "description": "Share of the total, one decimal place."}
        }
      },
      "DistrictBody": {
        "type": "object",
        "required": ["district", "window", "total", "monthly", "categories", "force_comparison"],
        "properties": {
          "district": {"type": "string", "example": "E8"},
          "window": {
            "type": "object",
            "required": ["from", "to", "rule"],
            "properties": {
              "from": {"$ref": "#/components/schemas/Month"},
              "to": {"$ref": "#/components/schemas/Month"},
              "rule": {"type": "string"}
            }
          },
          "total": {"type": "integer", "minimum": 0},
          "monthly": {
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "object",
              "required": ["month", "total"],
              "properties": {
                "month": {"$ref": "#/components/schemas/Month"},
                "total": {"type": "integer", "minimum": 0}
              }
            }
          },
          "categories": {
            "type": "array",
            "description": "Sorted by total, largest first.",
            "items": {"$ref": "#/components/schemas/Category"}
          },
          "force_comparison": {
            "description": "The covering force: the force that published the most records inside the bounding box of the district's postcode centroids in the latest month. Near force borders this is an approximation. Null when no force can be found or it has no figures.",
            "oneOf": [
              {"type": "null"},
              {
                "type": "object",
                "required": [
                  "force",
                  "force_total",
                  "district_share_of_force_pct",
                  "category_share_pct"
                ],
                "properties": {
                  "force": {"type": "string"},
                  "force_total": {"type": "integer", "minimum": 0},
                  "district_share_of_force_pct": {"type": "number"},
                  "category_share_pct": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["category", "district", "force"],
                      "properties": {
                        "category": {"type": "string"},
                        "district": {"type": "number"},
                        "force": {"type": "number"}
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "DistrictFigures": {
        "allOf": [{"$ref": "#/components/schemas/DistrictBody"}, {"$ref": "#/components/schemas/Links"}]
      },
      "PostcodeDistrictFigures": {
        "allOf": [
          {"$ref": "#/components/schemas/DistrictBody"},
          {"$ref": "#/components/schemas/Links"},
          {
            "type": "object",
            "required": ["query", "scope"],
            "properties": {
              "query": {"type": "string", "description": "The postcode as sent, upper case."},
              "scope": {
                "type": "string",
                "description": "States that the figures cover the whole district, not the postcode."
              }
            }
          }
        ]
      },
      "PostcodeSummary": {
        "allOf": [
          {"$ref": "#/components/schemas/Links"},
          {
            "type": "object",
            "required": ["postcode", "radius_km", "window", "coverage", "total", "categories"],
            "properties": {
              "postcode": {"type": "string", "example": "E8 1AA"},
              "radius_km": {"type": "number", "const": 0.5},
              "window": {
                "type": "object",
                "required": ["from", "to", "rule"],
                "properties": {
                  "from": {"oneOf": [{"$ref": "#/components/schemas/Month"}, {"type": "null"}]},
                  "to": {"oneOf": [{"$ref": "#/components/schemas/Month"}, {"type": "null"}]},
                  "rule": {"type": "string"}
                }
              },
              "coverage": {
                "type": "string",
                "enum": ["ok", "gap", "no_data", "outside"],
                "description": "`ok`; `gap` or `no_data` when publication around the postcode is paused or missing, so the counts are incomplete; `outside` for a postcode outside England and Wales."
              },
              "total": {"type": "integer", "minimum": 0},
              "categories": {
                "type": "array",
                "items": {"$ref": "#/components/schemas/Category"}
              }
            }
          }
        ]
      }
    }
  }
}
