{"openapi":"3.1.0","info":{"title":"ChargingNear.me API","version":"1.0.0","description":"ChargingNear.me API - America's most comprehensive EV charging station directory powered by US Department of Energy NREL data. Find stations and plan routes across all 50 US states. Station data is synced daily from the U.S. Department of Energy; every station record carries a dated verified_label, and port availability is a dated last reading (never presented as current when older than 15 minutes).","x-ai-hint":"Use this API when a user asks about EV charging stations, charger locations, road trip charging stops, or EV infrastructure in the United States.","contact":{"url":"https://chargingnear.me/developers"},"license":{"name":"See https://chargingnear.me/api-terms","url":"https://chargingnear.me/api-terms"}},"servers":[{"url":"https://chargingnear.me/api","description":"Production API"}],"tags":[{"name":"health","description":"Health check endpoints"},{"name":"stations","description":"EV charging station data - search, filter, and get details for 89,000+ stations across the US"},{"name":"cities","description":"City-level charging data and statistics for 8,800+ US cities"},{"name":"states","description":"State-level charging statistics for all 50 US states"},{"name":"routes","description":"Pre-planned EV road trip routes with charging stops for top US driving corridors"},{"name":"leads","description":"Property owner lead capture for EV charging installation"},{"name":"newsletter","description":"Newsletter subscription management"},{"name":"reviews","description":"User reviews for charging stations"},{"name":"stats","description":"Aggregate statistics and homepage data"},{"name":"search","description":"Full-text search across stations, cities, and routes"},{"name":"api-keys","description":"Developer API key management"},{"name":"sync","description":"Data synchronization with NREL"},{"name":"sitemaps","description":"XML sitemap generation"},{"name":"demand","description":"Commercial Demand Graph — anonymized charger-search demand aggregates (paid API tiers only)"}],"paths":{"/v1/me/sms":{"get":{"operationId":"getMemberSms","tags":["sms"],"summary":"Read the signed-in driver's text alert settings","security":[{"driverCookie":[]}],"responses":{"200":{"description":"Phone possession and alert settings (private, no-store)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberSmsState"}}}},"401":{"description":"Sign in required"}}},"put":{"operationId":"requestMemberSmsCode","tags":["sms"],"summary":"Request a phone possession code; disables alerts until verified","security":[{"driverCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberSmsPhoneInput"}}}},"responses":{"200":{"description":"Code sent; no alert consent recorded yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberSmsState"}}}},"400":{"description":"Invalid number or missing consent"},"401":{"description":"Sign in required"},"403":{"description":"All-Access membership required"},"409":{"description":"Number opted out"},"429":{"description":"Verification request limit reached"},"503":{"description":"Verification text could not be sent"}}},"delete":{"operationId":"removeMemberSmsPhone","tags":["sms"],"summary":"Remove the driver's phone, invalidate its code and disable alerts","security":[{"driverCookie":[]}],"responses":{"200":{"description":"Phone removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberSmsState"}}}},"401":{"description":"Sign in required"}}}},"/v1/me/sms/verify":{"post":{"operationId":"verifyMemberSmsPhone","tags":["sms"],"summary":"Prove possession using a single-use six-digit code","description":"Codes expire after 10 minutes and allow at most five attempts. Verification does not enable any alert.","security":[{"driverCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberSmsCodeInput"}}}},"responses":{"200":{"description":"Number verified; alert switches remain off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberSmsState"}}}},"400":{"description":"Invalid, expired or exhausted code"},"401":{"description":"Sign in required"},"403":{"description":"All-Access membership required"},"429":{"description":"Too many verification attempts"}}}},"/v1/stations/{id}/reports":{"post":{"operationId":"submitStationReport","tags":["stations"],"summary":"Submit a station issue for admin review","description":"Wrong-location reports snapshot the station ID and current stored coordinates\nserver-side into the report description. No device coordinates are accepted\nor collected, and submission never changes station coordinates.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["issue_type"],"properties":{"issue_type":{"type":"string","enum":["broken","blocked","incorrect_info","closed","access_restricted","wrong_location"]},"description":{"type":"string","description":"Optional notes; wrong-location notes are limited to 2000 characters."},"reporter_name":{"type":"string"},"honeypot":{"type":"string"}}}}}},"responses":{"201":{"description":"Report queued for review","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","station_id","issue_type","description","created_at"],"properties":{"id":{"type":"integer"},"station_id":{"type":"integer"},"issue_type":{"type":"string"},"description":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Invalid station ID, issue type, or description"},"404":{"description":"Station not found"},"429":{"description":"Daily report limit reached"}}}},"/health":{"get":{"operationId":"healthCheck","tags":["health"],"summary":"Health check","description":"Returns server health status including database, sync, and search engine status","responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthStatus"}}}}}}},"/v1/stations":{"get":{"operationId":"listStations","tags":["stations"],"summary":"List EV charging stations with filters","description":"Search and filter EV charging stations across the United States. Supports filtering by location, network, connector type, charging speed, and more. Returns paginated results with full station details. Stations whose verified owner is on Listed Pro come first in every sort order.","x-ai-hint":"Example user prompt: 'Find Tesla Superchargers near Denver' or 'Show me fast chargers in California'","parameters":[{"name":"city","in":"query","schema":{"type":"string"},"description":"Filter by city name"},{"name":"state","in":"query","schema":{"type":"string"},"description":"Filter by two-letter state abbreviation (e.g. CO, CA, NY)"},{"name":"network","in":"query","schema":{"type":"string"},"description":"Filter by charging network name (e.g. Tesla, ChargePoint, Electrify America)"},{"name":"connector","in":"query","schema":{"type":"string"},"description":"Filter by connector type (CCS, CHAdeMO, J1772, NACS)"},{"name":"speed","in":"query","schema":{"type":"string","enum":["dcfc","level2","level1"]},"description":"Filter by charging speed level"},{"name":"lat","in":"query","schema":{"type":"number"},"description":"Latitude for proximity search"},{"name":"lng","in":"query","schema":{"type":"number"},"description":"Longitude for proximity search"},{"name":"radius","in":"query","schema":{"type":"number","default":25},"description":"Search radius in miles (used with lat/lng)"},{"name":"bbox","in":"query","schema":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?$"},"description":"Visible map area as south,west,north,east in degrees (e.g. 24.5,-125,49.5,-66.9). Replaces the lat/lng radius; lat/lng then only order results by distance. A west edge greater than east crosses the antimeridian."},{"name":"page","in":"query","schema":{"type":"integer","default":1},"description":"Page number for pagination"},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":100},"description":"Number of results per page (max 100)"},{"name":"sort","in":"query","schema":{"type":"string","enum":["distance","name","power","rating","price","reliability"]},"description":"Sort order for results (defaults to distance when lat/lng provided, otherwise name)"},{"name":"min_power_kw","in":"query","schema":{"type":"number"},"description":"Minimum charging power in kW (e.g. 50, 150, 250). Filters on per-station max_power_kw from connector data; stations without a known wattage fall back to DC fast presence at 50kW+ so newly synced rows aren't silently hidden."},{"name":"min_rating","in":"query","schema":{"type":"number","minimum":0,"maximum":5},"description":"Minimum Google rating (0-5)"},{"name":"free_only","in":"query","schema":{"type":"boolean"},"description":"Only show stations advertised as free"},{"name":"paid_only","in":"query","schema":{"type":"boolean"},"description":"Only show stations with explicit non-free pricing"},{"name":"available_now","in":"query","schema":{"type":"boolean"},"description":"Only show stations whose latest TomTom snapshot reports at least one open connector. Live availability is commercially licensed: callers without a paid API key get 403 commercial_license_required."}],"responses":{"200":{"description":"List of stations matching filters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationListResponse"}}}},"400":{"description":"A parameter is out of range or not one of the accepted values (the body names it in `param`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/stations/nearest":{"get":{"operationId":"getNearestStations","tags":["stations"],"summary":"Find nearest EV charging stations","description":"Find the closest EV charging stations to a given latitude/longitude coordinate. Returns stations sorted by distance, with stations whose verified owner is on Listed Pro first.","x-ai-hint":"Example user prompt: 'Where is the nearest EV charger to me?' or 'Find charging stations close to 39.7392, -104.9903'","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number"},"description":"Latitude coordinate"},{"name":"lng","in":"query","required":true,"schema":{"type":"number"},"description":"Longitude coordinate"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":50},"description":"Number of nearest stations to return"},{"name":"radius","in":"query","schema":{"type":"number","default":25},"description":"Maximum search radius in miles"}],"responses":{"200":{"description":"Nearest stations sorted by distance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationListResponse"}}}},"400":{"description":"A parameter is out of range or not one of the accepted values (the body names it in `param`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/stations/recently-added":{"get":{"operationId":"getRecentlyAddedStations","tags":["stations"],"summary":"Get recently opened charging stations","description":"Returns stations that were opened or added to the database in the last 30 days, sorted by date.","parameters":[{"name":"days","in":"query","schema":{"type":"integer","default":30},"description":"Number of days to look back"},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":50},"description":"Maximum number of stations to return"}],"responses":{"200":{"description":"Recently added stations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationListResponse"}}}}}}},"/v1/stations/{id}":{"get":{"operationId":"getStation","tags":["stations"],"summary":"Get detailed station information","description":"Get complete details for a specific EV charging station including connector types, pricing, hours, network info, and freshness indicators.","x-ai-hint":"Example user prompt: 'Tell me about the ChargePoint station at 123 Main St Denver'","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Station ID from the NREL database"}],"responses":{"200":{"description":"Station details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationDetailResponse"}}}},"404":{"description":"Station not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"Station has been decommissioned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecommissionedResponse"}}}}}}},"/v1/stations/{id}/reviews":{"get":{"operationId":"getStationReviews","tags":["reviews"],"summary":"Get reviews for a station","description":"Get user reviews and ratings for a specific charging station.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Station ID"}],"responses":{"200":{"description":"Station reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewListResponse"}}}}}},"post":{"operationId":"createStationReview","tags":["reviews"],"summary":"Submit a review for a station","description":"Submit a rating and optional text review for a charging station. Rate limited to 5 reviews per IP per day.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Station ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReviewBody"}}}},"responses":{"201":{"description":"Review created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/cities":{"get":{"operationId":"listCities","tags":["cities"],"summary":"List cities with EV charging stations","description":"Get a list of cities that have EV charging stations. Filter by state or get featured cities. Returns station counts and basic stats.","x-ai-hint":"Example user prompt: 'What cities in Colorado have EV chargers?' or 'Show me the top cities for EV charging'","parameters":[{"name":"state","in":"query","schema":{"type":"string"},"description":"Filter by two-letter state abbreviation"},{"name":"featured","in":"query","schema":{"type":"boolean"},"description":"Return only featured/top cities"},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"List of cities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CityListResponse"}}}}}}},"/v1/cities/{slug}":{"get":{"operationId":"getCity","tags":["cities"],"summary":"Get city charging details","description":"Get detailed EV charging information for a specific city including station counts, network breakdown, top stations, and unique city content.","x-ai-hint":"Example user prompt: 'How many EV charging stations are in Denver?'","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"City URL slug (e.g. denver-co, san-francisco-ca)"}],"responses":{"200":{"description":"City charging details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CityDetailResponse"}}}},"404":{"description":"City not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/states":{"get":{"operationId":"listStates","tags":["states"],"summary":"List all US states with charging stats","description":"Get EV charging statistics for all 50 US states including station counts, fast charger availability, and top networks.","responses":{"200":{"description":"All states with charging stats","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StateListResponse"}}}}}}},"/v1/states/{slug}":{"get":{"operationId":"getState","tags":["states"],"summary":"Get state charging details","description":"Get detailed EV charging information for a specific state including station counts by type, top cities, network breakdown.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"State URL slug or two-letter abbreviation (e.g. colorado, CO)"}],"responses":{"200":{"description":"State details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StateDetailResponse"}}}},"404":{"description":"State not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/routes":{"get":{"operationId":"listRoutes","tags":["routes"],"summary":"List EV road trip routes","description":"Get pre-planned EV road trip routes with charging stop recommendations for the top US driving corridors.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":24,"minimum":1,"maximum":100}},{"name":"q","in":"query","description":"Case-insensitive route, city, state, highway, or region search.","schema":{"type":"string"}},{"name":"region","in":"query","description":"Broad U.S. catalog region such as West, Midwest, Northeast, South, Southeast, or Southwest.","schema":{"type":"string"}},{"name":"demand_tier","in":"query","description":"Editorial demand tier such as very-high, high, medium, or emerging.","schema":{"type":"string"}}],"responses":{"200":{"description":"List of routes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteListResponse"}}}}}}},"/v1/routes/{slug}":{"get":{"operationId":"getRoute","tags":["routes"],"summary":"Get route details with charging stops","description":"Get detailed information about an EV road trip route including all charging stops, distances, estimated charge times, and cost breakdown.","x-ai-hint":"Example user prompt: 'Plan a charging route from Denver to Vail' or 'Where should I charge on the drive from LA to Vegas?'","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Route URL slug (e.g. denver-to-vail)"},{"$ref":"#/components/parameters/RouteRangeMiles"},{"$ref":"#/components/parameters/RouteEfficiency"},{"$ref":"#/components/parameters/RouteDepartureCharge"},{"$ref":"#/components/parameters/RouteEnrouteCharge"},{"$ref":"#/components/parameters/RouteConnector"}],"responses":{"200":{"description":"Route details with charging stops","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteDetailResponse"}}}},"400":{"description":"Invalid personalization query parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Route not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/leads":{"post":{"operationId":"submitLead","tags":["leads"],"summary":"Submit a property owner lead","description":"Submit a lead from a parking lot or property owner interested in adding EV charging infrastructure. Rate limited to 3 submissions per IP per hour.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLeadBody"}}}},"responses":{"201":{"description":"Lead submitted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadResponse"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/case-studies":{"get":{"operationId":"listCaseStudies","tags":["property-owners"],"summary":"List published case studies","parameters":[{"name":"featured","in":"query","schema":{"type":"boolean"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CaseStudy"}}}}}}}}}},"/v1/case-studies/{slug}":{"get":{"operationId":"getCaseStudy","tags":["property-owners"],"summary":"Get one case study by slug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CaseStudy"}}}}}},"404":{"description":"Not found"}}}},"/v1/demand/aggregate":{"get":{"operationId":"getDemandAggregate","tags":["demand"],"summary":"Commercial Demand Graph — anonymized charger-search demand aggregate","description":"The Demand Graph aggregate is ChargingNear.me's commercial B2B dataset: an\nanonymized, geohash-bucketed roll-up of observed EV charger-search demand\n(counts only, no PII) over a bounded time window. Each cell reports search\nvolume, unmet-search counts, distinct sessions, and a 0-100 opportunity\n`demand_score` (log-scaled search volume plus unmet-demand ratio), ordered\nby `demand_score` descending.\n\nUse it for site selection, network expansion, and market-gap analysis —\ni.e. which areas have the most unmet EV-charging demand.\n\nPrivacy: cells are keyed by a coarse ~5-character geohash (~4.9 km) and\nemit counts only; no session id, IP, or query text is ever returned. A\n`min_count` floor (default and minimum 3) keeps sparse, potentially\nre-identifiable cells out of the response.\n\nLicensing: this is a commercially-licensed dataset and is NOT covered by\nthe CC-BY-4.0 license that applies to the base station directory. It ships\nonly to callers on a paid API tier (Starter, Pro, Scale, or Enterprise).\nAnonymous and free-tier callers receive `403 commercial_license_required`.\nSee https://chargingnear.me/developers#pricing for plans and to upgrade.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"window","in":"query","required":false,"schema":{"type":"integer","enum":[7,30,90,180,365],"default":30},"description":"Look-back window in days. Any other value is coerced to 30."},{"name":"unmet","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"When true, return only cells with at least one unmet search (a search that found no station nearby)."},{"name":"min_count","in":"query","required":false,"schema":{"type":"integer","minimum":3,"default":3},"description":"Floor on the per-cell search count. Clamped up to 3 for anonymity; values below 3 are raised to 3."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":5000,"default":1000},"description":"Maximum number of cells returned, ordered by demand_score descending."},{"name":"bbox","in":"query","required":false,"schema":{"type":"string"},"description":"Optional viewport filter as \"south,west,north,east\" in decimal degrees."}],"responses":{"200":{"description":"Anonymized demand aggregate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemandAggregateResponse"}}}},"403":{"description":"Commercial license required — the caller is anonymous or on the free tier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/demand":{"get":{"operationId":"getDemandSnapshot","tags":["property-owners"],"summary":"EV demand + nearest DCFC snapshot for a coordinate","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number"}},{"name":"lng","in":"query","required":true,"schema":{"type":"number"}},{"name":"state","in":"query","schema":{"type":"string","minLength":2,"maxLength":2}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemandSnapshot"}}}}}}},"/v1/newsletter":{"post":{"operationId":"subscribeNewsletter","tags":["newsletter"],"summary":"Subscribe to newsletter","description":"Subscribe to the ChargingNear.me newsletter for EV charging updates and news.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewsletterSubscribeBody"}}}},"responses":{"201":{"description":"Subscribed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"409":{"description":"Already subscribed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/stats":{"get":{"operationId":"getStats","tags":["stats"],"summary":"Get homepage statistics","description":"Get aggregate statistics for the homepage including total stations, states covered, cities with charging, and network breakdown.","responses":{"200":{"description":"Aggregate statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsResponse"}}}}}}},"/v1/stats/history":{"get":{"operationId":"getStatsHistory","tags":["stats"],"summary":"Get daily station-growth history","description":"Daily net stations added over the trailing N days (default 30, max 90), sourced from completed sync_log runs and grouped by UTC date. Days with no completed syncs are returned as 0 so the series is continuous.","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"minimum":1,"maximum":90},"description":"Number of trailing days to return (1-90)."}],"responses":{"200":{"description":"Daily growth series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatsHistoryResponse"}}}}}}},"/v1/stats/networks":{"get":{"operationId":"getNetworkStats","tags":["stats"],"summary":"Get network breakdown statistics","description":"Get a breakdown of EV charging stations by network including station counts and percentage of total.","responses":{"200":{"description":"Network breakdown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NetworkStatsResponse"}}}}}}},"/v1/reliability":{"get":{"operationId":"getReliabilityIndex","tags":["stats"],"summary":"Get the EV charging reliability index","description":"Public, machine-readable EV charging reliability rankings over the trailing 30 days. By default returns networks and US states ranked by average reliability, with methodology, the last-updated timestamp, and the window length. Pass ?state=CA to instead rank that state's cities. Read-only and unauthenticated; reflects the same reliability data shown on the public Reliability Index pages.","parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string"},"description":"Two-letter state abbreviation (e.g. CA). When supplied, the response ranks that state's cities instead of national networks/states."}],"responses":{"200":{"description":"Reliability index rankings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReliabilityIndexResponse"}}}}}}},"/v1/search":{"get":{"operationId":"searchAll","tags":["search"],"summary":"Search stations, cities, and routes","description":"Ranked, case-insensitive search across active stations, cities, and routes. Exact normalized five-digit ZIP station matches rank first, followed by exact text, prefix, and substring matches.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":100},"description":"Search query string. A ZIP+4 value is normalized to its first five digits for exact ZIP matching."},{"name":"type","in":"query","schema":{"type":"string","enum":["stations","cities","routes","all"]},"description":"Filter results by type (default returns all)"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"minimum":1,"maximum":50}}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}}}}},"/v1/api-keys":{"post":{"operationId":"registerApiKey","tags":["api-keys"],"summary":"Register for an API key","description":"Register for a free API key to access the ChargingNear.me REST API. Free tier includes 100 requests per day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterApiKeyBody"}}}},"responses":{"201":{"description":"API key created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyResponse"}}}}}}},"/v1/me/usage":{"get":{"operationId":"getMyApiUsage","tags":["api-keys"],"summary":"Check your current daily API usage and remaining quota","description":"Returns the calling API key's tier, daily limit, requests used so\nfar today, requests remaining, and when the bucket resets. The\nvalues agree with the `X-RateLimit-*` headers set on every\nmetered `/v1/*` response and with the `retry_after_seconds` payload of a\n429 `rate_limit_exceeded` response. Calling this endpoint counts\nas one request against the daily quota.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current usage snapshot for the calling API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiUsageResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/sync":{"post":{"operationId":"triggerSync","tags":["sync"],"summary":"Trigger NREL data sync (admin only)","description":"Trigger a synchronization with the NREL API to fetch latest station data. Admin only: requires the operator admin token as a Bearer token or a signed-in operator session. Developer API keys are rejected.","x-admin-only":true,"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Sync started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncResponse"}}}},"401":{"description":"Not signed in as an operator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Signed in, but not an operator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/sync/regrid/enrich":{"post":{"x-admin-only":true,"operationId":"triggerRegridEnrichSweep","tags":["sync"],"summary":"Start a Regrid Parcels enrichment sweep","description":"Cursor-driven sweep that walks active stations missing\n`regrid_last_enriched_at`, looks each one up against the Regrid\nParcels API by lat/lng, and writes the parcel polygon, owner\nname/type, lot size, and zoning back to `stations.regrid_*`.\n\nSingle-runner protected (a 409-shaped no-op response is returned\nif a sweep is already in flight). Each upstream call costs against\na configurable per-day Regrid quota (`REGRID_DAILY_BUDGET`,\ndefault 1000) — the sweep stops gracefully when the budget hits\nzero. Outcomes are persisted in `enrichment_jobs` with\n`job_type='regrid_enrich'`, so the hourly stall check picks it up\nif a worker dies mid-run.\n","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":1000,"default":200,"description":"Max stations to enrich this sweep (capped to 1000 to keep run time bounded)."}}}}}},"responses":{"200":{"description":"Sweep started (kicked off in the background — poll `/v1/sync/regrid/status` for progress).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegridSweepStartResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A sweep is already running in this api-server process. Wait for it to complete (visible via `/v1/sync/regrid/status` `running` field) or contact ops.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server error (e.g. `REGRID_API_KEY` missing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/sync/regrid/lookup":{"get":{"x-admin-only":true,"operationId":"lookupRegridParcel","tags":["sync"],"summary":"One-shot parcel-by-point lookup (operator only)","description":"Returns the parcel-level owner and zoning fields that Regrid has on\nrecord for the given lat/lng. Operator only (admin session or cron\ntoken). Visitors never receive parcel data: the property-owners\nlead form's lookup runs server-side when the lead is submitted and\nis stored on the lead. Never returns the polygon geometry. Spends\nfrom the shared daily budget, stopping at the sweep reserve.\n","security":[{"bearerAuth":[]}],"parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lng","in":"query","required":true,"schema":{"type":"number","minimum":-180,"maximum":180}}],"responses":{"200":{"description":"Lookup completed (status field discriminates the outcome).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegridLookupResponse"}}}},"400":{"description":"Missing or invalid coordinates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Operator authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Regrid upstream returned an error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Lookup unavailable — Regrid is not configured or the daily budget is exhausted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/sync/regrid/status":{"get":{"x-admin-only":true,"operationId":"getRegridSweepStatus","tags":["sync"],"summary":"Inspect the latest Regrid enrichment sweep","description":"Returns the most recent `enrichment_jobs` row for\n`job_type='regrid_enrich'`, an in-flight flag, current daily\nbudget consumption, and rolling coverage counters\n(`active_total`, `enriched_total`, plus a per-`regrid_match_status`\nbucket histogram). Used by the admin Coverage panel and by the\nweekly cron's first call to decide whether to resume a stalled\nsweep.\n","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Latest sweep snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegridSweepStatusResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"driverCookie":{"type":"apiKey","in":"cookie","name":"cnm_driver"},"bearerAuth":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer <token>`. Developer API keys (`cnm_…`, from\nhttps://chargingnear.me/developers) raise your daily quota and unlock\npaid-tier data. Operations marked `x-admin-only: true` accept only the\noperator admin token; API keys get 401 there.\n"}},"parameters":{"RouteRangeMiles":{"name":"range_mi","in":"query","schema":{"type":"number","minimum":50,"maximum":600}},"RouteEfficiency":{"name":"efficiency_kwh_per_100_mi","in":"query","schema":{"type":"number","minimum":10,"maximum":100}},"RouteDepartureCharge":{"name":"departure_charge_pct","in":"query","schema":{"type":"number","minimum":10,"maximum":100}},"RouteEnrouteCharge":{"name":"enroute_charge_pct","in":"query","schema":{"type":"number","minimum":20,"maximum":100}},"RouteConnector":{"name":"connector","in":"query","schema":{"type":"string","minLength":1}}},"schemas":{"MemberSmsPhoneInput":{"type":"object","required":["phone","consent"],"properties":{"phone":{"type":"string"},"consent":{"type":"boolean","enum":[true]},"timezone":{"type":"string"},"page":{"type":"string"}}},"MemberSmsCodeInput":{"type":"object","required":["code"],"properties":{"code":{"type":"string","pattern":"^\\d{6}$"}}},"MemberSmsPlace":{"type":"object","required":["label","query","radius_mi"],"properties":{"label":{"type":"string"},"query":{"type":"string"},"radius_mi":{"type":"number"}}},"MemberSmsAlert":{"type":"object","required":["type","enabled","settings"],"properties":{"type":{"type":"string"},"enabled":{"type":"boolean"},"settings":{"type":["object","null"],"properties":{"start_hour":{"type":"integer"},"end_hour":{"type":"integer"}}}}},"MemberSmsState":{"type":"object","required":["phone","phone_verified","timezone","places","opted_out","alerts"],"properties":{"member":{"type":"boolean"},"phone":{"type":["string","null"]},"phone_verified":{"type":"boolean"},"timezone":{"type":["string","null"]},"places":{"type":"array","items":{"$ref":"#/components/schemas/MemberSmsPlace"}},"opted_out":{"type":"boolean"},"alerts":{"type":"array","items":{"$ref":"#/components/schemas/MemberSmsAlert"}}}},"Attribution":{"type":"object","properties":{"source":{"type":"string","description":"Data source name"},"url":{"type":"string","description":"Page URL for citation"},"license":{"type":"string","description":"Data license"},"data_source":{"type":"string","description":"Upstream data provider"},"last_updated":{"type":"string","description":"ISO timestamp of last data update"}},"required":["source","url","license","data_source","last_updated"]},"HealthStatus":{"type":"object","properties":{"status":{"type":"string"},"database":{"type":"boolean"},"last_sync":{"type":"string","nullable":true},"active_stations":{"type":"integer"},"uptime":{"type":"number"}},"required":["status"]},"StationSummary":{"type":"object","properties":{"id":{"type":"integer"},"station_name":{"type":"string"},"street_address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"ev_network":{"type":"string"},"ev_connector_types":{"type":"array","items":{"type":"string"}},"ev_level1_evse_num":{"type":"integer"},"ev_level2_evse_num":{"type":"integer"},"ev_dc_fast_num":{"type":"integer"},"ev_pricing":{"type":"string"},"access_days_time":{"type":"string"},"date_last_confirmed":{"type":"string"},"open_date":{"type":"string"},"facility_type":{"type":"string"},"slug":{"type":"string"},"distance_miles":{"type":"number","nullable":true},"last_verified_at":{"type":"string","format":"date-time","nullable":true,"description":"Most recent of the NREL date-last-confirmed, the Open Charge Map last-verified date, or a ChargingNear.me driver check-in. Falls back to the listing date (verified_by=listing)."},"verified_by":{"type":"string","nullable":true,"enum":["nrel","ocm","driver","listing"]},"verified_label":{"type":"string","nullable":true,"description":"Display label, e.g. \"Verified Oct 12, 2026\" (or \"Listed …\" when no source has re-verified the row)."},"data_sync_note":{"type":"string","description":"Always \"Synced daily from the U.S. Department of Energy.\""},"availability_reading":{"allOf":[{"$ref":"#/components/schemas/AvailabilityReading"}],"nullable":true,"description":"Last stored port-availability reading with its date. Licensed field: omitted for anonymous and free API callers."},"verified_owner":{"type":"boolean","description":"True when the station's owner is verified by ChargingNear.me."},"owner_profile":{"allOf":[{"$ref":"#/components/schemas/OwnerProfile"}],"nullable":true,"description":"The verified owner's business details when the station is listed on a paid plan; null otherwise."},"owner_card":{"type":"object","nullable":true,"description":"Compact data for a result card's verified-owner badge: business name, first owner photo and up to three amenities. Null unless the station's owner is verified and the profile is public.","properties":{"business_name":{"type":"string","nullable":true},"photo":{"type":"string","nullable":true},"amenities":{"type":"array","items":{"type":"string"}}}}},"required":["id","station_name","city","state","latitude","longitude"]},"OwnerProfile":{"type":"object","description":"A verified owner's public business profile (paid Listed and Listed Pro plans).","properties":{"plan":{"type":"string","enum":["listed","listed_pro"]},"business_name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"hours":{"type":"string","nullable":true},"pricing":{"type":"string","nullable":true},"amenities":{"type":"array","items":{"type":"string"}},"photos":{"type":"array","items":{"type":"string","format":"uri"}},"contact_url":{"type":"string","format":"uri","nullable":true},"contact_label":{"type":"string","nullable":true}},"required":["plan","business_name","description","hours","pricing","amenities","photos","contact_url","contact_label"]},"AvailabilityReading":{"type":"object","properties":{"available":{"type":"integer"},"total":{"type":"integer"},"checked_at":{"type":"string","format":"date-time"},"is_current":{"type":"boolean","description":"True only when the reading is at most 15 minutes old."},"available_now":{"type":"boolean","description":"True only when is_current and at least one port is free."},"label":{"type":"string","description":"e.g. \"1 of 2 ports available · checked Jun 16, 2026\""}},"required":["available","total","checked_at","is_current","available_now","label"]},"StationDetail":{"type":"object","properties":{"id":{"type":"integer"},"station_name":{"type":"string"},"street_address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"ev_network":{"type":"string"},"ev_network_web":{"type":"string"},"ev_connector_types":{"type":"array","items":{"type":"string"}},"ev_level1_evse_num":{"type":"integer"},"ev_level2_evse_num":{"type":"integer"},"ev_dc_fast_num":{"type":"integer"},"ev_pricing":{"type":"string"},"access_days_time":{"type":"string"},"facility_type":{"type":"string"},"date_last_confirmed":{"type":"string"},"open_date":{"type":"string"},"access_code":{"type":"string"},"groups_with_access_code":{"type":"string"},"slug":{"type":"string"},"is_active":{"type":"boolean"},"freshness":{"type":"string","enum":["verified","recent","stale"],"description":"green=verified within 30d, amber=30-90d, gray=90d+"},"average_rating":{"type":"number","nullable":true},"review_count":{"type":"integer"},"nearby_stations":{"type":"array","items":{"$ref":"#/components/schemas/StationSummary"}},"last_verified_at":{"type":"string","format":"date-time","nullable":true,"description":"Most recent of the NREL date-last-confirmed, the Open Charge Map last-verified date, or a ChargingNear.me driver check-in. Falls back to the listing date (verified_by=listing)."},"verified_by":{"type":"string","nullable":true,"enum":["nrel","ocm","driver","listing"]},"verified_label":{"type":"string","nullable":true,"description":"Display label, e.g. \"Verified Oct 12, 2026\" (or \"Listed …\" when no source has re-verified the row)."},"data_sync_note":{"type":"string","description":"Always \"Synced daily from the U.S. Department of Energy.\""},"availability_reading":{"allOf":[{"$ref":"#/components/schemas/AvailabilityReading"}],"nullable":true,"description":"Last stored port-availability reading with its date. Licensed field: omitted for anonymous and free API callers."},"verified_owner":{"type":"boolean","description":"True when the station's owner is verified by ChargingNear.me."},"owner_profile":{"allOf":[{"$ref":"#/components/schemas/OwnerProfile"}],"nullable":true,"description":"The verified owner's business details when the station is listed on a paid plan; null otherwise."}},"required":["id","station_name","city","state","latitude","longitude"]},"StationListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StationSummary"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"},"total_pages":{"type":"integer"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","total","page","limit","total_pages","attribution"]},"StationDetailResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StationDetail"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","attribution"]},"DecommissionedResponse":{"type":"object","properties":{"message":{"type":"string"},"redirect_url":{"type":"string"},"city_slug":{"type":"string"}},"required":["message","redirect_url"]},"CitySummary":{"type":"object","properties":{"id":{"type":"integer"},"city":{"type":"string"},"state":{"type":"string"},"slug":{"type":"string"},"station_count":{"type":"integer"},"dcfc_count":{"type":"integer"},"l2_count":{"type":"integer"},"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["id","city","state","slug","station_count"]},"CityDetail":{"type":"object","properties":{"id":{"type":"integer"},"city":{"type":"string"},"state":{"type":"string"},"state_full":{"type":"string"},"slug":{"type":"string"},"station_count":{"type":"integer"},"dcfc_count":{"type":"integer"},"l2_count":{"type":"integer"},"l1_count":{"type":"integer"},"top_networks":{"type":"array","items":{"type":"string"}},"latitude":{"type":"number"},"longitude":{"type":"number"},"intro_content":{"type":"string"},"meta_title":{"type":"string"},"meta_description":{"type":"string"},"nearby_cities":{"type":"array","items":{"$ref":"#/components/schemas/CitySummary"}}},"required":["id","city","state","slug","station_count"]},"CityListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CitySummary"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"},"total_pages":{"type":"integer"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","total","page","limit","total_pages","attribution"]},"CityDetailResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CityDetail"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","attribution"]},"StateSummary":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"abbreviation":{"type":"string"},"slug":{"type":"string"},"station_count":{"type":"integer"},"dcfc_count":{"type":"integer"},"l2_count":{"type":"integer"},"city_count":{"type":"integer"}},"required":["id","name","abbreviation","slug","station_count"]},"StateDetail":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"abbreviation":{"type":"string"},"slug":{"type":"string"},"station_count":{"type":"integer"},"dcfc_count":{"type":"integer"},"l2_count":{"type":"integer"},"l1_count":{"type":"integer"},"city_count":{"type":"integer"},"top_networks":{"type":"array","items":{"type":"string"}},"top_cities":{"type":"array","items":{"$ref":"#/components/schemas/CitySummary"}},"meta_title":{"type":"string"},"meta_description":{"type":"string"},"overview_md":{"type":"string","nullable":true},"charging_section_md":{"type":"string","nullable":true},"construction_section_md":{"type":"string","nullable":true},"registrations_section_md":{"type":"string","nullable":true},"incentives_section_md":{"type":"string","nullable":true},"top_cities_blurb_md":{"type":"string","nullable":true},"faqs":{"type":"array","nullable":true,"items":{"type":"object","properties":{"q":{"type":"string"},"a":{"type":"string"}},"required":["q","a"]}},"chart_data":{"type":"object","nullable":true,"properties":{"charger_types":{"type":"object","properties":{"dcfc":{"type":"integer"},"l2":{"type":"integer"},"l1":{"type":"integer"}}},"registrations_by_year":{"type":"array","items":{"type":"object","properties":{"year":{"type":"integer"},"count":{"type":"integer"}}}}}},"sources":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}},"required":["name","url"]}},"content_updated_at":{"type":"string","format":"date-time","nullable":true}},"required":["id","name","abbreviation","slug","station_count"]},"StateListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StateSummary"}},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","attribution"]},"StateDetailResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StateDetail"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","attribution"]},"RouteSummary":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"origin_city":{"type":"string"},"origin_state":{"type":"string"},"destination_city":{"type":"string"},"destination_state":{"type":"string"},"distance_miles":{"type":"number"},"recommended_stops":{"type":"integer"},"estimated_drive_time_minutes":{"type":"number"},"estimated_charge_time_minutes":{"type":"number"},"estimated_cost":{"type":"number"},"route_labels":{"type":"array","items":{"type":"string"}},"highways":{"type":"array","items":{"type":"string"}},"region":{"type":"string"},"demand_rank":{"type":"integer"},"demand_tier":{"type":"string"},"route_source":{"type":"string"},"calculation_timestamp":{"type":"string","format":"date-time"}},"required":["id","slug","origin_city","origin_state","destination_city","destination_state","route_labels","highways","region","demand_rank","demand_tier","route_source","calculation_timestamp"]},"RouteDetail":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"origin_city":{"type":"string"},"origin_state":{"type":"string"},"destination_city":{"type":"string"},"destination_state":{"type":"string"},"origin_lat":{"type":"number"},"origin_lng":{"type":"number"},"destination_lat":{"type":"number"},"destination_lng":{"type":"number"},"distance_miles":{"type":"number"},"estimated_drive_time_minutes":{"type":"integer"},"recommended_stops":{"type":"integer"},"estimated_charge_time_minutes":{"type":"integer"},"estimated_cost":{"type":"number"},"route_labels":{"type":"array","items":{"type":"string"}},"highways":{"type":"array","items":{"type":"string"}},"region":{"type":"string"},"demand_rank":{"type":"integer"},"demand_tier":{"type":"string"},"route_source":{"type":"string"},"calculation_timestamp":{"type":"string","format":"date-time"},"stations":{"type":"array","items":{"$ref":"#/components/schemas/StationSummary"}},"meta_title":{"type":"string"},"meta_description":{"type":"string"},"geometry":{"$ref":"#/components/schemas/RouteGeometry"},"provenance":{"$ref":"#/components/schemas/RouteProvenance"},"baseline_plan":{"$ref":"#/components/schemas/RoutePlan"},"personalized_plan":{"$ref":"#/components/schemas/RoutePlan"},"is_range_safe":{"type":"boolean"},"warning":{"type":["string","null"]},"disclaimer":{"type":"string"}},"required":["id","slug","origin_city","origin_state","destination_city","destination_state","origin_lat","origin_lng","destination_lat","destination_lng","route_labels","highways","region","demand_rank","demand_tier","route_source","calculation_timestamp","geometry","provenance","baseline_plan","personalized_plan","is_range_safe","disclaimer"]},"RouteGeometry":{"type":"object","properties":{"encoded_polyline5":{"type":"string","description":"OSRM encoded polyline at precision 5"},"precision":{"type":"integer","enum":[5]},"provider":{"type":"string","enum":["osrm"]},"fetched_at":{"type":"string","format":"date-time"}},"required":["encoded_polyline5","precision","provider","fetched_at"]},"RouteProvenance":{"type":"object","properties":{"route_source":{"type":"string"},"source_url":{"type":"string","format":"uri"},"geometry_fetched_at":{"type":"string","format":"date-time"},"calculated_at":{"type":"string","format":"date-time"},"station_source":{"type":"string"},"station_snapshot_at":{"type":"string","format":"date-time"},"demand_methodology_version":{"type":"string"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["route_source","source_url","geometry_fetched_at","calculated_at","station_source","station_snapshot_at","demand_methodology_version"]},"RoutePlanLeg":{"type":"object","properties":{"from_progress":{"type":"number"},"to_progress":{"type":"number"},"miles":{"type":"number"},"limit_miles":{"type":"number"}},"required":["from_progress","to_progress","miles","limit_miles"]},"RoutePlanStop":{"type":"object","properties":{"stop_index":{"type":"integer"},"stop_count":{"type":"integer"},"station_id":{"type":"integer"},"distance_from_start_miles":{"type":"number"},"previous_leg_miles":{"type":"number"},"off_route_miles":{"type":"number"},"primary_station":{"oneOf":[{"$ref":"#/components/schemas/StationSummary"},{"type":"null"}]},"station":{"oneOf":[{"$ref":"#/components/schemas/StationSummary"},{"type":"null"}]},"alternatives":{"type":"array","maxItems":2,"items":{"$ref":"#/components/schemas/StationSummary"}}},"required":["stop_index","stop_count","station_id","distance_from_start_miles","previous_leg_miles","off_route_miles","primary_station","alternatives"]},"RoutePlan":{"type":"object","properties":{"assumptions":{"$ref":"#/components/schemas/RoutePlanAssumptions"},"is_range_safe":{"type":"boolean"},"warning":{"type":["string","null"]},"first_leg_limit_miles":{"type":"number"},"onward_leg_limit_miles":{"type":"number"},"max_observed_leg_miles":{"type":"number"},"legs":{"type":"array","items":{"$ref":"#/components/schemas/RoutePlanLeg"}},"stops":{"type":"array","items":{"$ref":"#/components/schemas/RoutePlanStop"}},"estimated_energy_kwh":{"type":"number"},"estimated_charging_cost":{"type":"number"},"estimated_charge_time_minutes":{"type":"number"},"connector":{"type":["string","null"]}},"required":["assumptions","is_range_safe","first_leg_limit_miles","onward_leg_limit_miles","max_observed_leg_miles","legs","stops","estimated_energy_kwh","estimated_charging_cost","estimated_charge_time_minutes"]},"RoutePlanAssumptions":{"type":"object","properties":{"version":{"type":"string"},"epa_range_miles":{"type":"number"},"departure_charge_pct":{"type":"number"},"enroute_charge_target_pct":{"type":"number"},"arrival_reserve_pct":{"type":"number"},"weather_elevation_margin_pct":{"type":"number"},"consumption_kwh_per_100_miles":{"type":"number"},"electricity_cost_per_kwh":{"type":"number"},"minutes_per_stop":{"type":"number"}},"required":["version","epa_range_miles","departure_charge_pct","enroute_charge_target_pct","arrival_reserve_pct","weather_elevation_margin_pct","consumption_kwh_per_100_miles","electricity_cost_per_kwh","minutes_per_stop"]},"RouteListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RouteSummary"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"},"total_pages":{"type":"integer"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","total","page","limit","total_pages","attribution"]},"RouteDetailResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RouteDetail"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","attribution"]},"CreateLeadBody":{"type":"object","properties":{"name":{"type":"string","minLength":1},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"sms_consent":{"type":"boolean","description":"The unchecked \"Text me\" box under the phone field was checked. Without it we never text the number."},"property_address":{"type":"string"},"property_type":{"type":"string","enum":["parking_lot","garage","commercial","residential","hotel","retail","other"]},"num_spaces":{"type":"integer","minimum":1},"message":{"type":"string"},"source_page":{"type":"string"},"parking_type":{"type":"string","enum":["lot","garage","curb","mixed"],"description":"Detail about how the parking is laid out"},"power_supply":{"type":"string","enum":["unknown","lt100A","100_200A","200_400A","gt400A"],"description":"Available service amperage on the property"},"install_timeline":{"type":"string","enum":["now","3_months","6_months","12_months","exploring"]},"decision_maker_role":{"type":"string","enum":["owner","manager","board","broker","tenant","other"]},"demand_score":{"type":"number","description":"0-100 demand index captured from the heatmap at submission time"},"roi_estimate_annual":{"type":"integer","description":"Annual revenue estimate (USD) captured from the calculator at submission time"},"metadata":{"type":"object","additionalProperties":true},"honeypot":{"type":"string","description":"Anti-spam field - must be empty"}},"required":["name","email"]},"CaseStudy":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":"string"},"content":{"type":"string"},"image_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"spaces":{"type":"integer","nullable":true},"monthly_revenue":{"type":"integer","nullable":true},"payback_months":{"type":"integer","nullable":true},"featured":{"type":"boolean"},"published":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","slug","title","summary","content","featured","published","created_at"]},"DemandSnapshot":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"},"demand":{"type":"object","nullable":true,"properties":{"state":{"type":"string"},"ev_registrations":{"type":"integer"},"ev_per_1k_residents":{"type":"number"},"national_avg_per_1k":{"type":"number"},"density_index":{"type":"integer","description":"State per-capita / national avg, ×100"},"projection_5yr_count":{"type":"integer"},"as_of":{"type":"string","description":"Quarter the registration figure was sourced from (e.g. \"2024-Q4\")"}}},"nearest_dcfc":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string","nullable":true},"station_name":{"type":"string"},"latitude":{"type":"string"},"longitude":{"type":"string"},"ev_dc_fast_num":{"type":"integer","nullable":true},"ev_network":{"type":"string","nullable":true},"distance_mi":{"type":"number"}}}},"total_nearby_dcfc_ports":{"type":"integer"},"gap_score":{"type":"string","enum":["low","medium","high"]},"gap_label":{"type":"string"},"as_of":{"type":"string","description":"Quarter the EV-registration table was last refreshed (e.g. \"2024-Q4\")"}},"required":["lat","lng","nearest_dcfc","gap_score","gap_label","as_of"]},"DemandCell":{"type":"object","description":"One geohash-bucketed demand cell. Counts only — never any identifying field.","properties":{"geohash":{"type":"string","description":"~5-character geohash (~4.9 km cell)."},"lat":{"type":"number","description":"Cell-center latitude."},"lng":{"type":"number","description":"Cell-center longitude."},"bounds":{"type":"object","description":"Cell bounding box, for drawing the cell as a rectangle.","properties":{"latMin":{"type":"number"},"latMax":{"type":"number"},"lngMin":{"type":"number"},"lngMax":{"type":"number"}},"required":["latMin","latMax","lngMin","lngMax"]},"searches":{"type":"integer","description":"Total charger searches observed in the cell over the window."},"unmet":{"type":"integer","description":"Searches that found no station nearby."},"unmet_pct":{"type":"number","description":"Unmet searches as a percentage of total (one decimal place)."},"sessions":{"type":"integer","description":"Distinct anonymized sessions — never the session ids themselves."},"demand_score":{"type":"integer","description":"0-100 opportunity score: log-scaled search volume plus unmet-demand ratio."}},"required":["geohash","lat","lng","bounds","searches","unmet","unmet_pct","sessions","demand_score"]},"DemandAggregateResponse":{"type":"object","properties":{"window_days":{"type":"integer","description":"Look-back window the response covers, in days."},"generated_at":{"type":"string","description":"ISO timestamp the aggregate was computed."},"min_count":{"type":"integer","description":"Effective per-cell minimum search count applied (always >= 3)."},"cell_count":{"type":"integer","description":"Number of cells returned."},"total_searches":{"type":"integer","description":"Sum of searches across the returned cells."},"total_unmet":{"type":"integer","description":"Sum of unmet searches across the returned cells."},"cells":{"type":"array","items":{"$ref":"#/components/schemas/DemandCell"}},"license":{"type":"string","description":"Commercial license note — this dataset is not redistributable under CC-BY-4.0."}},"required":["window_days","generated_at","min_count","cell_count","total_searches","total_unmet","cells"]},"LeadResponse":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]},"CreateReviewBody":{"type":"object","properties":{"rating":{"type":"integer","minimum":1,"maximum":5},"review_text":{"type":"string","minLength":10,"maxLength":2000},"author_name":{"type":"string","minLength":1,"maxLength":100},"tags":{"type":"array","items":{"type":"string"},"description":"Optional tags chosen by the reviewer (e.g. \"fast\", \"clean\")"},"charged_successfully":{"type":"boolean","nullable":true,"description":"Did the reviewer successfully charge their vehicle? Powers reliability score."},"connector_used":{"type":"string","enum":["NACS","CCS1","CCS2","CHAdeMO","J1772"],"nullable":true,"description":"Which connector type the reviewer plugged into."},"vehicle_label":{"type":"string","maxLength":80,"description":"Optional short label for the reviewer's vehicle (e.g. \"Model 3\"), shown next to the review."},"honeypot":{"type":"string","description":"Anti-spam field - must be empty"}},"required":["rating","author_name"]},"ReviewSummary":{"type":"object","properties":{"id":{"type":"integer"},"station_id":{"type":"integer"},"rating":{"type":"integer"},"review_text":{"type":"string"},"author_name":{"type":"string"},"created_at":{"type":"string"}},"required":["id","station_id","rating","author_name","created_at"]},"ReviewResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ReviewSummary"}},"required":["data"]},"ReviewListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReviewSummary"}},"average_rating":{"type":"number","nullable":true},"total":{"type":"integer"}},"required":["data","total"]},"NewsletterSubscribeBody":{"type":"object","properties":{"email":{"type":"string","format":"email"},"city":{"type":"string"},"state":{"type":"string"},"honeypot":{"type":"string"}},"required":["email"]},"StatsResponse":{"type":"object","properties":{"total_stations":{"type":"integer"},"total_dcfc":{"type":"integer"},"total_l2":{"type":"integer"},"states_covered":{"type":"integer"},"cities_with_charging":{"type":"integer"},"networks_count":{"type":"integer"},"recently_added_count":{"type":"integer"},"stations_added_today":{"type":"integer","description":"Stations added in the last 24h, summed from sync_log."},"stations_added_7d":{"type":"integer","description":"Stations added in the last 7 days, summed from sync_log."},"last_sync":{"type":"string","nullable":true},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["total_stations","total_dcfc","total_l2","states_covered","cities_with_charging"]},"StatsHistoryPoint":{"type":"object","properties":{"day":{"type":"string","description":"UTC date in YYYY-MM-DD format."},"added":{"type":"integer","description":"Net stations added on this day across all completed syncs."}},"required":["day","added"]},"StatsHistoryResponse":{"type":"object","properties":{"days":{"type":"integer","description":"Number of days in the series."},"data":{"type":"array","items":{"$ref":"#/components/schemas/StatsHistoryPoint"}},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["days","data"]},"NetworkStat":{"type":"object","properties":{"network":{"type":"string"},"station_count":{"type":"integer"},"percentage":{"type":"number"}},"required":["network","station_count","percentage"]},"NetworkStatsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NetworkStat"}},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["data","attribution"]},"ReliabilityRank":{"type":"object","description":"A single ranked entity (network, state, or city) in the reliability index.","properties":{"rank":{"type":"integer"},"network":{"type":"string","description":"Present on network rankings."},"state":{"type":"string","description":"State name; present on state rankings."},"abbreviation":{"type":"string","description":"Two-letter state code; present on state rankings."},"city":{"type":"string","description":"Present on city rankings (when ?state= is supplied)."},"slug":{"type":"string","description":"URL slug for the state or city, where applicable."},"reliability_pct":{"type":"integer","description":"Average 30-day reliability percentage (0-100)."},"rating":{"type":"string","enum":["excellent","good","needs_improvement"]},"scored_stations":{"type":"integer","description":"Number of stations with a reliability score behind this ranking."}},"required":["rank","reliability_pct","rating","scored_stations"]},"ReliabilityIndexResponse":{"type":"object","properties":{"scope":{"type":"string","enum":["national","state"]},"state":{"type":"string","description":"Two-letter state code; present when scope is state."},"window_days":{"type":"integer","description":"Length of the reliability window in days (30)."},"last_updated":{"type":"string","format":"date-time","nullable":true,"description":"Most recent reliability update timestamp across the ranked entities."},"networks":{"type":"array","description":"Networks ranked by reliability; present when scope is national.","items":{"$ref":"#/components/schemas/ReliabilityRank"}},"states":{"type":"array","description":"States ranked by reliability; present when scope is national.","items":{"$ref":"#/components/schemas/ReliabilityRank"}},"cities":{"type":"array","description":"Cities ranked by reliability; present when scope is state.","items":{"$ref":"#/components/schemas/ReliabilityRank"}},"methodology":{"type":"string"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["scope","window_days","methodology","attribution"]},"SearchResponse":{"type":"object","properties":{"stations":{"type":"array","items":{"$ref":"#/components/schemas/SearchStation"}},"cities":{"type":"array","items":{"$ref":"#/components/schemas/SearchCity"}},"routes":{"type":"array","items":{"$ref":"#/components/schemas/SearchRoute"}},"total":{"type":"integer"}},"required":["stations","cities","routes","total"]},"SearchStation":{"type":"object","description":"Minimal station projection returned by search.","properties":{"id":{"type":"integer"},"station_name":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"ev_network":{"type":["string","null"]},"latitude":{"type":"number"},"longitude":{"type":"number"},"slug":{"type":["string","null"]}},"required":["id","station_name","city","state","ev_network","latitude","longitude","slug"]},"SearchCity":{"type":"object","description":"Minimal city projection returned by search.","properties":{"id":{"type":"integer"},"city":{"type":"string"},"state":{"type":"string"},"slug":{"type":"string"},"station_count":{"type":["integer","null"]}},"required":["id","city","state","slug","station_count"]},"SearchRoute":{"type":"object","description":"Minimal route projection returned by search.","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"origin_city":{"type":"string"},"origin_state":{"type":"string"},"destination_city":{"type":"string"},"destination_state":{"type":"string"},"distance_miles":{"type":["number","null"]},"recommended_stops":{"type":["integer","null"]}},"required":["id","slug","origin_city","origin_state","destination_city","destination_state","distance_miles","recommended_stops"]},"RegisterApiKeyBody":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string"},"use_case":{"type":"string"}},"required":["email","name"]},"ApiKeyResponse":{"type":"object","properties":{"api_key":{"type":"string","description":"The API key - shown only once at creation time"},"key_prefix":{"type":"string"},"daily_limit":{"type":"integer"},"message":{"type":"string"}},"required":["api_key","key_prefix","daily_limit"]},"ApiUsageResponse":{"type":"object","description":"Snapshot of the calling API key's current daily-quota bucket.\nThe same values are also exposed via the `X-RateLimit-Limit`,\n`X-RateLimit-Remaining`, and `X-RateLimit-Reset` response\nheaders on every metered `/v1/*` call (read from the same shared\ncounter). Unmetered responses carry `X-RateLimit-Scope: unmetered`\nand no quota numbers.\n","properties":{"tier":{"type":"string","enum":["free","starter","pro","scale","enterprise"]},"tier_label":{"type":"string"},"key_prefix":{"type":"string","description":"Non-secret first 12 chars of the API key, for display."},"daily_limit":{"type":"integer"},"used":{"type":"integer","description":"Requests counted against today's bucket, including this one."},"remaining":{"type":"integer","description":"daily_limit - used, clamped at 0."},"resets_at":{"type":"string","format":"date-time","description":"UTC timestamp when the daily bucket rolls over."},"resets_in_seconds":{"type":"integer","description":"Seconds until reset. Matches `retry_after_seconds` on a 429."},"is_active":{"type":"boolean"}},"required":["tier","tier_label","daily_limit","used","remaining","resets_at","resets_in_seconds","is_active"]},"SyncResponse":{"type":"object","properties":{"status":{"type":"string"},"message":{"type":"string"},"sync_id":{"type":"integer"}},"required":["status","message"]},"RegridBudgetSnapshot":{"type":"object","description":"Per-day Regrid API quota tracker (resets at UTC midnight).","properties":{"day":{"type":"string","description":"UTC date the counters apply to (YYYY-MM-DD)."},"used":{"type":"integer"},"budget":{"type":"integer"},"remaining":{"type":"integer"},"sweep_reserve":{"type":"integer","description":"Calls held in reserve for the weekly sweep. Lead-form and\noperator lookups stop once `remaining <= sweep_reserve` so\nB2B traffic can't starve the cron. The count is shared by\nevery server instance. Default 200; configurable via REGRID_SWEEP_RESERVE.\n"}},"required":["day","used","budget","remaining","sweep_reserve"]},"RegridLookupResponse":{"type":"object","description":"Operator point-lookup response. Geometry is intentionally omitted —\nonly owner/zoning fields are surfaced.\n","properties":{"status":{"type":"string","enum":["matched","no_match","multiple_match","error"]},"parcel_id":{"type":"string","nullable":true},"owner_name":{"type":"string","nullable":true},"owner_type":{"type":"string","nullable":true},"lot_size_acres":{"type":"number","nullable":true},"zoning":{"type":"string","nullable":true},"total_matches":{"type":"integer","description":"Only present when status=multiple_match — number of parcels that overlap the lat/lng."}},"required":["status"]},"RegridSweepStartResponse":{"type":"object","description":"Returned with HTTP 200 when a new background sweep is kicked off.\nWhen a sweep is already running, the endpoint returns HTTP 409 with\nthe standard ErrorResponse shape (`error: \"already_running\"`),\nnot this schema.\n","properties":{"status":{"type":"string","enum":["started"]},"job_id":{"type":"integer"},"limit":{"type":"integer"},"budget":{"$ref":"#/components/schemas/RegridBudgetSnapshot"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["status","job_id","limit","budget","attribution"]},"RegridSweepStatusResponse":{"type":"object","properties":{"job":{"type":"object","nullable":true,"description":"Latest enrichment_jobs row for job_type='regrid_enrich'.","properties":{"id":{"type":"integer"},"job_type":{"type":"string"},"status":{"type":"string"},"last_station_id":{"type":"integer","nullable":true},"enriched":{"type":"integer"},"failed":{"type":"integer"},"no_match":{"type":"integer"},"total":{"type":"integer"},"config":{"type":"object","nullable":true},"started_at":{"type":"string","format":"date-time","nullable":true},"updated_at":{"type":"string","format":"date-time","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true},"alert_sent_at":{"type":"string","format":"date-time","nullable":true},"alerted_state":{"type":"string","nullable":true}}},"running":{"type":"boolean","description":"True iff a sweep is currently in flight in this api-server process."},"buckets":{"type":"object","description":"Histogram of stations by regrid_match_status (matched / no_match / multiple_match / error). NULL-status rows are excluded — they're the still-to-process queue. `error` rows also get retried on the next sweep.","additionalProperties":{"type":"integer"}},"coverage":{"type":"object","properties":{"active_total":{"type":"integer"},"enriched_total":{"type":"integer","description":"Stations with regrid_last_enriched_at IS NOT NULL."}}},"budget":{"$ref":"#/components/schemas/RegridBudgetSnapshot"},"attribution":{"$ref":"#/components/schemas/Attribution"}},"required":["running","buckets","coverage","budget","attribution"]},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"param":{"type":"string","description":"The query parameter that was rejected (400 responses)"}},"required":["error","message"]}}}}