Web Scraping Weather Data: Sources, APIs, and Code
A weather forecast is one of those blocks that quietly raises the value of a site: users linger on the page, come back more often, and the content feels "alive." That is why weather widgets show up so often on city and travel portals, booking services, and overseas real-estate sites. In this guide we will cover where to get weather data, which APIs are popular in 2026, how web scraping weather data differs from working through an API, and show ready-to-run implementations in Python, JavaScript, PHP, and Go.
What "scraping weather data" means and which approaches exist
"Scraping weather data" usually refers to two fundamentally different tasks:
- Working through an official API. The service returns structured data (most often JSON); you make an HTTP request and get back ready-made fields: temperature, humidity, wind speed, a weather-condition code. This is the reliable, legal, and predictable path.
- Scraping HTML pages (web scraping proper). When a source has no API — or its API is paid — developers pull data straight out of the site's markup. It works, but it is brittle: any change to the HTML breaks the parser, and the approach often violates the source's terms of service.
The practical takeaway is simple: whenever there is an API, use the API. Save scraping for the case where no other source genuinely exists, and always check robots.txt and the terms of use first. If you do need to fall back to markup, our guide to parsing HTML text and links covers the mechanics.
Popular data sources and APIs {#api}
Open-Meteo — free and no key
Open-Meteo is one of the most convenient options to start with. The API requires no key for non-commercial use, returns JSON, and aggregates forecasts from national weather services at a resolution of 1 to 11 km. For commercial projects there are separate paid tiers with monthly request limits. It is ideal for prototypes, widgets, and open-source projects — and the go-to free weather API for most people getting started.
OpenWeatherMap — a classic with a large free tier
OpenWeatherMap remains the most tutorial-featured service around. In 2026 it still has a working free path, but you need to understand which product you are calling. The classic endpoints (current weather, 3-hour forecast, air pollution, weather maps, geocoding) are free with a limit of 60 requests per minute and up to 1,000,000 requests per month. The One Call family (3.0 / 4.0) is a separate pay-per-call subscription where the first 1,000 requests per day are free. Before going to production, set a spending cap so you do not accidentally blow past the free threshold.
WeatherAPI.com — a generous free tier
WeatherAPI.com returns current weather, hourly and daily forecasts, historical data, astronomy, and geolocation in JSON and XML. A convenient choice when you want one "everything" provider with a clear free limit.
Visual Crossing — strong historical data
Visual Crossing is often called one of the best for price-to-capability: a single API for current weather, forecasts, alerts, and — especially — long historical weather data series. There is a free key and a Query Builder for assembling requests with export to JSON or CSV. A good fit for analytics and dashboards.
Business platforms: Tomorrow.io, Weatherbit, Meteomatics, meteoblue
If you need highly specialized parameters (around 500 with Tomorrow.io, more than 1,800 with Meteomatics, historical series going back to 1940), look toward enterprise platforms. They offer high accuracy and a huge set of parameters, but almost always require a paid subscription for commercial use.
Regional and national services
Beyond the global providers, most countries run an authoritative national service — usually free and reliable, but limited to their own territory:
- United States — NWS (
weather.gov). The National Weather Service API is free and needs no key — an excellent choice for civic and public-interest projects within the US. - United Kingdom — Met Office. The Met Office Weather DataHub offers site-specific forecasts via a modern REST API (free tier available for developers).
- Australia — BOM. The Bureau of Meteorology publishes observations and forecast data (check current access terms, as its public feeds have changed over time).
- Canada — Environment and Climate Change Canada (MSC). Open GeoMet services and Datamart provide free, open weather data.
- AccuWeather and The Weather Company (weather.com / IBM). Widely used commercial providers with global coverage and rich datasets, aimed at business use.
National sources are free and authoritative, but usually confined to a specific territory.
Comparison across key criteria
| Source | Key | Free tier | Coverage | Good for |
|---|---|---|---|---|
| Open-Meteo | Not needed (non-comm.) | Yes, generous | Global | Prototypes, widgets, open-source |
| OpenWeatherMap | Needed | Yes (classic endpoints) | Global | General-purpose tasks |
| WeatherAPI.com | Needed | Yes | Global | "All in one" |
| Visual Crossing | Needed | Yes | Global | History, analytics |
| Tomorrow.io / Meteomatics | Needed | Trial/limited | Global | Business, niche parameters |
| AccuWeather | Needed | Limited trial | Global | Commercial apps, B2B |
| NWS (weather.gov) | Not needed | Yes | US only | Civic projects in the US |
Implementations in different languages
All examples below use Open-Meteo, because it needs no key — you can run the code immediately. The coordinates in the examples are New York City (40.71, -74.01). To switch to a different provider, you usually just change the URL, add a key, and adjust the JSON parsing.
Python (requests)
The canonical way to do scraping weather data in Python is a plain HTTP client like requests:
import requests
def get_weather(lat: float, lon: float) -> dict:
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code",
"timezone": "auto",
}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
return response.json()
data = get_weather(40.71, -74.01)
current = data["current"]
print(f"Temperature: {current['temperature_2m']}°C")
print(f"Humidity: {current['relative_humidity_2m']}%")
print(f"Wind: {current['wind_speed_10m']} km/h")
A version for OpenWeatherMap with a key and imperial units:
import requests
API_KEY = "YOUR_KEY"
def get_weather_owm(city: str) -> dict:
url = "https://api.openweathermap.org/data/2.5/weather"
params = {"q": city, "units": "imperial", "lang": "en", "appid": API_KEY}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
d = response.json()
return {
"city": d["name"],
"temperature": d["main"]["temp"],
"feels_like": d["main"]["feels_like"],
"description": d["weather"][0]["description"],
}
print(get_weather_owm("New York"))
JavaScript / Node.js (native fetch)
In Node.js 18+, fetch is available out of the box — no external dependencies needed.
async function getWeather(lat, lon) {
const url = new URL("https://api.open-meteo.com/v1/forecast");
url.searchParams.set("latitude", lat);
url.searchParams.set("longitude", lon);
url.searchParams.set("current", "temperature_2m,wind_speed_10m,weather_code");
url.searchParams.set("timezone", "auto");
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
getWeather(40.71, -74.01)
.then((data) => {
const c = data.current;
console.log(`Temperature: ${c.temperature_2m}°C, wind: ${c.wind_speed_10m} km/h`);
})
.catch((err) => console.error("Failed to fetch weather:", err.message));
PHP (cURL)
<?php
function getWeather(float $lat, float $lon): ?array
{
$query = http_build_query([
'latitude' => $lat,
'longitude' => $lon,
'current' => 'temperature_2m,wind_speed_10m,weather_code',
'timezone' => 'auto',
]);
$url = "https://api.open-meteo.com/v1/forecast?{$query}";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_USERAGENT => 'WeatherWidget/1.0',
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($body === false || $code !== 200) {
return null;
}
return json_decode($body, true);
}
$data = getWeather(40.71, -74.01);
if ($data !== null) {
echo "Temperature: " . $data['current']['temperature_2m'] . "°C\n";
}
Go
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type WeatherResponse struct {
Current struct {
Temperature float64 `json:"temperature_2m"`
WindSpeed float64 `json:"wind_speed_10m"`
WeatherCode int `json:"weather_code"`
} `json:"current"`
}
func main() {
url := "https://api.open-meteo.com/v1/forecast" +
"?latitude=40.71&longitude=-74.01" +
"¤t=temperature_2m,wind_speed_10m,weather_code&timezone=auto"
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var data WeatherResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
panic(err)
}
fmt.Printf("Temperature: %.1f°C, wind: %.1f km/h\n",
data.Current.Temperature, data.Current.WindSpeed)
}
Calling a keyed provider (OpenWeatherMap, quick curl test)
Most commercial providers authenticate with an API key in the query string or a header. A quick smoke test against OpenWeatherMap:
curl "https://api.openweathermap.org/data/2.5/weather?q=London&units=metric&appid=YOUR_KEY"
Scraping HTML as a fallback (Python + BeautifulSoup)
When no API is available, you can pull data out of the markup. The approach works but is brittle: when the HTML changes, your selectors stop working, and you should only use it if it does not conflict with the source's terms.
import requests
from bs4 import BeautifulSoup
def scrape_weather(url: str) -> str:
headers = {"User-Agent": "Mozilla/5.0"}
html = requests.get(url, headers=headers, timeout=10).text
soup = BeautifulSoup(html, "html.parser")
# The selector is tailored to the specific source page
temp = soup.select_one(".temperature")
return temp.get_text(strip=True) if temp else "not found"
Open-Meteo's
weather_codefollows the WMO standard: for example, 0 = clear sky, 2 = partly cloudy, 61 = slight rain, 71 = slight snow. Keep the full code table as a mapping dictionary so you can show users a human-readable description.
Caching, limits, and legal nuances
A few rules that save money and nerves in production:
- Cache responses. Weather almost never needs to refresh on every page view. Cache by coordinates, city, or ZIP/postcode, and update current conditions every 5–15 minutes. For mobile apps, route requests through your own backend rather than directly from every client.
- Keep keys on the server. An API key must not be baked into client-side code — it is trivial to steal. Requests should go through your backend proxy.
- Watch your limits. Handle 401 (bad key) and 429 (rate limit exceeded), and set up alerts on sudden spikes in call volume.
- Check the license. Most free tiers allow non-commercial use only. Some services (Visual Crossing, OpenWeatherMap) permit limited commercial use with attribution. For scraping, always check
robots.txtand the terms of service.
Where this is used: portals, travel, and real estate
A weather block is a classic feature of projects tied to geography and users' lifestyles:
- City portals show current weather and the forecast on the homepage — it keeps the audience around and makes the site a daily entry point.
- Travel portals and booking services add weather to a destination card: a traveler wants to know what awaits them at the destination.
- Overseas real-estate sites use weather as part of "selling the climate": sunny days, mild winters, and comfortable temperatures are a weighty argument when choosing a home by the sea or in the mountains.
Interestingly, technically, scraping weather data sits right alongside another popular task for these projects — collecting and aggregating listings. If weather makes a destination card feel "alive," then filling the catalog with properties is the job of real-estate scraping: automatically gathering offers from source platforms (Zillow, Realtor.com, Rightmove, Domain.com.au), normalizing prices and features, and keeping the database fresh. These two modules often work side by side — one supplies content about the property, the other the context around it.
Conclusion
For most tasks, start with Open-Meteo (a fast start with no key) or OpenWeatherMap (a rich free tier and a mature ecosystem). If historical data matters, look at Visual Crossing; if you need a business-grade provider with global coverage, look at AccuWeather or The Weather Company. Technically, integration takes only a few lines in any language: an HTTP request, JSON parsing, a cache. And a well-placed weather block noticeably boosts engagement on city portals, travel sites, and real-estate projects — anywhere the user cares not only about the object itself, but about the context around it.
If you would rather receive clean, structured feeds than build and babysit collectors, scraping.pro offers custom data extraction and data as a service — including weather, listings, and monitoring pipelines delivered straight to your systems.