Jupyter notebooks are a convenient way to retrieve, analyze, and visualize weather data using Python.
In this tutorial, we’ll use the Visual Crossing Weather API to load weather data into a Jupyter notebook, convert the results into a pandas DataFrame, and create some simple weather charts.
If you want to explore or download weather datasets without writing code, you can also use Visual Crossing Weather Data.
To follow the examples, sign up for a free Visual Crossing account and obtain your Weather API key.
We’ll use:
- Python
- Jupyter Notebook or JupyterLab
requests- pandas
- matplotlib
- The Visual Crossing Timeline Weather API
What we’ll do
We’ll create a notebook that:
- Retrieves weather data for a location.
- Converts the returned daily weather records into a pandas DataFrame.
- Examines the resulting data.
- Plots daily maximum and minimum temperatures.
- Retrieves historical weather for a date range.
- Extends the analysis to precipitation and other weather elements.
The Timeline Weather API uses the same basic structure for historical weather, current conditions, and forecasts, making it particularly useful for notebook-based analysis.
For the complete API reference, see the Timeline Weather API documentation.
Create a Jupyter notebook
You can use a locally installed Jupyter environment, JupyterLab, or another notebook environment that supports Python.
Create a new Python notebook.
If your environment does not already contain the required Python packages, install them from a notebook cell:
%pip install requests pandas matplotlib
After installation, import the libraries we’ll use:
import os
import requests
import pandas as pd
import matplotlib.pyplot as plt
Get your Weather API key
Your Visual Crossing Weather API key authenticates Weather API requests.
For a quick private notebook, you can enter your API key directly while experimenting, but avoid committing API keys to shared notebooks or source repositories.
A better approach is to store the key in an environment variable:
API_KEY = os.environ.get("VISUAL_CROSSING_API_KEY")
if not API_KEY:
raise RuntimeError(
"VISUAL_CROSSING_API_KEY environment variable is not set"
)
Visual Crossing plans include usage and request limits, so notebook workflows should be designed to operate within the limits of the account plan being used.
If you’re new to the API, see Getting Started with the Weather API.
Retrieve weather data
Let’s retrieve daily weather for New York City.
API_KEY = os.environ.get("VISUAL_CROSSING_API_KEY")
if not API_KEY:
raise RuntimeError(
"VISUAL_CROSSING_API_KEY environment variable is not set"
)
LOCATION = "New York City, NY"
UNIT_GROUP = "us"
url = (
"https://weather.visualcrossing.com/"
"VisualCrossingWebServices/rest/services/timeline/"
f"{requests.utils.quote(LOCATION, safe='')}"
)
params = {
"key": API_KEY,
"unitGroup": UNIT_GROUP,
"include": "days",
"elements": (
"datetime,tempmax,tempmin,temp,"
"precip,precipprob,humidity,windspeed,conditions"
),
"contentType": "json"
}
response = requests.get(
url,
params=params,
timeout=20
)
response.raise_for_status()
weather = response.json()
The Weather API response is now available in the weather Python dictionary.
For example:
weather["resolvedAddress"]
returns the resolved location.
Daily weather records are available in:
weather["days"]
Inspect the daily weather data
Let’s display some of the returned values:
print("Weather for:", weather["resolvedAddress"])
for day in weather["days"]:
print(
day["datetime"],
day["tempmax"],
day["tempmin"],
day["conditions"]
)
Each daily record can contain fields such as:
datetime
tempmax
tempmin
temp
precip
precipprob
humidity
windspeed
conditions
The exact fields returned depend on the elements parameter in the API request.
Load the weather data into pandas
The daily records can be converted directly into a pandas DataFrame:
df = pd.DataFrame(weather["days"])
Display the first few rows:
df.head()
You can also inspect the columns:
df.columns
or get a summary of the DataFrame:
df.info()
This gives you standard pandas access to the returned weather data.
Convert the date column
Convert the Timeline datetime field into a pandas datetime column:
df["datetime"] = pd.to_datetime(
df["datetime"]
)
You can then use it as the DataFrame index if desired:
df = df.set_index("datetime")
Now the DataFrame is organized as a time series.
Plot daily maximum and minimum temperatures
A simple line chart makes it easy to compare daily high and low temperatures:
df[["tempmax", "tempmin"]].plot(
figsize=(10, 5)
)
plt.title(
f"Daily Temperatures - {weather['resolvedAddress']}"
)
plt.xlabel("Date")
plt.ylabel("Temperature")
plt.grid(True)
plt.show()
Because the dates are stored in the DataFrame index, pandas automatically uses them along the horizontal axis.
Plot precipitation
We can create another chart using daily precipitation:
df["precip"].plot(
kind="bar",
figsize=(10, 4)
)
plt.title(
f"Daily Precipitation - {weather['resolvedAddress']}"
)
plt.xlabel("Date")
plt.ylabel("Precipitation")
plt.show()
The unit of precipitation depends on the selected unitGroup.
Our example uses:
UNIT_GROUP = "us"
For metric data, use:
UNIT_GROUP = "metric"
Retrieve historical weather
The same Timeline Weather API can retrieve historical data simply by adding dates to the URL.
For example, let’s retrieve weather for New York City from July 1 through July 31, 2026.
LOCATION = "New York City, NY"
START_DATE = "2026-07-01"
END_DATE = "2026-07-31"
url = (
"https://weather.visualcrossing.com/"
"VisualCrossingWebServices/rest/services/timeline/"
f"{requests.utils.quote(LOCATION, safe='')}/"
f"{START_DATE}/{END_DATE}"
)
params = {
"key": API_KEY,
"unitGroup": "us",
"include": "days",
"elements": (
"datetime,tempmax,tempmin,temp,"
"precip,precipprob,humidity,windspeed,conditions"
),
"contentType": "json"
}
response = requests.get(
url,
params=params,
timeout=20
)
response.raise_for_status()
weather = response.json()
df = pd.DataFrame(weather["days"])
df["datetime"] = pd.to_datetime(
df["datetime"]
)
df = df.set_index("datetime")
df.head()
The same DataFrame and charting code can now be used with historical weather.
This is one of the advantages of the Timeline Weather API: historical and forecast weather use a consistent response structure.
If your primary goal is exploring or downloading historical datasets rather than building API requests manually, see Visual Crossing Weather Data.
Create a reusable weather function
For notebook analysis, it is often useful to wrap the Weather API request in a function.
def get_weather(
location,
start_date=None,
end_date=None,
unit_group="us"
):
api_key = os.environ.get(
"VISUAL_CROSSING_API_KEY"
)
if not api_key:
raise RuntimeError(
"VISUAL_CROSSING_API_KEY environment variable is not set"
)
path = requests.utils.quote(
location,
safe=""
)
if start_date:
path += f"/{start_date}"
if start_date and end_date:
path += f"/{end_date}"
url = (
"https://weather.visualcrossing.com/"
"VisualCrossingWebServices/rest/services/timeline/"
+ path
)
params = {
"key": api_key,
"unitGroup": unit_group,
"include": "days",
"elements": (
"datetime,tempmax,tempmin,temp,"
"precip,precipprob,humidity,"
"windspeed,conditions"
),
"contentType": "json"
}
response = requests.get(
url,
params=params,
timeout=20
)
response.raise_for_status()
return response.json()
You can then retrieve a forecast:
weather = get_weather(
"London, UK",
unit_group="metric"
)
or historical weather:
weather = get_weather(
"London, UK",
"2026-07-01",
"2026-07-31",
unit_group="metric"
)
Then convert the returned daily data into a DataFrame:
df = pd.DataFrame(
weather["days"]
)
df["datetime"] = pd.to_datetime(
df["datetime"]
)
df = df.set_index(
"datetime"
)
This provides a reusable starting point for notebook analysis.
Compare multiple weather variables
Once the data is in pandas, you can use standard DataFrame operations.
For example, display temperature, humidity, and wind speed:
df[
[
"tempmax",
"tempmin",
"humidity",
"windspeed"
]
].head()
Calculate summary statistics:
df[
[
"tempmax",
"tempmin",
"precip",
"humidity",
"windspeed"
]
].describe()
Find the hottest day:
df.loc[
df["tempmax"].idxmax()
]
Find the wettest day:
df.loc[
df["precip"].idxmax()
]
Calculate total precipitation:
df["precip"].sum()
These are standard pandas operations, so Weather API data can easily be combined with other datasets already being analyzed in the notebook.
Retrieve hourly weather
Daily data is useful for many analyses, but the Timeline Weather API can also return hourly weather.
Change:
"include": "days"
to:
"include": "hours"
and request appropriate hourly elements:
params = {
"key": API_KEY,
"unitGroup": "us",
"include": "hours",
"elements": (
"datetime,temp,feelslike,"
"precip,precipprob,humidity,"
"windspeed,conditions"
),
"contentType": "json"
}
Hourly records are stored within each daily record.
For example:
hours = weather["days"][0]["hours"]
hourly_df = pd.DataFrame(hours)
hourly_df.head()
For a request containing multiple days, you can combine all the hourly records:
hourly_records = []
for day in weather["days"]:
for hour in day["hours"]:
hourly_records.append({
"date": day["datetime"],
**hour
})
hourly_df = pd.DataFrame(
hourly_records
)
This gives you a single DataFrame containing the hourly observations or forecasts.
Handle Weather API errors
Notebook code should expose useful information when a Weather API request fails.
For example:
try:
response = requests.get(
url,
params=params,
timeout=20
)
response.raise_for_status()
weather = response.json()
except requests.RequestException as error:
print(
"Unable to retrieve weather data:"
)
print(error)
if (
hasattr(error, "response")
and error.response is not None
):
print(error.response.text)
raise
When troubleshooting a failed request, the HTTP status code and API response body are often the most useful pieces of information.
Possible causes include:
- An invalid API key
- An invalid location
- Invalid dates
- Invalid request parameters
- Account usage limits
- Network connectivity problems
- Request timeouts
Request only the elements you need
The Timeline Weather API can return many weather variables.
Use the include and elements parameters to control the returned data.
For example, a temperature-only analysis could request:
params = {
"key": API_KEY,
"unitGroup": "metric",
"include": "days",
"elements": (
"datetime,tempmax,tempmin,temp"
),
"contentType": "json"
}
For an analysis involving precipitation and humidity:
params = {
"key": API_KEY,
"unitGroup": "metric",
"include": "days",
"elements": (
"datetime,tempmax,tempmin,"
"precip,humidity"
),
"contentType": "json"
}
For the complete list of available weather elements, see the Timeline Weather API documentation.
Going further
Jupyter notebooks are particularly useful when weather data needs to be analyzed alongside other datasets.
Possible uses include:
- Historical weather analysis
- Climate and environmental research
- Energy modeling
- Agricultural analysis
- Transportation analysis
- Retail and demand analysis
- Machine learning
- Statistical modeling
- Data visualization
- Comparing weather with business or sensor data
The Visual Crossing Weather API provides programmatic access to historical weather, current conditions, forecasts, alerts, and additional weather information.
For interactive weather-data exploration and downloads, see Visual Crossing Weather Data.
If you haven’t created an account, sign up for a free Visual Crossing account.
For detailed API request options and response fields, see the Timeline Weather API documentation.
Summary
Jupyter and pandas provide a convenient environment for working with Visual Crossing Weather API data.
The basic process is:
- Obtain a Visual Crossing Weather API key.
- Retrieve Timeline Weather API data using Python and
requests. - Check the HTTP response for errors.
- Parse the returned JSON.
- Convert the
daysorhoursrecords into a pandas DataFrame. - Convert dates into pandas datetime values.
- Analyze and visualize the weather data using standard pandas and matplotlib tools.
Because the Timeline Weather API provides historical and forecast weather through a consistent response structure, the same notebook workflow can support both exploratory analysis and repeatable weather-data research.

