---
title: How to Build a Weather API with Express and Vercel
description: Provide real-time weather data to apps and websites with a single Express route.
url: /kb/guide/weather-api-with-express
canonical_url: "https://vercel.com/kb/guide/weather-api-with-express"
published: 2025-11-03
last_updated: 2026-06-16
authors: Jeff See, Ismael Rumzan
related:
  - /docs/functions
  - /docs/fluid-compute
  - /docs/frameworks/backend/express
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

To provide real-time weather data, you need to handle API integration, error handling and automatic scaling. With Vercel functions and Express, 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:

1. Accepts a city name and geocodes it to coordinates
   
2. Fetches current weather data from the Open-Meteo API
   
3. Returns temperature, humidity, wind speed, and other weather metrics with optional metric/imperial unit conversion
   

## Prerequisites

- Node.js and pnpm installed locally
  
- A Vercel account
  
- Basic understanding of Express and async/await in TypeScript
  

## Build the Weather API

### 1\. Create your Express project

Start with the [Express starter](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fvercel%2Ftree%2Fmain%2Fexamples%2Fexpress&template=express) template. 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

```bash
vc init express
```

### 2\. Add the Weather endpoint

Add the weather endpoint after your existing routes:

```typescript
app.get('/api/weather/:city', async (req, res) => {
  try {
    const city = req.params.city
    const units = req.query.units as string | undefined
    
    // Normalize units parameter
    const normalizedUnits = units === 'imperial' ? 'imperial' : 'metric'
    
    // Step 1: Geocode city to get coordinates
    const geoParams = new URLSearchParams({
      name: city,
      count: '1',
      language: 'en',
      format: 'json'
    })
    
    const geoResponse = await fetch(`https://geocoding-api.open-meteo.com/v1/search?${geoParams}`)
    
    if (!geoResponse.ok) {
      return res.status(geoResponse.status).json({ 
        error: 'Failed to fetch geocoding data' 
      })
    }
    
    const geoData = await geoResponse.json()
    
    if (!geoData.results || geoData.results.length === 0) {
      return res.status(404).json({ error: `City '${city}' not found` })
    }
    
    const location = geoData.results[0]
    const { name, country, latitude, longitude } = location
    
    // Step 2: Fetch current weather data
    const weatherParams: Record<string, string> = {
      latitude: latitude.toString(),
      longitude: longitude.toString(),
      current: 'temperature_2m,relative_humidity_2m,apparent_temperature,wind_speed_10m',
      timezone: 'auto'
    }
    
    // Add unit parameters for imperial if needed
    if (units === 'imperial') {
      weatherParams.temperature_unit = 'fahrenheit'
      weatherParams.wind_speed_unit = 'mph'
    }
    
    const weatherUrlParams = new URLSearchParams(weatherParams)
    const weatherResponse = await fetch(`https://api.open-meteo.com/v1/forecast?${weatherUrlParams}`)
    
    if (!weatherResponse.ok) {
      return res.status(weatherResponse.status).json({ 
        error: 'Failed to fetch weather data' 
      })
    }
    
    const weatherData = await weatherResponse.json()
    
    // Return structured weather data
    res.json({
      city: name,
      country,
      latitude,
      longitude,
      units: normalizedUnits,
      current: weatherData.current
    })
    
  } catch (error) {
    console.error('Weather API error:', error)
    res.status(500).json({ 
      error: 'Failed to fetch weather data',
      message: error instanceof Error ? error.message : 'Unknown error'
    })
  }
})
```

### 3\. Test the route locally

Install the dependencies and launch the application in dev mode:

```bash
vercel dev
```

Use `curl` to test the API route:

```bash
# Test with default metric units
curl http://localhost:3000/api/weather/london


<!-- docsgraph:related -->
## Related pages

> **For AI agents:** Follow these links to understand how this page connects to the rest of the Vercel ecosystem. For the full cross-link map (inbound, outbound, prerequisites, and semantic neighbors), see the .graph.md link below.

- [How to Build a Weather API with FastAPI and Vercel](https://vercel.com/kb/guide/weather-api-with-fastapi?from=related) — Provide real-time weather data to apps and websites with a single FastAPI route.
- [How to Build a Weather API with Nitro and Vercel](https://vercel.com/kb/guide/weather-api-with-nitro?from=related) — Provide real-time weather data to apps and websites with a single Nitro route, Vercel cache storage, and Observability.
- [Getting Started](https://vercel.com/docs/functions/quickstart?from=related) — Build your first Vercel Function in a few steps.
- [How to ship an Express app on Vercel](https://vercel.com/kb/guide/ship-a-express-app-on-vercel?from=related) — Deploy an Express app to Vercel with zero configuration. Configure response streaming, middleware, cron jobs, the Bun ru
- [Build an MCP Server with Weather tools using Express and Vercel](https://vercel.com/kb/guide/mcp-server-with-weather-tool-express?from=related) — Make your Express weather API accessible to AI assistants through the Model Context Protocol.
- [Using Express.js with Vercel](https://vercel.com/kb/guide/using-express-with-vercel?from=related) — Learn how to use Express.js in a Serverless environment.

Full cross-link map for this page: [/kb/guide/weather-api-with-express.graph.md](/kb/guide/weather-api-with-express.graph.md)
<!-- /docsgraph:related -->

# Test with imperial units
curl "http://localhost:3000/api/weather/san%20francisco?units=imperial"
```

### 4\. Deploy to Vercel

- 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
  

> When you deploy an Express app to Vercel, the application becomes a single [Vercel Function](https://vercel.com/docs/functions) and uses [Fluid compute](https://vercel.com/docs/fluid-compute) by default.

## Summary

In this tutorial, you’ve built a real-time weather API using Express 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.

## Next steps

### Explore references

- [Explore the Express on Vercel docs](https://vercel.com/docs/frameworks/backend/express)
  
- [Express documentation](https://expressjs.com/)
  
- [Vercel Functions](https://vercel.com/docs/functions)