{
  "openapi": "3.0.3",
  "info": {
    "title": "UCRLens — US City Crime Statistics",
    "version": "0.1.0",
    "description": "Annual crime statistics for 17,000+ US cities and all 50 states, derived from the FBI Uniform Crime Reporting (UCR) Return A (RETA) master file. All 8 Part I offenses with per-100k rates, 8-year trends (2018–2025), and coverage metadata on every response. Informational use only — not legal or investment advice.",
    "contact": {
      "name": "Remulous Labs",
      "url": "https://remulouslabs.com"
    }
  },
  "servers": [
    { "url": "https://ucrlens-api.remulouslabs.com" }
  ],
  "x-tiers": {
    "description": "Quota-only: every plan returns the same full dataset and all filters; plans differ only by monthly call quota enforced at the RapidAPI gateway. No in-app feature gating."
  },
  "paths": {
    "/v1/crime/city": {
      "get": {
        "summary": "Crime statistics for a US city",
        "description": "Returns annual Part I offense counts and per-100k rates for the requested city. Omit `year` to get all available years (2018–2025).",
        "operationId": "getCityStats",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "City name (case-insensitive).",
            "example": "Dayton"
          },
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Two-letter state abbreviation.",
            "example": "OH"
          },
          {
            "name": "year",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 2018, "maximum": 2025 },
            "description": "Filter to a single data year. Omit for all years.",
            "example": 2024
          }
        ],
        "responses": {
          "200": {
            "description": "City crime statistics.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CrimeResultList" },
                "example": {
                  "results": [{
                    "location": "DAYTON",
                    "state": "OH",
                    "population": 137644,
                    "data_year": 2024,
                    "data_source": "FBI UCR RETA reta-latest.zip, year 2024",
                    "coverage_rate": 1.0,
                    "violent_crime": { "rate_per_100k": 1842.3, "count": 2536 },
                    "property_crime": { "rate_per_100k": 4201.7, "count": 5784 },
                    "offenses": {
                      "murder": { "rate_per_100k": 14.5, "count": 20 },
                      "rape": { "rate_per_100k": 88.6, "count": 122 },
                      "robbery": { "rate_per_100k": 201.2, "count": 277 },
                      "aggravated_assault": { "rate_per_100k": 1538.0, "count": 2117 },
                      "burglary": { "rate_per_100k": 523.1, "count": 720 },
                      "larceny": { "rate_per_100k": 3204.4, "count": 4411 },
                      "motor_vehicle_theft": { "rate_per_100k": 474.2, "count": 653 },
                      "arson": { "rate_per_100k": null, "count": null }
                    },
                    "disclaimer": "Derived from FBI UCR Program public data. Informational use only."
                  }]
                }
              }
            }
          },
          "400": { "description": "Missing required parameter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Missing or invalid proxy secret.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "City not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/crime/state": {
      "get": {
        "summary": "Crime statistics for a US state",
        "description": "Returns annual Part I offense totals aggregated from all reporting city agencies in the state. `coverage_rate` reflects the share of state population covered by those agencies.",
        "operationId": "getStateStats",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Two-letter state abbreviation.",
            "example": "OH"
          },
          {
            "name": "year",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 2018, "maximum": 2025 },
            "example": 2024
          }
        ],
        "responses": {
          "200": { "description": "State crime statistics.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CrimeResultList" } } } },
          "400": { "description": "Missing required parameter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Missing or invalid proxy secret.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "State not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/crime/national": {
      "get": {
        "summary": "National-level crime totals",
        "operationId": "getNationalStats",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 2018, "maximum": 2025 }
          }
        ],
        "responses": {
          "200": { "description": "National crime totals.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CrimeResultList" } } } },
          "403": { "description": "Missing or invalid proxy secret.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "No national data available.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/crime/trends": {
      "get": {
        "summary": "Multi-year crime trend for a city",
        "description": "Returns up to `years` years of annual crime data for a city, oldest-first. Use this to chart how crime has changed over time.",
        "operationId": "getCityTrends",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "example": "Dayton"
          },
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "example": "OH"
          },
          {
            "name": "years",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 8, "default": 8 },
            "description": "Number of most-recent years to return (max 8)."
          }
        ],
        "responses": {
          "200": {
            "description": "Multi-year crime trend.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TrendsResult" }
              }
            }
          },
          "400": { "description": "Missing required parameter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Missing or invalid proxy secret.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "City not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/meta": {
      "get": {
        "summary": "Dataset metadata",
        "description": "Returns city and state record counts, year range, and total record count.",
        "operationId": "getMeta",
        "responses": {
          "200": {
            "description": "Dataset metadata.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Meta" },
                "example": {
                  "city_count": 17124,
                  "state_count": 51,
                  "earliest_year": 2018,
                  "latest_year": 2025,
                  "total_records": 133283,
                  "data_source": "FBI Uniform Crime Reporting (UCR) Program"
                }
              }
            }
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "summary": "Health check",
        "operationId": "healthz",
        "responses": {
          "200": {
            "description": "Service is healthy.",
            "content": {
              "application/json": {
                "example": { "status": "ok", "version": "0.1.0" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "OffenseDetail": {
        "type": "object",
        "properties": {
          "count":        { "type": "integer", "nullable": true },
          "rate_per_100k": { "type": "number", "nullable": true, "description": "Incidents per 100,000 population." }
        }
      },
      "CrimeResult": {
        "type": "object",
        "properties": {
          "location":      { "type": "string" },
          "state":         { "type": "string", "nullable": true },
          "fips":          { "type": "string", "nullable": true },
          "population":    { "type": "integer" },
          "data_year":     { "type": "integer" },
          "data_source":   { "type": "string" },
          "coverage_rate": { "type": "number", "nullable": true, "description": "Share of population covered by reporting agencies (0–1)." },
          "violent_crime":  { "$ref": "#/components/schemas/OffenseDetail" },
          "property_crime": { "$ref": "#/components/schemas/OffenseDetail" },
          "offenses": {
            "type": "object",
            "properties": {
              "murder":              { "$ref": "#/components/schemas/OffenseDetail" },
              "rape":                { "$ref": "#/components/schemas/OffenseDetail" },
              "robbery":             { "$ref": "#/components/schemas/OffenseDetail" },
              "aggravated_assault":  { "$ref": "#/components/schemas/OffenseDetail" },
              "burglary":            { "$ref": "#/components/schemas/OffenseDetail" },
              "larceny":             { "$ref": "#/components/schemas/OffenseDetail" },
              "motor_vehicle_theft": { "$ref": "#/components/schemas/OffenseDetail" },
              "arson":               { "$ref": "#/components/schemas/OffenseDetail" }
            }
          },
          "disclaimer": { "type": "string" }
        }
      },
      "CrimeResultList": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CrimeResult" }
          }
        }
      },
      "TrendsResult": {
        "type": "object",
        "properties": {
          "location": { "type": "string" },
          "state":    { "type": "string" },
          "trends": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CrimeResult" }
          }
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "city_count":    { "type": "integer" },
          "state_count":   { "type": "integer" },
          "earliest_year": { "type": "integer" },
          "latest_year":   { "type": "integer" },
          "total_records": { "type": "integer" },
          "data_source":   { "type": "string" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" }
        }
      }
    }
  }
}
