sequenceDiagram
participant C as Your laptop
participant S as api.worldbank.org
C->>S: GET /v2/country/BRA/...
Note right of S: look up data
S-->>C: 200 OK + JSON
C->>C: parse and analyse
Lecture 18 - Web APIs and JSON
t3.micro running Ubuntu 26.04ssh -iaptscp and wgett3.micro left running costs about $12 a monthSource: K21
Create caseAccount and billing support and explain what happenedrequestsdata/raw/. The report reads only that snapshotThe starter repository: https://github.com/danilofreire/datasci350-project-starter
| Component | Weight |
|---|---|
| Reproducibility | 30% |
| Analysis quality | 30% |
| Communication | 20% |
| Code quality | 10% |
| Git workflow | 10% |
If docker run on my machine does not reproduce your report, your project does not exist 😅
.env fileopenai library hid the detailsbase_url is a web address. By the end of today you will know what the library sent there, and what came back
1. What an API is
2. URLs and status codes
3. JSON
4. APIs from the terminal
curl, a browser for your terminal5. A first taste of Python
curl did. Lecture 19 takes them apart/country/BRA/indicator/SP.POP.TOTL into Brazil’s population, year by yearSource: Manutan
| Restaurant | API |
|---|---|
| Menu | Documentation |
| Order | HTTP request |
| Kitchen | Server |
| Dish | JSON response |
| “We’re out of that” | 404 Not Found |
| “One order per customer” | Rate limit |
sequenceDiagram
participant C as Your laptop
participant S as api.worldbank.org
C->>S: GET /v2/country/BRA/...
Note right of S: look up data
S-->>C: 200 OK + JSON
C->>C: parse and analyse
pandas has an API, and so does your operating systemcurl/country/BRA). Most public data APIs follow it/v2/country/{code}/indicator/{code}| Kind | What it costs you | Examples |
|---|---|---|
| Open and keyless | Nothing. Just fetch the URL | Open-Meteo, World Bank, National Weather Service, USGS earthquakes, GitHub |
| Free but key-required | A signup form, then a key in every request | NASA, OpenWeatherMap, OpenRouter’s free models, GitHub for higher limits |
| Paid | A key and a bill | OpenRouter’s paid models, most commercial data vendors |
https://api.worldbank.org/v2/country/BRA/indicator/NY.GDP.PCAP.KD?format=json&date=2014:2025
| Part | Name | What it does |
|---|---|---|
| https | scheme | Which protocol. Almost always https |
| api.worldbank.org | host | Which machine to ask |
| /v2/country/BRA/indicator/… | path | Which data on that machine |
| ? | separator | Ends the path. Everything after it is the query string |
| format=json&date=2014:2025 | query string | Options, as key=value pairs joined by & |
| Step | Add this | What it means |
|---|---|---|
| 1 | https://api.worldbank.org/v2 |
The API’s base URL, version 2 |
| 2 | /country/BRA |
Brazil, by its three-letter code |
| 3 | /indicator/NY.GDP.PCAP.KD |
GDP per person, in constant US dollars |
| 4 | ?format=json |
Start the options. Send JSON, not XML |
| 5 | &date=2014:2025 |
Only these years |
BRA for URY and you get UruguaySP.POP.TOTL and you get populationNY.GDP.PCAP.KD:
NY = national accounts (income)GDP = gross domestic productPCAP = per capitaKD = constant US dollars?key=value, and & joins them?format=json&date=2014:2025&per_page=100
^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^
option 1 option 2 option 3
?a=1&b=2 and ?b=2&a=1 are the same request?, &, =, #, and the space%20, and a / becomes %2FAmerica/New_York travels as America%2FNew_Yorkrequests library does this for you, so you rarely type it2xx worked, 4xx is your mistake, 5xx is theirs| Code | Name | What it really means |
|---|---|---|
200 |
OK | It worked. Go ahead and read the data |
301 / 302 |
Moved | The data lives elsewhere now. Browsers and requests follow these for you |
400 |
Bad Request | Your URL is malformed. Check the query string |
401 |
Unauthorized | You need a key, or yours is wrong |
403 |
Forbidden | Your key works, but it does not give you access to this |
404 |
Not Found | Nothing at that path. Usually a typo |
429 |
Too Many Requests | You are asking too fast. Slow down |
500 |
Internal Server Error | Their problem. Wait and retry |
Some APIs, like the World Bank, answer errors with 200 and hide the failure in the reply. A status check alone is not enough. We will see this in the terminal soon
GET: “please send me this”POST: “here is some data”openai library sent your prompt as a POSTOther methods exist (PUT, DELETE, PATCH), but you rarely need them to read public data
GET and shows the raw replyhttps://api.open-meteo.com/v1/forecast33.75, longitude -84.39What to look for
longitude option. What does the page say?Stuck, or want to compare your URL with mine?
{ } holds "key": value pairs[ ] holds values in orderEach JSON type has a Python twin
| JSON | Python |
|---|---|
{...} object |
dict (dictionary) |
[...] array |
list |
"text" |
str (string) |
42, 3.14 |
int, float |
true / false |
True / False |
null |
None |
"sensors" is an array inside an objectShortened from the saved reply, openmeteo_atlanta.json
{ } is an object. It has nine keys, and six are shown herecurrent and current_units, hold objects of their owncurrent. Its unit is inside current_unitstime is text, because JSON has no date typecurrent → temperature_2m → 33.2
current_units → temperature_2m → °C
Shortened from wb_gdp_bra.json, the reply to our Brazil URL
A reply from a fictional course API:
{
"department": "Data and Decision Sciences",
"term": "Fall 2026",
"courses": [
{"code": "DATASCI 350",
"title": "Data Science Computing",
"enrolled": 40,
"instructor": {"name": "Danilo Freire",
"office": "PAIS 480"}},
{"code": "DATASCI 101",
"title": "Introduction to AI Applications",
"enrolled": 65,
"instructor": {"name": "Danilo Freire",
"office": "PAIS 480"}}
],
"updated": null
}Write the path to each value, like term → “Fall 2026”:
updated. What Python type will it become?Stuck, or want to compare your paths with mine?
curl: a browser for your terminalcurl sends a GET to a URL and prints the replycurl --version
sudo apt install curlbrew install curl gets a newer version, but the built-in one works fine& and ? as its own symbols{"latitude":33.759865,"longitude":-84.39586,
"generationtime_ms":0.0399,"utc_offset_seconds":0,
"timezone":"GMT","timezone_abbreviation":"GMT",
"elevation":316.0,"current_units":{"time":"iso8601",
"interval":"seconds","temperature_2m":"°C"},
"current":{"time":"2026-09-27T06:30","interval":900,
"temperature_2m":15.8}}|) into Python’s JSON tool to indent it:-s means “silent”: no progress barpython3 -m json.tool reads JSON and prints it with indentation
-m means “module”: it runs a tool that ships with Python, so there is nothing to install-i shows the headers, and the first line is the status-i asks curl to print the response headers before the data| head -1 keeps only the first line, which holds the statusXYZ:200-o means “output”: save the reply to a file instead of printing itdata/ folder were madedata/raw/, and the report reads only that fileURY, indicator SP.POP.TOTLcurl to wb_pop_ury.jsonpython3 -m json.toolWhat to look for
Stuck, or want to compare your commands with mine?
requests is the Python library for talking to web APIspip install requestsrequests.get(url) does what curl did: it sends a GET and keeps the reply in rr.status_code is the status code, here 200r.json() turns the JSON text into a Python dictionarydata["current"]["temperature_2m"] is the path from earlier, one pair of brackets per steprequests build the query string for youpull_data.py line by lineEverything you did today still applies:
2xx, 4xx, or 5xxparams, and checking them for errorsBefore then
A list of keyless APIs to explore is in Appendix 06
Group names are due by Thursday 5 November, and I assign the rest at random
Open-Meteo needs latitude and longitude, and current asks for present conditions
https://api.open-meteo.com/v1/forecast?latitude=33.75
&longitude=-84.39¤t=temperature_2m
Adding a timezone makes the time readable:
https://api.open-meteo.com/v1/forecast?latitude=33.75
&longitude=-84.39¤t=temperature_2m
&timezone=America%2FNew_York
Without longitude, the page shows:
/ in the timezone is written %2F, because / already has a job in a URLtimezone, the time comes back in GMT, which reads oddly for Atlantacourses → 1 → title → “Introduction to AI Applications”courses → 0 → instructor → office → “PAIS 480”courses → 0 → enrolled is 40, and courses → 1 → enrolled is 65. The total is 105updated → null, which becomes None in Pythoncourses holds an array. Each course and each instructor is an objectThe same paths in Python, which we write in Lecture 19:
courses is an array, so the second course is position 1, not 2instructor, then officenull is JSON’s word for “no value”. It is not the text "null"Part of the output, around 2020:
The path: 1 → 5 → value → 3,398,968
0, so 2020 is at position 5200, with an error message inside the JSONdata/wb_pop_ury.json| You write | It travels as | You write | It travels as | |
|---|---|---|---|---|
| space | %20 or + |
= |
%3D |
|
/ |
%2F |
: |
%3A |
|
? |
%3F |
# |
%23 |
|
& |
%26 |
% |
%25 |
% and the character’s byte in hexadecimal (remember them from Lecture 02? 😉)America/New_York becomes America%2FNew_Yorkrequests encodes values for you, so you rarely write these by hand| Header | Purpose |
|---|---|
User-Agent |
Who is asking. Browsers set this, and polite scripts should too |
Accept |
What format you want back (application/json) |
Authorization |
Your API key, for APIs that need one |
Content-Type |
The format of the data being sent |
-I asks for the headers only, without the datacontent-type confirms the reply is JSONmax-age=86400 says the reply can be reused for a day (86,400 seconds)All of these answered a keyless request on 21 August 2026
| API | What it gives you | Documentation |
|---|---|---|
| Open-Meteo | Weather forecasts and history, anywhere | https://open-meteo.com/en/docs |
| World Bank | 29,544 development indicators, all countries | https://datahelpdesk.worldbank.org/knowledgebase/topics/125589 |
| National Weather Service | US forecasts and alerts, from api.weather.gov/points/{lat},{lon} |
https://www.weather.gov/documentation/services-web-api |
| GitHub | Repositories, users, commits (keyless with low limits) | https://docs.github.com/en/rest |
| Open Library | Books, authors, covers, by ISBN (occasionally flaky) | https://openlibrary.org/developers/api |
| USGS Earthquakes | Every recorded earthquake, live | https://earthquake.usgs.gov/fdsnws/event/1/ |
Pick one this week and fetch something from it, in the browser or with curl