{
  "openapi": "3.1.0",
  "info": {
    "title": "StreetSignal API",
    "version": "1.0.0",
    "description": "Neighbourhood intelligence data for 744 Cape Town suburbs -- our pilot city. Static JSON API served via Cloudflare CDN. Free for non-commercial use with attribution.",
    "contact": {
      "email": "info@streetsignal.co.za",
      "url": "https://streetsignal.co.za/api/"
    },
    "license": {
      "name": "Free for non-commercial use",
      "url": "https://streetsignal.co.za/legal/terms/"
    }
  },
  "servers": [
    {
      "url": "https://streetsignal.co.za/api/v1",
      "description": "Production CDN"
    }
  ],
  "paths": {
    "/meta.json": {
      "get": {
        "operationId": "getMeta",
        "summary": "API metadata",
        "description": "Returns API version, suburb count, data source descriptions, endpoint paths, and licence information.",
        "responses": {
          "200": {
            "description": "API metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Meta"
                }
              }
            }
          }
        }
      }
    },
    "/suburbs.json": {
      "get": {
        "operationId": "listSuburbs",
        "summary": "All suburbs (summary)",
        "description": "Returns a lightweight array of all 744 suburbs with key metrics including safety score, risk profile, property valuation, school count, healthcare access, civic responsiveness, and taxi route connectivity.",
        "responses": {
          "200": {
            "description": "Array of suburb summaries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SuburbSummary"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/suburbs/{slug}.json": {
      "get": {
        "operationId": "getSuburb",
        "summary": "Suburb detail",
        "description": "Returns a full profile for a single suburb including safety, property, education, transport, services, healthcare, and census data.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "URL-safe suburb identifier (e.g. camps-bay, woodstock, khayelitsha)",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$",
              "example": "camps-bay"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full suburb profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuburbDetail"
                }
              }
            }
          },
          "404": {
            "description": "Suburb not found. The file does not exist on the CDN."
          }
        }
      }
    },
    "/suburbs.csv": {
      "get": {
        "operationId": "listSuburbsCsv",
        "summary": "All suburbs (CSV bulk download)",
        "description": "The same summary dataset as /suburbs.json, as a CSV file with one row per suburb, for spreadsheet users and bulk analysis. Columns: slug, name, lat, lng, safety_score, risk_profile, median_gv, property_percentile, school_count, healthcare_facilities_5km, civic_responsiveness_percentile, taxi_routes.",
        "responses": {
          "200": {
            "description": "CSV file, header row plus one row per suburb",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Coordinates": {
        "type": "object",
        "required": [
          "lat",
          "lng"
        ],
        "properties": {
          "lat": {
            "type": "number",
            "description": "Latitude (WGS84)",
            "example": -33.955651
          },
          "lng": {
            "type": "number",
            "description": "Longitude (WGS84)",
            "example": 18.381376
          }
        }
      },
      "Meta": {
        "type": "object",
        "required": [
          "api_version",
          "generated_at",
          "suburb_count",
          "city",
          "country",
          "base_url",
          "endpoints",
          "data_sources",
          "schema",
          "licence",
          "usage_restriction",
          "methodology"
        ],
        "properties": {
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp of when the data was generated"
          },
          "suburb_count": {
            "type": "integer",
            "example": 744
          },
          "city": {
            "type": "string",
            "example": "Cape Town"
          },
          "country": {
            "type": "string",
            "example": "South Africa"
          },
          "base_url": {
            "type": "string",
            "format": "uri",
            "example": "https://streetsignal.co.za/api/v1"
          },
          "endpoints": {
            "type": "object",
            "properties": {
              "meta": {
                "type": "string"
              },
              "suburbs_summary": {
                "type": "string"
              },
              "suburb_detail": {
                "type": "string"
              }
            }
          },
          "data_sources": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Map of data dimension to plain-text source description"
          },
          "schema": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Human-readable descriptions of summary and detail schemas"
          },
          "licence": {
            "type": "string"
          },
          "usage_restriction": {
            "type": "string"
          },
          "methodology": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "SuburbSummary": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "coordinates"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "URL-safe suburb identifier",
            "example": "camps-bay"
          },
          "name": {
            "type": "string",
            "description": "Display name",
            "example": "Camps Bay"
          },
          "coordinates": {
            "$ref": "#/components/schemas/Coordinates"
          },
          "safety_score": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Composite safety index (1-100, higher is safer). Null when data is unavailable.",
            "minimum": 1,
            "maximum": 100,
            "example": 46
          },
          "risk_profile": {
            "type": "string",
            "enum": [
              "Lower reported crime",
              "Moderate reported crime",
              "Higher reported crime",
              "No data"
            ],
            "description": "Categorical risk classification derived from safety score",
            "example": "Moderate reported crime"
          },
          "median_gv": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median municipal general valuation (GV 2022) in ZAR. Null when data is unavailable.",
            "example": 12500000
          },
          "property_percentile": {
            "type": [
              "number",
              "null"
            ],
            "description": "Property value percentile rank among all 744 suburbs (0-100)",
            "minimum": 0,
            "maximum": 100,
            "example": 99.2
          },
          "school_count": {
            "type": "integer",
            "description": "Number of schools within the suburb boundary",
            "minimum": 0,
            "example": 3
          },
          "healthcare_facilities_5km": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Number of healthcare facilities within 5 km of the suburb centroid",
            "example": 3
          },
          "civic_responsiveness_percentile": {
            "type": [
              "integer",
              "null"
            ],
            "description": "C3 service delivery responsiveness percentile (0-100)",
            "minimum": 0,
            "maximum": 100,
            "example": 58
          },
          "taxi_routes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Number of minibus taxi routes serving the suburb",
            "example": 10
          }
        }
      },
      "Safety": {
        "type": "object",
        "required": [
          "score",
          "risk_profile",
          "trend",
          "is_proxy_precinct"
        ],
        "properties": {
          "score": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Composite safety index (1-100, higher is safer)",
            "minimum": 1,
            "maximum": 100,
            "example": 46
          },
          "risk_profile": {
            "type": "string",
            "enum": [
              "Lower reported crime",
              "Moderate reported crime",
              "Higher reported crime",
              "No data"
            ],
            "example": "Moderate reported crime"
          },
          "trend": {
            "type": "string",
            "enum": [
              "Up",
              "Down",
              "Stable"
            ],
            "description": "Quarter-on-quarter trend direction",
            "example": "Down"
          },
          "is_proxy_precinct": {
            "type": "boolean",
            "description": "True if this suburb uses a neighbouring precinct as a proxy (no dedicated station)"
          },
          "tourist_paradox": {
            "type": "boolean",
            "description": "True if the suburb's crime rate is inflated by a large transient visitor population relative to its resident count"
          }
        }
      },
      "Property": {
        "type": "object",
        "required": [
          "median_gv",
          "percentile",
          "cagr",
          "median_band"
        ],
        "properties": {
          "median_gv": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median municipal general valuation (GV 2022) in ZAR",
            "example": 12500000
          },
          "percentile": {
            "type": [
              "number",
              "null"
            ],
            "description": "Percentile rank among all suburbs (0-100)",
            "example": 99.2
          },
          "cagr": {
            "type": [
              "number",
              "null"
            ],
            "description": "Compound annual growth rate of valuations",
            "example": -0.0087
          },
          "median_band": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable price band",
            "example": "10-15M"
          }
        }
      },
      "Education": {
        "type": "object",
        "required": [
          "school_count",
          "matric_pass_rate"
        ],
        "properties": {
          "school_count": {
            "type": "integer",
            "minimum": 0,
            "example": 3
          },
          "matric_pass_rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Average matric pass rate across schools in the suburb (percentage)",
            "example": 96.1
          }
        }
      },
      "Transport": {
        "type": "object",
        "required": [
          "dominant_mode",
          "taxi_routes"
        ],
        "properties": {
          "dominant_mode": {
            "type": "string",
            "description": "Primary transport mode for residents (from Census 2022)",
            "example": "vehicle"
          },
          "taxi_routes": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Number of minibus taxi routes",
            "example": 10
          }
        }
      },
      "Services": {
        "type": "object",
        "required": [
          "civic_responsiveness_percentile",
          "median_resolution_days"
        ],
        "properties": {
          "civic_responsiveness_percentile": {
            "type": [
              "integer",
              "null"
            ],
            "description": "C3 service delivery responsiveness percentile (0-100)",
            "example": 58
          },
          "median_resolution_days": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median days to resolve a C3 service complaint",
            "example": 1
          }
        }
      },
      "Healthcare": {
        "type": "object",
        "required": [
          "facilities_within_5km",
          "nearest_facility_km"
        ],
        "properties": {
          "facilities_within_5km": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Number of healthcare facilities within 5 km",
            "example": 3
          },
          "nearest_facility_km": {
            "type": [
              "number",
              "null"
            ],
            "description": "Distance to the nearest healthcare facility in kilometres",
            "example": 3.4
          }
        }
      },
      "Census": {
        "type": "object",
        "required": [
          "population"
        ],
        "properties": {
          "population": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Resident population from Census 2022 sub-place crosswalk",
            "example": 4296
          }
        }
      },
      "SuburbDetail": {
        "type": "object",
        "required": [
          "slug",
          "name",
          "coordinates",
          "police_station",
          "is_proxy_precinct",
          "safety",
          "property",
          "education",
          "transport",
          "services",
          "healthcare",
          "census"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "example": "camps-bay"
          },
          "name": {
            "type": "string",
            "example": "Camps Bay"
          },
          "coordinates": {
            "$ref": "#/components/schemas/Coordinates"
          },
          "police_station": {
            "type": "string",
            "description": "Name of the SAPS police station covering this suburb",
            "example": "Camps Bay"
          },
          "is_proxy_precinct": {
            "type": "boolean",
            "description": "True if the suburb uses a neighbouring precinct as a proxy"
          },
          "safety": {
            "$ref": "#/components/schemas/Safety"
          },
          "property": {
            "$ref": "#/components/schemas/Property"
          },
          "education": {
            "$ref": "#/components/schemas/Education"
          },
          "transport": {
            "$ref": "#/components/schemas/Transport"
          },
          "services": {
            "$ref": "#/components/schemas/Services"
          },
          "healthcare": {
            "$ref": "#/components/schemas/Healthcare"
          },
          "census": {
            "$ref": "#/components/schemas/Census"
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Reserved for future use. The free API does not require authentication. A bearer token scheme will be introduced with the commercial tier."
      }
    }
  },
  "security": [],
  "x-rate-limiting": {
    "description": "Reserved for future use. The free static API has no rate limits. Commercial tiers will include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers on authenticated requests."
  }
}
