Copy link to headingHow to build a weather API on Vercel with FastAPI, Express, or Nitro
A weather API takes a place name, turns it into coordinates, and returns current conditions for that location. You can build one on Vercel with FastAPI, Express, or Nitro. Each one deploys as a Vercel Function that scales with traffic.
The route logic is the same in all three. What changes is the language you write it in and how much caching and monitoring you get without extra configuration.
Copy link to headingWhich runtime should you build your weather API on?
Pick the runtime your project already uses. The route is short enough that the surrounding stack matters more than the framework.
Each one suits a different starting point:
- FastAPI: Python, with async support and a generated interactive documentation page at
/docs. Choose it if your stack is already Python, or if you want that documentation page without writing it. - Express: Node.js and TypeScript, with the least setup of the three. Choose it if you want one route file and nothing else.
- Nitro: TypeScript, with filesystem routing and a caching layer you configure rather than write. Choose it if you expect enough traffic that caching matters on day one.
All three run on Fluid compute by default, so the runtime you pick doesn't change how the deployment scales.
Copy link to headingWhat you need to build a weather API
All three runtimes need the same two things:
- A Vercel account
- The Vercel CLI installed locally
The rest depends on the runtime you chose:
- FastAPI: Python installed locally, plus familiarity with
asyncandawait. Vercel's Python runtime supports 3.12, 3.13, and 3.14, with 3.12 as the default. Match your local version to one of those. - Express: Node.js and pnpm installed locally, plus familiarity with Express routing and async TypeScript.
- Nitro: Node.js and pnpm installed locally, plus familiarity with Nitro's filesystem routing.
You don't need a weather API key. Open-Meteo serves its free tier without registration.
Copy link to headingHow the weather API route works
Open-Meteo's forecast endpoint takes coordinates, not place names. So the route makes two upstream calls before it returns anything.
The sequence is identical across the three runtimes:
- Read the city name from the URL path and the optional
unitsquery parameter. - Call the Open-Meteo geocoding API to resolve that name to a latitude and longitude, returning a 404 when nothing matches.
- Call the forecast endpoint with those coordinates, requesting temperature, humidity, apparent temperature, and wind speed.
- Return one object combining the resolved location with the current conditions.
Open-Meteo returns Celsius and km/h by default, so imperial output means adding temperature_unit=fahrenheit and wind_speed_unit=mph to the forecast request. The route checks the units query parameter for the exact value imperial. Anything else falls back to metric, so a typo returns Celsius rather than an error.
Setting timezone=auto returns timestamps in the location's own local time, so the client doesn't have to convert from UTC.
Copy link to headingHow to build a weather API with FastAPI
FastAPI needs one extra dependency, httpx, to make the two upstream calls with async and await.
Copy link to heading1. Create the project
Start from the FastAPI boilerplate template, then clone the repository once Vercel has deployed it.
To scaffold locally instead, use the CLI to instantiate the project and install the dependencies:
Running uv add httpx records httpx in pyproject.toml, so Vercel installs it during deployment.
uvicorn serves the app locally. Vercel's Python runtime loads the FastAPI application directly, so it isn't needed in production.
Copy link to heading2. Add the weather route
In app/main.py and add the imports at the top of the file:
Then add the route after your existing ones:
Both calls share a single httpx.AsyncClient, which reuses the connection between them. Each call has its own error handler, so a geocoding failure reports differently from a forecast failure.
Copy link to heading3. Run FastAPI locally
Open http://localhost:3000/docs to call the route through FastAPI's generated
documentation page.
Copy link to headingHow to build a weather API with Express
Express needs no extra dependencies. The runtime's built-in fetch covers both upstream calls, so the route fits in one file.
Copy link to heading1. Create the project
Start from the Express starter, then clone the repository once Vercel has deployed it.
To scaffold locally instead, use the CLI:
Copy link to heading2. Add the weather route
Express uses the fetch, URLSearchParams, and AbortSignal APIs included in Node.js, so the base route requires no additional dependency.
Express 5 forwards rejected async route handlers to its error-handling layer. This route catches errors locally so it can return a consistent JSON response instead of Express’s default error page.
Copy link to heading3. Run Express locally
Use the vercel CLI to run the project locally:
Copy link to headingHow to build a weather API with Nitro
Nitro needs no extra dependencies either, and the route path comes from the filename rather than a registration call.
Copy link to heading1. Create the project
Start from the Nitro starter template, then clone the repository once Vercel has deployed it.
To scaffold locally instead, use the CLI:
Copy link to heading2. Add the weather route
Create server/routes/api/weather/[city].ts and add the route:
Nitro's filesystem routing maps the [city] filename to the path parameter, so there's no route registration to write separately. The Nitro starter supplies the handler, router, error, query, and $fetch helpers through its generated auto-imports. Do not call decodeURIComponent on the router parameter: Nitro has already decoded it.
Copy link to heading3. Run Nitro locally
Start the Nitro app by running:
Copy link to headingHow to test the weather API route locally
The command that starts the dev server depends on your runtime. The requests you make against it don't.
Start the dev server:
- FastAPI: Run
vercel dev. - Express: Run
vercel dev. - Nitro: Run
pnpm install, thenpnpm dev.
All three serve on port 3000, so the same two requests exercise any of them:
A successful response returns the resolved location alongside the current conditions. On FastAPI, you can also call the route from the generated documentation page at http://localhost:3000/docs.
Copy link to headingHow to deploy your weather API to Vercel
The deploy sequence is the same, whichever runtime you built on.
Ship it from Git or from the CLI:
- Push your changes to your remote repository, or run
vercelfrom the project directory. - Open the preview deployment Vercel creates and test the route against it.
- Merge to your main branch to promote the build to production.
What Vercel builds differs slightly by runtime. A FastAPI or Express application becomes a single Vercel Function, resolved from its entrypoint file. Nitro bundles its server application into a Vercel Function. It uses Fluid compute by default, so compute scale with traffic without further configuration.
Copy link to headingHow to cache and monitor a weather API route
Open-Meteo's free tier allows 600 calls per minute, 5,000 per hour, and 10,000 per day. This route calls it twice per request, so it consumes that budget at double the rate the request count suggests. Caching is what keeps you under it.
Runtime cache stores data in the region where your function runs and works with every runtime. Items are capped at 2 MB, which a weather response stays well under.
Copy link to headingCache the response in Nitro
Nitro reads cached values from a mount point, so configure the driver first and then wrap the handler.
Mount the Vercel driver in nitro.config.ts:
In server/routes/api/weather/[city].ts, swap defineEventHandler for defineCachedEventHandler and pass a duration:
Nitro serves a stale value while it refreshes in the background, so a cached route stays fast through the refresh.
Copy link to headingCache the response in FastAPI or Express
Neither framework has a declarative cache layer, so call the runtime cache directly.
For FastAPI, install the vercel package:
Then read from the cache before making the upstream calls:
For Express, the equivalent uses getCache from @vercel/functions:
Then update src/index.ts:
The units belong in the cache key. Leave them out and the first caller sets the units for everyone who asks about that city afterward.
Copy link to headingMonitor the weather API in Observability
Once the route is live, the Observability tab reports invocations, active CPU time, and error rate. Nitro applications need a compatibility date of 2025-07-15 or later, on Nitro 2.12 or newer. With that set, /api/weather/[city] gets its own row instead of folding into an application-wide total.
Runtime cache has its own panel showing cache reads and writes, hit rate, and on-demand revalidations. Call your production route several times in a row and confirm the hit rate climbs after the first request.
Runtime cache usage is charged, and its scope depends on your plan. On Pro and Enterprise each project gets its own cache. On Hobby, every project on your team shares one, so a busy project can evict another project's entries.
Copy link to headingHow to troubleshoot common weather API errors
The geocoding step and Open-Meteo's rate limits account for most failures in this route.
Match the behavior you're seeing to one of these:
- A city that exists returns 404: The geocoding endpoint matches on the name string alone, so misspellings and less common transliterations find nothing. Try the local spelling before assuming the city is missing from the data.
- The wrong city comes back: Place names aren't unique, and
count: 1returns whichever match ranks highest. Raisecount, then filter the results on thecountryfield before picking one. - Requests start failing with 429: You've passed one of Open-Meteo's free-tier limits. Cache the response to cut the call count. The free tier is also non-commercial only, so a revenue-generating service needs a paid plan regardless of volume.
- Imperial units return Celsius: The comparison is exact and case-sensitive, so
ImperialandIMPERIALboth fall back to metric. Lowercase the parameter before comparing it, or log the received value to confirm what arrived.
If the route works locally but fails in production, check the function's runtime logs for the upstream response. The geocoding and forecast calls return different error messages, which narrows the failure to one of the two.
Copy link to headingNext steps
The same pattern works for any external API that needs a lookup before the fetch.
Start a new Vercel project to deploy your own version, or browse the backend templates for a starting point in another runtime.
Copy link to headingRelated resources
- Vercel Functions
- Fluid compute
- Runtime cache
- Observability
- Deploy a FastAPI app on Vercel
- Build an MCP server with weather tools using Express
Copy link to headingFAQ
Do you need an API key to build a weather API?
Not with Open-Meteo, which serves its free tier without registration. Most other providers issue a key. Store that key as an environment variable in your Vercel project settings and read it from the environment at request time. A key committed to a repository is exposed to everyone with read access.
What are the Open-Meteo API rate limits?
The free tier allows 600 calls per minute, 5,000 per hour, 10,000 per day, and 300,000 per month. A weather route that geocodes a place name before fetching its forecast spends two calls per request, so plan against half those figures. Caching the response reduces the count. The free tier is non-commercial only.
Can you use a different weather API instead of Open-Meteo?
Yes. Most weather APIs follow the same two-step shape, resolving a place to coordinates and then fetching conditions for those coordinates. Swap the endpoint URLs and adjust how you map the response fields. Providers that accept place names directly remove the geocoding call. Most of them require an API key.
Does a weather API deployed on Vercel scale automatically?
Yes. FastAPI and Express applications deploy as a single Vercel Function, and Nitro compiles each server route into its own. All of them run on Fluid compute by default. That model handles concurrent requests inside one instance instead of starting a new instance per request.