What you need
Open-Meteo offers a forecast API and a geocoding API that turns a place name into coordinates. Both are free without a key and allow calls from any web page (CORS), so a plain HTML file is enough. The free use is for non-commercial projects; see the end of this guide for a commercial site.
Step 1: find the city
The geocoding API searches places by name. count=1 asks for the best match only. The answer has latitude, longitude, name and country for each result, or no results at all when nothing matches.
curl
curl 'https://geocoding-api.open-meteo.com/v1/search?name=Amsterdam&count=1'
Step 2: get the weather
The forecast API takes the coordinates and the values you want. current asks for the weather right now, daily for one value per day, and timezone=auto gives the days in the local time of the place. weather_code is a WMO weather code: 0 is a clear sky, 61 light rain, 95 a thunderstorm.
curl
curl 'https://api.open-meteo.com/v1/forecast?latitude=52.37&longitude=4.89¤t=temperature_2m,weather_code,wind_speed_10m&daily=temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days=3'
Step 3: the complete page
Save this as an HTML file and open it in a browser. The script looks up the city, fetches the forecast and writes the result as text, so nothing a visitor types can end up as HTML on the page.
HTML
<form id="weather-form">
<input id="city" placeholder="City, e.g. Amsterdam" required>
<button>Show weather</button>
</form>
<p id="weather"></p>
<script>
const weatherCodes = {
0: 'clear sky', 1: 'mainly clear', 2: 'partly cloudy', 3: 'overcast',
45: 'fog', 48: 'fog', 51: 'light drizzle', 53: 'drizzle', 55: 'heavy drizzle',
61: 'light rain', 63: 'rain', 65: 'heavy rain', 71: 'light snow', 73: 'snow',
75: 'heavy snow', 80: 'rain showers', 81: 'rain showers', 82: 'heavy showers',
95: 'thunderstorm'
};
async function findCity(name) {
const url = 'https://geocoding-api.open-meteo.com/v1/search?count=1&name=' + encodeURIComponent(name);
const data = await (await fetch(url)).json();
if (!data.results) {
throw new Error('No place found called ' + name);
}
return data.results[0];
}
async function getWeather(latitude, longitude) {
const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + latitude + '&longitude=' + longitude
+ '¤t=temperature_2m,weather_code,wind_speed_10m'
+ '&daily=temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days=3';
return (await fetch(url)).json();
}
document.getElementById('weather-form').addEventListener('submit', async (event) => {
event.preventDefault();
const output = document.getElementById('weather');
try {
const city = await findCity(document.getElementById('city').value);
const weather = await getWeather(city.latitude, city.longitude);
const now = weather.current;
const days = weather.daily.time.map((day, i) =>
day + ': ' + weather.daily.temperature_2m_min[i] + ' to ' + weather.daily.temperature_2m_max[i] + ' °C');
output.textContent = city.name + ', ' + city.country + ': ' + now.temperature_2m + ' °C, '
+ (weatherCodes[now.weather_code] || 'weather code ' + now.weather_code)
+ ', wind ' + now.wind_speed_10m + ' km/h. ' + days.join('; ') + '.';
} catch (error) {
output.textContent = error.message;
}
});
</script>
Going further
Show the weather where the visitor is: the browser's own navigator.geolocation.getCurrentPosition() gives coordinates, with the visitor's permission, and you can skip step 1.
Do not ask the forecast again on every page view. Keep the answer for a place for, say, 30 minutes, in localStorage or on your server; forecasts do not change by the minute.
For a commercial site, use MET Norway Weather API: it is free also for commercial use, under an open licence that asks for attribution, a User-Agent that names your app and cached results, so call it from your server. For official US forecasts and weather alerts there is the National Weather Service API.