Mathew K Analytics

Lesson 21 · Data analytics zero to hero

Pull Data from APIs with Python | Data Analytics #21

Video twenty-one of the 30-part series, and a new block: connecting directly to a live data source over the internet, instead of a local file. We're using…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Data Analytics Zero to Hero, Video 21: Working with APIs and Web Data#

  • Video twenty-one of the 30-part series, and a new block: connecting directly to a live data source over the internet, instead of a local file.
  • We're using Open-Meteo, a real, free weather API that needs no signup and no API key.
  • Let's jump straight in.

Before You Start#

  • Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
  • Install requests if you haven't already: pip install requests.
  • You'll need an active internet connection for this video, since every cell here calls a real, live API.

Part 1: A First Real API Request#

import requests

url = 'https://api.open-meteo.com/v1/forecast'
params = {'latitude': 52.52, 'longitude': 13.41, 'current_weather': True}
response = requests.get(url, params=params)
print(response.status_code)
print(response.url)
200
https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=True
data = response.json()
print(data.keys())
print(data['current_weather'])
dict_keys(['latitude', 'longitude', 'generationtime_ms', 'utc_offset_seconds', 'timezone', 'timezone_abbreviation', 'elevation', 'current_weather_units', 'current_weather'])
{'time': '2026-08-16T02:15', 'interval': 900, 'temperature': 19.4, 'windspeed': 8.7, 'winddirection': 343, 'is_day': 0, 'weathercode': 95}

Part 2: A Geocoding Lookup#

geo_url = 'https://geocoding-api.open-meteo.com/v1/search'
geo_params = {'name': 'Nairobi', 'count': 1}
geo_response = requests.get(geo_url, params=geo_params)
geo_data = geo_response.json()
result = geo_data['results'][0]
print(result['name'], result['country'], result['latitude'], result['longitude'])
Nairobi Kenya -1.28333 36.81667
def get_current_weather(city_name):
    geo = requests.get('https://geocoding-api.open-meteo.com/v1/search', params={'name': city_name, 'count': 1}).json()
    place = geo['results'][0]
    weather = requests.get('https://api.open-meteo.com/v1/forecast', params={
        'latitude': place['latitude'], 'longitude': place['longitude'], 'current_weather': True
    }).json()
    return place['name'], place['country'], weather['current_weather']['temperature']

for city in ['Tokyo', 'Cairo', 'Toronto']:
    name, country, temp = get_current_weather(city)
    print(f'{name}, {country}: {temp}C')
Tokyo, Japan: 23.6C
Cairo, Egypt: 25.0C
Toronto, Canada: 18.3C

Part 3: Handling Errors#

try:
    bad_response = requests.get('https://api.open-meteo.com/v1/forecast', params={'latitude': 999, 'longitude': 999})
    bad_response.raise_for_status()
except requests.exceptions.HTTPError as e:
    print(f'Real HTTP error caught: {e}')
except requests.exceptions.RequestException as e:
    print(f'Real request failed entirely: {e}')
Real HTTP error caught: 400 Client Error: Bad Request for url: https://api.open-meteo.com/v1/forecast?latitude=999&longitude=999

Part 4: Turning a Real API Response into a DataFrame#

import pandas as pd

hourly_params = {'latitude': 52.52, 'longitude': 13.41, 'hourly': 'temperature_2m,precipitation'}
hourly_response = requests.get('https://api.open-meteo.com/v1/forecast', params=hourly_params)
hourly_data = hourly_response.json()['hourly']
forecast_df = pd.DataFrame(hourly_data)
forecast_df['time'] = pd.to_datetime(forecast_df['time'])
print(forecast_df.head())
print(forecast_df.shape)
                 time  temperature_2m  precipitation
0 2026-08-16 00:00:00            21.6            0.0
1 2026-08-16 01:00:00            20.8            0.0
2 2026-08-16 02:00:00            19.8            0.1
3 2026-08-16 03:00:00            18.1            4.8
4 2026-08-16 04:00:00            17.9            0.9
(168, 3)
daily_avg = forecast_df.set_index('time')['temperature_2m'].resample('D').mean().round(1)
print(daily_avg)
time
2026-08-16    20.6
2026-08-17    19.0
2026-08-18    16.8
2026-08-19    19.5
2026-08-20    21.1
2026-08-21    21.7
2026-08-22    16.6
Freq: D, Name: temperature_2m, dtype: float64

Wrap-Up: What You Learned#

  • Making a real GET request with requests.get, and reading status_code and json.
  • Passing query parameters as a plain dictionary, and chaining two real API endpoints together.
  • Handling real failures with raise_for_status and try/except.
  • Converting a real JSON API response directly into a pandas DataFrame, ready for every tool from earlier in this series.
  • All of it against Open-Meteo, a real, free, live weather API. Video twenty-two covers web scraping: pulling real structured data straight out of a web page's HTML when no API exists. Subscribe so it lands automatically see you there.

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.