To provide real-time weather data, you need to handle API integration, error handling and automatic scaling. With Vercel functions and FastAPI, you can build a production-ready weather API in minutes.
In this tutorial, you will build and deploy a weather API route using the Open-Meteo API that:
- Accepts a city name and geocodes it to coordinates
- Fetches current weather data from the Open-Meteo API
- Returns temperature, humidity, wind speed, and other weather metrics with optional metric/imperial unit conversion
- Python 3.10+ installed locally
- A Vercel account
- Basic understanding of FastAPI and async/await concepts
Start with the template Deploy FastAPI Boilerplate. Once your the project is deployed on Vercel, clone the repository locally.
You can also create the project locally using the Vercel CLI with the following command
vc init fastapi
Update your requirements.txt
to include the httpx
library for making async HTTP requests:
fastapiuvicornhttpx
Open main.py
and add the necessary imports at the top of the file:
from fastapi import FastAPI, HTTPException, Queryfrom fastapi.responses import HTMLResponseimport httpx
Then add the weather endpoint after your existing routes:
@app.get("/api/weather/{city}")async def get_weather(city: str, units: str | None = Query(default="metric")): """ Get current weather for a city. Args: city: City name (e.g., "London", "New York") units: Temperature units - "metric" (Celsius) or "imperial" (Fahrenheit) Returns: Weather data including temperature, humidity, wind speed, and location info """ # Normalize units parameter normalized_units = "imperial" if units == "imperial" else "metric" async with httpx.AsyncClient() as client: # Step 1: Geocode city to get coordinates try: geo_response = await client.get( "https://geocoding-api.open-meteo.com/v1/search", params={ "name": city, "count": 1, "language": "en", "format": "json" } ) geo_response.raise_for_status() geo_data = geo_response.json() if not geo_data.get("results"): raise HTTPException(status_code=404, detail=f"City '{city}' not found") location = geo_data["results"][0] name = location["name"] country = location["country"] latitude = location["latitude"] longitude = location["longitude"] except httpx.HTTPError as e: raise HTTPException(status_code=500, detail=f"Geocoding API error: {str(e)}") # Step 2: Fetch current weather data try: weather_params = { "latitude": latitude, "longitude": longitude, "current": "temperature_2m,relative_humidity_2m,apparent_temperature,wind_speed_10m", "timezone": "auto" } # Add unit parameters for imperial if needed if units == "imperial": weather_params["temperature_unit"] = "fahrenheit" weather_params["wind_speed_unit"] = "mph" weather_response = await client.get( "https://api.open-meteo.com/v1/forecast", params=weather_params ) weather_response.raise_for_status() weather_data = weather_response.json() # Return structured weather data return { "city": name, "country": country, "latitude": latitude, "longitude": longitude, "units": normalized_units, "current": weather_data["current"] } except httpx.HTTPError as e: raise HTTPException(status_code=500, detail=f"Weather API error: {str(e)}")
Install the dependencies and launch the application in dev mode:
pip install -r requirements.txtvercel dev
Use curl
to test the API route:
# Test with default metric unitscurl http://localhost:3000/api/weather/london
# Test with imperial unitscurl "http://localhost:3000/api/weather/san%20francisco?units=imperial"
You can also test the endpoint in your browser using the interactive API documentation at http://localhost:3000/docs
.
- Push the changes to your remote repository or run the
vercel
cli command - Vercel will create a new preview deployment for your to test
- Merge to main branch to deploy to Production
In this tutorial, you’ve built a real-time weather API using FastAPI on Vercel.
You learned to:
- Structure a dynamic API route and integrate external APIs
- Deploy the app to Vercel as a function for automatic scaling
For a production application, make sure that you use a weather API that will not be rate limited based on the amount of traffic that you are expecting.