How to Add a Weather Forecast to Your Website

Adding a weather forecast to a website can be as simple as retrieving forecast data from a weather API and displaying the results with HTML, CSS, and JavaScript.

In this tutorial, we’ll use the Visual Crossing Timeline Weather API to build a simple multi-day weather forecast for a web page. The example uses standard browser JavaScript with the Fetch API, so no JavaScript framework or additional library such as jQuery is required.

We’ll also discuss how to structure the solution for a production website so that your Weather API key remains secure.

What we’ll build

Our example will display a five-day forecast including:

  • Date
  • Weather conditions
  • Maximum temperature
  • Minimum temperature
  • Chance of precipitation

The same approach can be extended to include hourly weather, current conditions, wind, humidity, snow, solar radiation, severe weather alerts, and other weather data available from the Timeline Weather API.

1. Get your Visual Crossing Weather API key

To use the Visual Crossing Weather API, first create a free Visual Crossing account and obtain your Weather API key from your account page.

Your API key authenticates Weather API requests and associates those requests with your Visual Crossing account and plan.

In the examples below, replace:

YOUR_API_KEY

with your own API key when testing the code.

Important: An API key included directly in browser-side JavaScript can be viewed by visitors to your website. This is useful for demonstrating how the API works, but a production public website should normally make Weather API requests through server-side code so that the API key is not exposed to the browser. We’ll discuss this in more detail later in the article.

2. Create a weather forecast API request

The Visual Crossing Timeline Weather API provides forecast, current, and historical weather data through the same API.

A basic forecast request has the following form:

https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/[location]?key=YOUR_API_KEY

For example, the following request retrieves the forecast for London using metric units:

https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/London%2CUK?unitGroup=metric&include=days&key=YOUR_API_KEY&contentType=json

When no date is supplied, the Timeline Weather API returns the available weather forecast.

For our web page, we only need daily forecast data, so we use:

include=days

We can reduce the response further by requesting only the weather elements that our page needs:

elements=datetime,tempmax,tempmin,precipprob,conditions,icon

Our final request therefore looks like:

https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/London%2CUK?unitGroup=metric&include=days&elements=datetime,tempmax,tempmin,precipprob,conditions,icon&key=YOUR_API_KEY&contentType=json

Requesting only the data that your application uses reduces the response size and makes the returned JSON easier to process.

You can also use the Visual Crossing Weather Query Builder to create requests interactively, inspect the returned data, and experiment with different locations, units, and weather elements.

3. Understanding the forecast response

The Timeline Weather API returns JSON data.

Daily forecast records are contained in the days array:

data.days

For example, the first forecast day can be accessed using:

data.days[0]

A daily record contains values such as:

day.datetime
day.tempmax
day.tempmin
day.precipprob
day.conditions
day.icon

For example, a simplified daily result might contain:

{
  "datetime": "2026-08-21",
  "tempmax": 24.8,
  "tempmin": 16.2,
  "precipprob": 35,
  "conditions": "Partially cloudy",
  "icon": "partly-cloudy-day"
}

We’ll use these values to create the forecast display.

4. Add a forecast container to your web page

First, create an HTML element where the forecast will appear:

<div id="weather-forecast"></div>

JavaScript will populate this element after retrieving the forecast from the Weather API.

5. Retrieve the forecast using JavaScript

Modern browsers provide the Fetch API for making HTTP requests, so no additional JavaScript library is required.

Here’s a simple request:

const apiKey = "YOUR_API_KEY";
const location = "London, UK";

const url =
  "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" +
  encodeURIComponent(location) +
  "?unitGroup=metric" +
  "&include=days" +
  "&elements=datetime,tempmax,tempmin,precipprob,conditions,icon" +
  "&key=" + encodeURIComponent(apiKey) +
  "&contentType=json";

fetch(url)
  .then(response => {
    if (!response.ok) {
      throw new Error("Weather API request failed: " + response.status);
    }

    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

There are three main steps here.

First, we build the Weather API request. encodeURIComponent() ensures that locations containing spaces, commas, and other characters are correctly encoded for use in a URL.

Next:

fetch(url)

sends the request to the Timeline Weather API.

Finally:

response.json()

converts the returned JSON into a JavaScript object that we can use to build the forecast.

6. Display the forecast

Now let’s turn the returned daily weather data into HTML.

We’ll display the first five days of the forecast:

function displayForecast(data) {
  const container = document.getElementById("weather-forecast");

  container.innerHTML = "";

  data.days.slice(0, 5).forEach(day => {
    const forecastDay = document.createElement("div");
    forecastDay.className = "forecast-day";

    forecastDay.innerHTML = `
      <div class="forecast-date">${formatDate(day.datetime)}</div>
      <div class="forecast-icon">${weatherIcon(day.icon)}</div>
      <div class="forecast-conditions">${day.conditions}</div>

      <div class="forecast-temp">
        <span class="forecast-high">${Math.round(day.tempmax)}°</span>
        <span class="forecast-low">${Math.round(day.tempmin)}°</span>
      </div>

      <div class="forecast-precip">
        Precipitation: ${Math.round(day.precipprob || 0)}%
      </div>
    `;

    container.appendChild(forecastDay);
  });
}

The call:

data.days.slice(0, 5)

selects the first five daily forecast records.

You can change 5 to display more or fewer days.

7. Format the forecast date

The datetime field for a daily forecast uses a date such as:

2026-08-21

We can convert this into a more readable value:

function formatDate(dateString) {
  const date = new Date(dateString + "T00:00:00Z");

  return new Intl.DateTimeFormat("en-US", {
    weekday: "short",
    month: "short",
    day: "numeric",
    timeZone: "UTC"
  }).format(date);
}

For example:

2026-08-21

becomes:

Fri, Aug 21

Using the date as a calendar date rather than applying the user’s local time-zone offset helps avoid accidentally displaying the previous or following date.

8. Display weather icons

The Timeline Weather API provides an icon field that summarizes the expected weather conditions.

Typical values include:

clear-day
partly-cloudy-day
cloudy
rain
snow
fog
wind

For this simple example, we can map those values to Unicode weather symbols:

function weatherIcon(icon) {
  const icons = {
    "clear-day": "☀️",
    "clear-night": "🌙",
    "partly-cloudy-day": "🌤️",
    "partly-cloudy-night": "☁️",
    "cloudy": "☁️",
    "rain": "🌧️",
    "snow": "🌨️",
    "fog": "🌫️",
    "wind": "💨"
  };

  return icons[icon] || "🌤️";
}

For a production application, you can replace these symbols with your own SVG, PNG, icon font, or other weather icon set.

9. Put the JavaScript together

Our complete JavaScript now looks like this:

const apiKey = "YOUR_API_KEY";
const location = "London, UK";
const unitGroup = "metric";

const url =
  "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" +
  encodeURIComponent(location) +
  "?unitGroup=" + encodeURIComponent(unitGroup) +
  "&include=days" +
  "&elements=datetime,tempmax,tempmin,precipprob,conditions,icon" +
  "&key=" + encodeURIComponent(apiKey) +
  "&contentType=json";

fetch(url)
  .then(response => {
    if (!response.ok) {
      throw new Error("Weather API request failed: " + response.status);
    }

    return response.json();
  })
  .then(data => {
    displayForecast(data);
  })
  .catch(error => {
    console.error(error);

    document.getElementById("weather-forecast").textContent =
      "Weather forecast is currently unavailable.";
  });


function displayForecast(data) {
  const container = document.getElementById("weather-forecast");

  container.innerHTML = "";

  data.days.slice(0, 5).forEach(day => {
    const forecastDay = document.createElement("div");
    forecastDay.className = "forecast-day";

    forecastDay.innerHTML = `
      <div class="forecast-date">${formatDate(day.datetime)}</div>
      <div class="forecast-icon">${weatherIcon(day.icon)}</div>
      <div class="forecast-conditions">${day.conditions}</div>

      <div class="forecast-temp">
        <span class="forecast-high">${Math.round(day.tempmax)}°</span>
        <span class="forecast-low">${Math.round(day.tempmin)}°</span>
      </div>

      <div class="forecast-precip">
        Precipitation: ${Math.round(day.precipprob || 0)}%
      </div>
    `;

    container.appendChild(forecastDay);
  });
}


function formatDate(dateString) {
  const date = new Date(dateString + "T00:00:00Z");

  return new Intl.DateTimeFormat("en-US", {
    weekday: "short",
    month: "short",
    day: "numeric",
    timeZone: "UTC"
  }).format(date);
}


function weatherIcon(icon) {
  const icons = {
    "clear-day": "☀️",
    "clear-night": "🌙",
    "partly-cloudy-day": "🌤️",
    "partly-cloudy-night": "☁️",
    "cloudy": "☁️",
    "rain": "🌧️",
    "snow": "🌨️",
    "fog": "🌫️",
    "wind": "💨"
  };

  return icons[icon] || "🌤️";
}

10. Add some CSS

The forecast now contains all the information we need, but we still need to format it.

Here’s a simple responsive layout:

#weather-forecast {
  display: grid;
  grid-template-columns: repeat(5, minmax(120px, 1fr));
  gap: 12px;
  max-width: 900px;
  font-family: Arial, sans-serif;
}

.forecast-day {
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
  text-align: center;
}

.forecast-date {
  font-weight: 600;
  margin-bottom: 10px;
}

.forecast-icon {
  font-size: 36px;
  margin-bottom: 8px;
}

.forecast-conditions {
  min-height: 40px;
  margin-bottom: 10px;
}

.forecast-temp {
  font-size: 20px;
  margin-bottom: 8px;
}

.forecast-high {
  font-weight: 600;
}

.forecast-low {
  margin-left: 8px;
}

.forecast-precip {
  font-size: 13px;
}

@media (max-width: 700px) {
  #weather-forecast {
    grid-template-columns: 1fr;
  }

  .forecast-day {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 10px;
    align-items: center;
    text-align: left;
  }
}

You can replace these styles with your own website’s design system.

11. Complete example

Putting everything together gives us a complete web page:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>Weather Forecast</title>

  <style>
    #weather-forecast {
      display: grid;
      grid-template-columns: repeat(5, minmax(120px, 1fr));
      gap: 12px;
      max-width: 900px;
      font-family: Arial, sans-serif;
    }

    .forecast-day {
      padding: 16px;
      border: 1px solid #ddd;
      border-radius: 8px;
      text-align: center;
    }

    .forecast-date {
      font-weight: 600;
      margin-bottom: 10px;
    }

    .forecast-icon {
      font-size: 36px;
      margin-bottom: 8px;
    }

    .forecast-conditions {
      min-height: 40px;
      margin-bottom: 10px;
    }

    .forecast-temp {
      font-size: 20px;
      margin-bottom: 8px;
    }

    .forecast-high {
      font-weight: 600;
    }

    .forecast-low {
      margin-left: 8px;
    }

    .forecast-precip {
      font-size: 13px;
    }

    @media (max-width: 700px) {
      #weather-forecast {
        grid-template-columns: 1fr;
      }
    }
  </style>
</head>

<body>

  <h1>London Weather Forecast</h1>

  <div id="weather-forecast">Loading forecast...</div>

  <script>
    const apiKey = "YOUR_API_KEY";
    const location = "London, UK";
    const unitGroup = "metric";

    const url =
      "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" +
      encodeURIComponent(location) +
      "?unitGroup=" + encodeURIComponent(unitGroup) +
      "&include=days" +
      "&elements=datetime,tempmax,tempmin,precipprob,conditions,icon" +
      "&key=" + encodeURIComponent(apiKey) +
      "&contentType=json";

    fetch(url)
      .then(response => {
        if (!response.ok) {
          throw new Error("Weather API request failed: " + response.status);
        }

        return response.json();
      })
      .then(data => {
        displayForecast(data);
      })
      .catch(error => {
        console.error(error);

        document.getElementById("weather-forecast").textContent =
          "Weather forecast is currently unavailable.";
      });


    function displayForecast(data) {
      const container = document.getElementById("weather-forecast");

      container.innerHTML = "";

      data.days.slice(0, 5).forEach(day => {
        const forecastDay = document.createElement("div");
        forecastDay.className = "forecast-day";

        forecastDay.innerHTML = `
          <div class="forecast-date">${formatDate(day.datetime)}</div>
          <div class="forecast-icon">${weatherIcon(day.icon)}</div>
          <div class="forecast-conditions">${day.conditions}</div>

          <div class="forecast-temp">
            <span class="forecast-high">${Math.round(day.tempmax)}°</span>
            <span class="forecast-low">${Math.round(day.tempmin)}°</span>
          </div>

          <div class="forecast-precip">
            Precipitation: ${Math.round(day.precipprob || 0)}%
          </div>
        `;

        container.appendChild(forecastDay);
      });
    }


    function formatDate(dateString) {
      const date = new Date(dateString + "T00:00:00Z");

      return new Intl.DateTimeFormat("en-US", {
        weekday: "short",
        month: "short",
        day: "numeric",
        timeZone: "UTC"
      }).format(date);
    }


    function weatherIcon(icon) {
      const icons = {
        "clear-day": "☀️",
        "clear-night": "🌙",
        "partly-cloudy-day": "🌤️",
        "partly-cloudy-night": "☁️",
        "cloudy": "☁️",
        "rain": "🌧️",
        "snow": "🌨️",
        "fog": "🌫️",
        "wind": "💨"
      };

      return icons[icon] || "🌤️";
    }
  </script>

</body>
</html>

Save the page, open it in your browser, and the forecast will be loaded from the Timeline Weather API.

Using a different location

To change the forecast location, simply change:

const location = "London, UK";

For example:

const location = "New York, NY";

or:

const location = "38.9697,-77.385";

The Timeline Weather API accepts addresses, partial addresses, cities, postal codes, and latitude/longitude coordinates.

If your website already knows the user’s location, you can use that location when constructing the Weather API request.

Changing the units

The examples above use metric units:

const unitGroup = "metric";

For US units, use:

const unitGroup = "us";

You can therefore change the weather display based on your application’s location or user preferences.

Adding current weather

The Timeline Weather API can return current conditions as well as forecast data.

For example, request both current conditions and daily forecast information using:

include=current,days

The current conditions are then available under:

data.currentConditions

For example:

data.currentConditions.temp
data.currentConditions.humidity
data.currentConditions.conditions
data.currentConditions.icon

This makes it easy to add a “Current Weather” section above the multi-day forecast.

Adding hourly weather

Hourly forecast data is available by including:

include=hours

Each forecast day then contains an hours array:

data.days[0].hours

For example:

data.days[0].hours.forEach(hour => {
  console.log(
    hour.datetime,
    hour.temp,
    hour.precipprob,
    hour.conditions
  );
});

The same basic technique can therefore be used to create hourly forecast charts, tables, or interactive weather displays.

Protecting your API key on a public website

The browser-side example in this tutorial is useful for learning how a web page communicates with the Weather API. However, JavaScript downloaded by a visitor is visible to that visitor.

That means an API key stored like this:

const apiKey = "YOUR_API_KEY";

cannot be considered private.

For a production public website, the recommended architecture is:

Visitor's browser
       ↓
Your web server
       ↓
Visual Crossing Weather API

Your web server stores the Visual Crossing API key and makes the Weather API request. The browser calls your server rather than calling the Weather API with the API key directly.

For example, your website might request:

/api/weather?location=London

Your server then adds the private Visual Crossing API key, calls the Timeline Weather API, and returns the required weather data to the page.

This approach also gives you an opportunity to cache forecast results, validate requested locations, control how frequently users can request data, and reduce unnecessary API usage.

Be aware of your Weather API plan limits

Visual Crossing plans include usage and request limits that should be followed when building a website or application.

Consider how many locations your website requests, how frequently forecasts are refreshed, and how many visitors will use the application. Avoid repeatedly retrieving identical weather data when a previously retrieved result can be reused.

For public or high-traffic websites, server-side caching can significantly reduce duplicate requests and help keep Weather API usage predictable.

Using the Timeline Weather API for more than forecasts

The example in this article uses the Timeline Weather API to retrieve a weather forecast, but the same API can also provide:

  • Historical weather observations
  • Current conditions
  • Hourly forecasts
  • Daily forecasts
  • Long-range statistical forecasts
  • Weather alerts
  • Air quality data
  • Solar and radiation data
  • Agricultural weather variables
  • Additional weather and environmental information

Because historical, current, and forecast data use the same Timeline API structure, you can expand the example without adopting a different API.

Summary

Adding a weather forecast to a web page requires only a few basic steps:

  1. Create a Visual Crossing Weather account and obtain an API key.
  2. Create a Timeline Weather API request for your location.
  3. Retrieve the forecast using JavaScript or your server-side application.
  4. Read the forecast from the returned days array.
  5. Build the forecast display using HTML and CSS.
  6. For production public websites, keep your API key on the server and consider caching forecast results.

The example in this tutorial provides a simple starting point that can be expanded into anything from a small five-day forecast to a complete weather dashboard.

Use the Visual Crossing Weather Query Builder to explore available weather data and build requests, or see the Timeline Weather API documentation for the complete API reference.