Computing Foundations › APIs and the Web › Day 22
Hands-on lab — Day 22: What an API Is and Why Everything Has One
- ← Back to the Day 22 lesson
- Open the hands-on files on GitHub — clone or download them from the public labs repository
- Local path in your clone:
labs/sections/computing-foundations/day-022-what-an-api-is-and-why/
Commands
Setup
cd labs/sections/computing-foundations/day-022-what-an-api-is-and-why Run
bash examples/call_apis.sh
bash starter/call_apis.sh Test
bash tests/run_tests.sh File tree
examples/call_apis.sh expected-output/sample-run.txt metadata.yml README.md requirements/README.md security.md starter/api-worksheet.md starter/call_apis.sh tests/run_tests.sh troubleshooting.md
Lab README
Day 022 lab — Call Your First APIs
Lesson
- Lesson title: What an API Is and Why Everything Has One
- Day number: 22 of 365
- Lesson article: https://ai-roadmap-365.github.io/day-022-what-an-api-is-and-why
- Lab files: everything you need is in this directory — follow “How to run” below.
- Browse the course locally: from the repository root, this lab also appears in the course website at
/labs/day-022-what-an-api-is-and-whywhen the site is running.
Purpose
Day 22's lesson explained what an API is: a contract two programs agree on so one can ask the other to do work. This lab makes it real. You call three free, public, no-key web APIs from the terminal, read their JSON responses, and see for yourself that an API call is just an HTTP request to an endpoint that comes back as structured data — the same move that later reaches a hosted model.
Learning objectives
- Call a web API with
curland read its JSON response. - Fetch two different resources (
/todos/1,/users/1) from one API and see how the endpoint selects what comes back. - Send a query parameter and watch httpbin echo it back in an
argsobject. - Read live data (the space station's current position) and tell it apart from fixed sample data.
- Pretty-print JSON with
python3 -m json.tool(nojqrequired).
Prerequisites
- The Day 22 lesson, and days 15–21 (the networking category).
- Day 21 comfort with
curl. - A terminal with
curlandpython3, and an internet connection.
Supported operating systems
- macOS and Linux — fully supported (
curlandpython3preinstalled). - Windows —
curlships with Windows 10+ andpython3is a free install; or run everything unmodified inside WSL.
Hardware requirements
Any computer with an internet connection. The lab only makes ordinary web requests and reads the responses.
Required software
curl— to send the requests (preinstalled on macOS/Linux).python3— only to pretty-print JSON viapython3 -m json.tool(preinstalled on macOS/Linux).jqis optional, never required.
See requirements/README.md.
Free and open-source options
Everything here is free: curl and Python are open source, and all three test
APIs — JSONPlaceholder, httpbin, and Open Notify — are free public endpoints
with no account and no key.
Installation
None. Change into this directory:
cd labs/sections/computing-foundations/day-022-what-an-api-is-and-why
File structure
day-022-what-an-api-is-and-why/
├── README.md
├── metadata.yml
├── starter/
│ ├── call_apis.sh ← YOUR working file (4 exercises)
│ └── api-worksheet.md ← record the values you got back
├── examples/
│ └── call_apis.sh ← completed reference implementation
├── tests/
│ └── run_tests.sh
├── expected-output/
│ └── sample-run.txt ← a real captured run
├── requirements/README.md
├── troubleshooting.md
└── security.md
How to run
## 1. See the finished tool first (needs network)
bash examples/call_apis.sh
## 2. Complete the four exercises in the starter, then run it
bash starter/call_apis.sh
## 3. Check your work
bash tests/run_tests.sh
What the commands do
bash examples/call_apis.sh— makes four API calls and explains each: JSONPlaceholder/todos/1and/users/1, httpbin/getwith a query parameter, and Open Notify's liveiss-now.json. It pretty-prints each JSON response withpython3 -m json.tool, and if a service returns something that is not JSON it prints the raw body instead of crashing. It degrades gracefully offline (clear message, exit 0).bash starter/call_apis.sh— the same skeleton with four numbered exercises; each names the exactcurlcommand to run and what to record.bash tests/run_tests.sh— always verifies structure (files present, both scripts parse, the example calls all three APIs and does not requirejq, the starter names four exercises,python3is available), then — when online — checks that each API returns parseable JSON with its documented key (title,email,args,iss_position). Offline or during a transient outage, the affected live checks are skipped (never failed). Exits 0 when no structure check fails.
Expected output
See expected-output/sample-run.txt for a
real captured run. The JSONPlaceholder data is fixed sample data, but the ISS
latitude/longitude and timestamp are live, so yours will differ.
Validation steps
bash examples/call_apis.shprints all four sections without errors.- You can point at each response and identify it as JSON with named fields.
- You found your
courseanddayparameters echoed by httpbin'sargs. - You ran the ISS call twice and saw the coordinates change.
- The tests pass.
Tests
bash tests/run_tests.sh
Expected final line online: 17 checks, 0 failure(s), 0 skip(s). (a
transiently down service turns its check into a skip, never a failure; offline,
all four live checks skip). Exit 0 on success.
Cleanup
Nothing to clean up — the scripts only make web requests and write no files.
Reset your edited starter with git checkout -- starter/call_apis.sh.
Troubleshooting
See troubleshooting.md — no network, unindented JSON,
httpbin 503s, the ISS http-vs-https gotcha, and shell-quoting URLs with &.
Security notes
See security.md. Short version: only public, no-auth endpoints; never send secrets to an echo service; an API key is a password.
Extension exercises
- Filter with a parameter:
curl -s "https://jsonplaceholder.typicode.com/todos?userId=1" | python3 -m json.toolreturns only user 1's to-dos. Count how many came back. - Add response headers with
curl -i https://jsonplaceholder.typicode.com/todos/1and find theContent-Type: application/jsonheader. - Time a call with
curl -o /dev/null -s -w 'status:%{http_code} total:%{time_total}s\n' https://jsonplaceholder.typicode.com/todos/1and compare it with how long a local calculation takes.
Navigation
- Previous day: Day 21 — Inspecting Traffic with curl and Developer Tools.
- Next day: Day 23 — REST Fundamentals: Resources and Verbs.
Expected output
sample-run.txt
Day 022 — Call Your First APIs
Three free public APIs, no key required.
=== 1. JSONPlaceholder — GET /todos/1 (a single to-do item) ===
$ curl https://jsonplaceholder.typicode.com/todos/1
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Explanation: a GET to the /todos/1 endpoint returns one to-do as JSON.
The 'title' field is the to-do's text; 'completed' is true/false.
=== 2. JSONPlaceholder — GET /users/1 (a user, with nested objects) ===
$ curl https://jsonplaceholder.typicode.com/users/1
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
Explanation: same API, different endpoint -> different resource.
Note the nested 'address' and 'company' objects — JSON can nest.
=== 3. httpbin — GET /get?course=365-days-of-ai&day=22 (echoes your request) ===
$ curl "https://httpbin.org/get?course=365-days-of-ai&day=22"
{
"args": {
"course": "365-days-of-ai",
"day": "22"
},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/8.7.1",
"X-Amzn-Trace-Id": "Root=1-6a538d93-395badf86c29f8ab41f84473"
},
"origin": "122.170.198.151",
"url": "https://httpbin.org/get?course=365-days-of-ai&day=22"
}
Explanation: the 'args' object shows the query parameters the server saw
(course, day); the 'headers' object shows the headers curl sent.
=== 4. Open Notify — GET iss-now.json (LIVE space-station position) ===
$ curl http://api.open-notify.org/iss-now.json
{
"timestamp": 1783860628,
"iss_position": {
"latitude": "36.1156",
"longitude": "86.5516"
},
"message": "success"
}
Explanation: 'iss_position' holds the latitude/longitude RIGHT NOW.
Run again in a minute and the numbers change — a real satellite is moving.
Note: this endpoint is served over plain http://, not https://.
Done. Each section above was one API call: a request to an endpoint and
a JSON response you read fields out of.
Source files
examples/call_apis.sh (5076 bytes)
#!/usr/bin/env bash
# Day 022 lab — Call Your First APIs (completed reference implementation).
#
# Calls three free, public, no-key web APIs and explains each response:
# * JSONPlaceholder (https://jsonplaceholder.typicode.com) — fake REST data
# * httpbin (https://httpbin.org) — echoes your request
# * Open Notify (http://api.open-notify.org) — live ISS position
#
# JSON is pretty-printed with `python3 -m json.tool`, which is always available
# on macOS and Linux — jq is NOT required. If a call returns something that is
# not JSON (an outage page, say), we print the raw body instead of crashing.
#
# Run from the lab directory: bash examples/call_apis.sh
#
# Requires network access. Offline (or if a service is transiently down), each
# section degrades gracefully with a clear message and the script still exits 0.
set -u
JSONPH="https://jsonplaceholder.typicode.com"
HTTPBIN="https://httpbin.org"
ISS="http://api.open-notify.org/iss-now.json"
rule() { printf '\n=== %s ===\n' "$1"; }
# Pretty-print stdin as JSON, or fall back to printing it raw if it is not JSON.
pretty() {
local body; body="$(cat)"
if printf '%s' "${body}" | python3 -m json.tool 2>/dev/null; then
return 0
fi
echo "(response was not valid JSON — showing raw body / message):"
printf '%s\n' "${body}"
}
# Return 0 if we appear to have network access, else 1.
have_network() {
curl -s -o /dev/null --max-time 12 "${JSONPH}/todos/1" 2>/dev/null
}
# GET a URL with sane timeouts/retries; prints the body, or empty on failure.
fetch() {
curl -s --max-time 25 --retry 3 --retry-delay 2 "$1" 2>/dev/null
}
# ---------------------------------------------------------------------------
# 1. JSONPlaceholder — a single to-do resource at /todos/1.
# ---------------------------------------------------------------------------
show_todo() {
rule "1. JSONPlaceholder — GET /todos/1 (a single to-do item)"
echo "\$ curl ${JSONPH}/todos/1"
local body; body="$(fetch "${JSONPH}/todos/1")"
if [ -z "${body}" ]; then echo "(could not reach ${JSONPH})"; return; fi
printf '%s' "${body}" | pretty
echo "Explanation: a GET to the /todos/1 endpoint returns one to-do as JSON."
echo "The 'title' field is the to-do's text; 'completed' is true/false."
}
# ---------------------------------------------------------------------------
# 2. JSONPlaceholder — a single user resource at /users/1 (nested JSON).
# ---------------------------------------------------------------------------
show_user() {
rule "2. JSONPlaceholder — GET /users/1 (a user, with nested objects)"
echo "\$ curl ${JSONPH}/users/1"
local body; body="$(fetch "${JSONPH}/users/1")"
if [ -z "${body}" ]; then echo "(could not reach ${JSONPH})"; return; fi
printf '%s' "${body}" | pretty
echo "Explanation: same API, different endpoint -> different resource."
echo "Note the nested 'address' and 'company' objects — JSON can nest."
}
# ---------------------------------------------------------------------------
# 3. httpbin — /get echoes back the request it received.
# ---------------------------------------------------------------------------
show_httpbin() {
rule "3. httpbin — GET /get?course=365-days-of-ai&day=22 (echoes your request)"
echo "\$ curl \"${HTTPBIN}/get?course=365-days-of-ai&day=22\""
local body; body="$(fetch "${HTTPBIN}/get?course=365-days-of-ai&day=22")"
if [ -z "${body}" ]; then
echo "(could not reach ${HTTPBIN} — it is a shared free service and is"
echo " sometimes overloaded; wait a minute and re-run. This is not your bug.)"
return
fi
printf '%s' "${body}" | pretty
echo "Explanation: the 'args' object shows the query parameters the server saw"
echo "(course, day); the 'headers' object shows the headers curl sent."
}
# ---------------------------------------------------------------------------
# 4. Open Notify — the current position of the International Space Station.
# ---------------------------------------------------------------------------
show_iss() {
rule "4. Open Notify — GET iss-now.json (LIVE space-station position)"
echo "\$ curl ${ISS}"
local body; body="$(fetch "${ISS}")"
if [ -z "${body}" ]; then echo "(could not reach ${ISS})"; return; fi
printf '%s' "${body}" | pretty
echo "Explanation: 'iss_position' holds the latitude/longitude RIGHT NOW."
echo "Run again in a minute and the numbers change — a real satellite is moving."
echo "Note: this endpoint is served over plain http://, not https://."
}
main() {
echo "Day 022 — Call Your First APIs"
echo "Three free public APIs, no key required."
if ! have_network; then
echo
echo "OFFLINE: could not reach ${JSONPH}. Every call below needs network"
echo "access. Connect to the internet and re-run: bash examples/call_apis.sh"
exit 0
fi
show_todo
show_user
show_httpbin
show_iss
echo
echo "Done. Each section above was one API call: a request to an endpoint and"
echo "a JSON response you read fields out of."
}
main "$@"
metadata.yml (576 bytes)
lesson_id: D022
day: 22
kind: api-example
languages: [bash]
setup_commands:
- cd labs/sections/computing-foundations/day-022-what-an-api-is-and-why
run_commands:
- bash examples/call_apis.sh
- bash starter/call_apis.sh
test_commands:
- bash tests/run_tests.sh
cleanup_commands:
- 'git checkout -- starter/call_apis.sh # optional: reset your work'
requires_network: true
requires_api_key: false
estimated_minutes: 30
last_executed: '2026-07-12'
executed_on: 'macOS (curl 8.7.1, Python 3.14.0), online, bash tests/run_tests.sh → 17 checks, 0 failure(s), 0 skip(s)'
requirements/README.md (822 bytes)
# Dependencies — Day 022 lab
**`curl` and `python3` only** — both preinstalled on macOS and mainstream
Linux (and available on Windows 10+ / WSL). No package installs, no accounts,
no API keys.
- `curl` sends the HTTP requests.
- `python3 -m json.tool` pretty-prints the JSON responses. It is part of the
Python standard library, so nothing extra is needed.
- `jq` is **optional**. Some people like it for JSON, but this lab deliberately
does **not** require it, so the scripts run on a stock machine.
The lab reaches three free public APIs — `jsonplaceholder.typicode.com`,
`httpbin.org`, and `api.open-notify.org` — so it needs network access to show
live output. With no network it degrades gracefully: the scripts print a clear
message and the tests skip (never fail) the live checks. No sudo required.
starter/api-worksheet.md (1416 bytes)
# API worksheet — Day 022
Call the three public APIs (from the examples or your completed starter) and
record what YOU got back. Some values are fixed; the ISS position is live, so
yours will differ from anyone else's — that is the point.
## 1. JSONPlaceholder — a to-do item
- Command: `curl -s https://jsonplaceholder.typicode.com/todos/1 | python3 -m json.tool`
- The `title` of to-do #1: `____________________________________`
- Is it `completed`? (true / false): `______`
## 2. JSONPlaceholder — a user
- Command: `curl -s https://jsonplaceholder.typicode.com/users/1 | python3 -m json.tool`
- Pick ONE field and record it:
- Field name: `____________` Value: `____________________________`
## 3. httpbin — echo your request
- Command: `curl -s "https://httpbin.org/get?course=365-days-of-ai&day=22" | python3 -m json.tool`
- What appeared inside the `"args"` object? `____________________________`
## 4. Open Notify — live ISS position
- Command: `curl -s http://api.open-notify.org/iss-now.json | python3 -m json.tool`
- Latitude right now: `____________` Longitude right now: `____________`
- Roughly what time did you run it? `____________`
## 5. One short paragraph
Using the restaurant analogy, name the menu, the order, and the plate that came
back for one of these calls. Then say which of the three responses was **live
data** and how you could tell.
> _your answer here_
starter/call_apis.sh (2020 bytes)
#!/usr/bin/env bash
# Day 022 starter — Call Your First APIs.
# Complete the four exercises below. Each names the exact curl command to run.
# Run from the lab directory: bash starter/call_apis.sh
# The completed reference is in ../examples/call_apis.sh — try it yourself first.
set -u
JSONPH="https://jsonplaceholder.typicode.com"
HTTPBIN="https://httpbin.org"
ISS="http://api.open-notify.org/iss-now.json"
rule() { printf '\n=== %s ===\n' "$1"; }
echo "Day 022 starter — Call Your First APIs"
echo "Tip: pipe any command through '| python3 -m json.tool' to pretty-print JSON."
# Exercise 1: fetch a single to-do from JSONPlaceholder.
# Replace the echo with:
# curl -s "$JSONPH/todos/1" | python3 -m json.tool
rule "1. JSONPlaceholder — GET /todos/1"
echo "EXERCISE — your turn: GET $JSONPH/todos/1 and pretty-print the JSON."
echo "Record the 'title' field on the worksheet."
# Exercise 2: fetch a single user from JSONPlaceholder.
# Replace with:
# curl -s "$JSONPH/users/1" | python3 -m json.tool
rule "2. JSONPlaceholder — GET /users/1"
echo "EXERCISE — your turn: GET $JSONPH/users/1 and pretty-print the JSON."
echo "Record ONE field of your choice (e.g. 'name' or 'email') on the worksheet."
# Exercise 3: echo your request with httpbin, including a query parameter.
# Replace with:
# curl -s "$HTTPBIN/get?course=365-days-of-ai&day=22" | python3 -m json.tool
rule "3. httpbin — GET /get with a query parameter"
echo "EXERCISE — your turn: GET $HTTPBIN/get?course=365-days-of-ai&day=22"
echo "Find your 'course' and 'day' in the echoed 'args' object."
# Exercise 4: read the LIVE position of the space station from Open Notify.
# Replace with:
# curl -s "$ISS" | python3 -m json.tool
rule "4. Open Notify — GET iss-now.json (live ISS position)"
echo "EXERCISE — your turn: GET $ISS (note: plain http://, not https://)"
echo "Record the current latitude and longitude on the worksheet."
echo
echo "When all four are done, compare your output with ../examples/call_apis.sh"
tests/run_tests.sh (5555 bytes)
#!/usr/bin/env bash
# Tests for the Day 022 lab. Run from the lab directory:
# bash tests/run_tests.sh
#
# Two kinds of checks:
# * Structure checks always run and must pass (files present, the example
# script calls each API, the starter names its four exercises, both scripts
# parse, python3 is available for pretty-printing).
# * Network checks run only when the internet is reachable. Offline, they are
# SKIPPED with a message. When online but a public service is transiently
# unavailable, the affected check is also SKIPPED rather than failed — an
# external outage is not the learner's bug.
#
# The script exits 0 when no structure check failed, whether online or offline.
set -u
lab_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
JSONPH="https://jsonplaceholder.typicode.com"
HTTPBIN="https://httpbin.org"
ISS="http://api.open-notify.org/iss-now.json"
failures=0
checks=0
skips=0
pass() { checks=$((checks + 1)); echo " ok: $1"; }
fail() { checks=$((checks + 1)); failures=$((failures + 1)); echo " FAIL: $1"; }
skip() { skips=$((skips + 1)); echo " skip: $1"; }
# Fetch a URL; echo the body on success, nothing on failure.
fetch() { curl -s --max-time 25 --retry 3 --retry-delay 2 "$1" 2>/dev/null; }
# True if the body parses as JSON.
is_json() { printf '%s' "$1" | python3 -m json.tool >/dev/null 2>&1; }
# True if the JSON body contains a top-level (or nested) key name.
has_key() { printf '%s' "$1" | python3 -c "import sys,json; d=json.dumps(json.load(sys.stdin)); sys.exit(0 if '\"$2\"' in d else 1)" 2>/dev/null; }
echo "== Structure checks =="
example="${lab_dir}/examples/call_apis.sh"
starter="${lab_dir}/starter/call_apis.sh"
[ -f "${example}" ] && pass "example script exists" || fail "example script missing"
[ -f "${starter}" ] && pass "starter script exists" || fail "starter script missing"
[ -f "${lab_dir}/starter/api-worksheet.md" ] && pass "worksheet exists" || fail "worksheet missing"
# python3 must be available (used to pretty-print without requiring jq).
if command -v python3 >/dev/null 2>&1; then pass "python3 available for JSON pretty-printing"; else fail "python3 not found"; fi
# The example must call each of the three APIs and NOT hard-require jq.
for needle in "jsonplaceholder.typicode.com" "httpbin.org" "api.open-notify.org" "python3 -m json.tool"; do
if grep -q -- "${needle}" "${example}"; then pass "example uses '${needle}'"; else fail "example missing '${needle}'"; fi
done
if grep -qE '\| *jq' "${example}"; then fail "example should not pipe to jq (use python3 -m json.tool)"; else pass "example does not require jq (uses python3)"; fi
# The starter must name its four numbered exercises.
ex_count="$(grep -cE 'Exercise [1-4]' "${starter}" 2>/dev/null || true)"
if [ "${ex_count:-0}" -ge 4 ]; then pass "starter has 4 numbered exercises"; else fail "starter has 4 numbered exercises (found ${ex_count:-0})"; fi
# Both scripts must be valid bash.
if bash -n "${example}" 2>/dev/null; then pass "example has valid bash syntax"; else fail "example has a syntax error"; fi
if bash -n "${starter}" 2>/dev/null; then pass "starter has valid bash syntax"; else fail "starter has a syntax error"; fi
echo
echo "== Network checks =="
if ! curl -s -o /dev/null --max-time 12 "${JSONPH}/todos/1" 2>/dev/null; then
skip "no network access — skipping all live-API checks (expected offline)"
else
pass "network reachable (JSONPlaceholder responded)"
# A network check only FAILS when a service returns valid JSON that is
# missing the documented key (a real contract break). If the service is
# unreachable or returns a non-JSON outage page, that is a transient outage,
# not the learner's bug, so we SKIP.
# Check 1: JSONPlaceholder /todos/1 returns JSON with the 'title' key.
body="$(fetch "${JSONPH}/todos/1")"
if [ -z "${body}" ] || ! is_json "${body}"; then skip "todos/1 — JSONPlaceholder transiently unavailable; retry later"
elif has_key "${body}" "title"; then pass "GET /todos/1 returned JSON with a 'title' key"
else fail "GET /todos/1 returned JSON but without the documented 'title' key"; fi
# Check 2: JSONPlaceholder /users/1 returns JSON with the 'email' key.
body="$(fetch "${JSONPH}/users/1")"
if [ -z "${body}" ] || ! is_json "${body}"; then skip "users/1 — JSONPlaceholder transiently unavailable; retry later"
elif has_key "${body}" "email"; then pass "GET /users/1 returned JSON with an 'email' key"
else fail "GET /users/1 returned JSON but without the documented 'email' key"; fi
# Check 3: httpbin /get echoes the query parameters in an 'args' object.
body="$(fetch "${HTTPBIN}/get?course=365-days-of-ai&day=22")"
if [ -z "${body}" ] || ! is_json "${body}"; then skip "httpbin /get — shared service transiently unavailable; retry later"
elif has_key "${body}" "args" && printf '%s' "${body}" | grep -q "365-days-of-ai"; then
pass "GET /get echoed the query parameters in 'args'"
else fail "GET /get returned JSON but did not echo the query parameters in 'args'"; fi
# Check 4: Open Notify ISS returns JSON with an 'iss_position' key.
body="$(fetch "${ISS}")"
if [ -z "${body}" ] || ! is_json "${body}"; then skip "ISS — Open Notify transiently unavailable; retry later"
elif has_key "${body}" "iss_position"; then pass "GET iss-now.json returned JSON with 'iss_position'"
else fail "GET iss-now.json returned JSON but without the documented 'iss_position' key"; fi
fi
echo
echo "${checks} checks, ${failures} failure(s), ${skips} skip(s)."
[ "${failures}" -eq 0 ]
Troubleshooting
Troubleshooting — Day 022 lab
curl: (6) Could not resolve host
Your machine cannot reach the internet — DNS failed (Day 16). Check your connection and try again; every call in this lab needs network access. The scripts and tests detect no network and exit 0 with a clear message.
The JSON prints as one long unindented line
That is the raw response body. Pipe it through python3 -m json.tool to
pretty-print it with indentation:
curl -s https://jsonplaceholder.typicode.com/todos/1 | python3 -m json.tool
If Python then reports Expecting value: line 1 column 1, the API returned
something that is not JSON (usually a transient error page) — retry in a
moment, and check the URL for a typo.
httpbin.org times out or returns a 503
httpbin.org is a shared free service and is sometimes overloaded. The example
retries transient failures and falls back to a clear message; the test turns a
transient httpbin outage into a skip, never a failure. Wait a minute and
re-run — the other two APIs are unaffected. A 503 is itself a real, valid
server response worth recognizing.
The ISS command "fails" only when I add https
Open Notify's ISS endpoint is served over plain http://, not https://. Use
the URL exactly as written: http://api.open-notify.org/iss-now.json. This is
a good reminder that not every service offers TLS (Day 19).
The httpbin URL breaks in my shell
A URL containing & must be quoted, or the shell reads & as "run in the
background" and mangles the request:
curl -s "https://httpbin.org/get?course=365-days-of-ai&day=22" # quotes required
python3: command not found
On some minimal Linux setups Python 3 is installed as python. Try python -m json.tool, or install python3 from your package manager (Day 13). On macOS
python3 is present by default.
Offline
The scripts and tests detect no network and exit 0 with a message. Connect to the internet to see live output.
Security notes
Security notes — Day 022 lab
- What it does: sends ordinary GET requests to three free, public,
no-authentication test APIs (
jsonplaceholder.typicode.com,httpbin.org,api.open-notify.org) and reads the responses. No data of yours is sent beyond a normal request; no sudo; no files written outside the lab. - Public, no-auth APIs only. These three need no key on purpose. Do not add authentication where none is asked for, and never invent or paste a key.
- An API key is a secret, like a password. Real APIs (next week) require a key or token in a header. Whoever holds your key can call the API as you, on your bill. Never commit a key to a repository, paste it into a screenshot or forum, or put it in a shared URL.
- Never send secrets to an echo service.
httpbin.orgreflects your request back to you, so anything you send is visible in the response. This lab sends only harmless sample values (course,day). Do not send real tokens, passwords, or personal data to any echo service. - Every API call transmits data to someone else's computer. For these demos that is nothing sensitive, but the habit to build now is to know exactly what each request sends before you send it.
- Only call services you are allowed to. These three publish open APIs for exactly this use. Probing or hammering servers you do not own or have permission to test can violate their terms or the law; the retry limits in these scripts are deliberately gentle.