21  Chapter 11: Connected Programs

NoteChapter Summary

In this chapter, you’ll learn how to connect your programs to the internet. You’ll discover APIs (Application Programming Interfaces), make web requests, and process real-time data. This is where your programs join the global conversation!

21.1 The Concept First

Every program you’ve written so far lives in isolation. It knows only what you type in or save to a file. But the most useful programs in the world – weather apps, price trackers, news readers – all have one thing in common: they talk to other programs across the internet to get fresh, live information.

This is a fundamental shift. Your program stops being a closed box and becomes part of a network. Instead of containing all the data it needs, it asks for data from services that already have it. That’s the idea behind connected programs, and it’s how most modern software actually works.

21.2 Understanding Through Real Life

Think about how you already get information from remote sources every day: - You check the weather on your phone (your phone asks a weather service) - You look up a word’s definition (your browser asks a dictionary service) - You check your bank balance (your bank’s app asks the bank’s server) - You translate a phrase (a translation service does the work remotely)

In every case, the pattern is the same: you make a request, a remote service processes it, and you get a response back. Your programs can do exactly the same thing.

21.3 Understanding APIs: How Programs Talk

An API (Application Programming Interface) is like a restaurant menu for programs. Just as a menu tells you what dishes you can order and how much they cost, an API tells your program what data it can request and how to ask for it.

The Restaurant Analogy

Think of APIs like this: 1. Menu (API Documentation) - Lists what’s available 2. Order (Request) - You ask for specific items 3. Kitchen (Server) - Prepares your data 4. Delivery (Response) - You receive what you ordered

You never walk into the kitchen yourself. You don’t need to know how the food is prepared – you just need to know what’s on the menu and how to order. APIs work the same way: your program doesn’t need to know how a weather service collects its data, only how to ask for it and what the answer will look like.

NoteMental Model: The Request-Response Cycle

Every API interaction follows the same three steps: (1) your program sends a request to a specific URL, (2) a remote server processes that request, and (3) the server sends back a response containing data (usually in JSON format) and a status code telling you whether it worked. Once you understand this cycle, every API in the world follows the same pattern.

21.4 Discovering APIs with Your AI Partner

Before writing any code, let’s build intuition for how connected programs work.

Exploration 1: APIs in Your Daily Life

Ask your AI:

Give me 5 examples of APIs that regular phone apps use behind the scenes, explained without code. What requests do they send, and what responses come back?

You’ll start to see that APIs are everywhere, quietly powering the apps you already use.

Exploration 2: The Request-Response Pattern

Try this prompt:

Using the restaurant analogy, walk me through what happens step by step when a weather app on my phone shows today's forecast. What is the "order"? What is the "menu"? What gets "delivered"?

This will solidify the mental model before you see any code.

Exploration 3: What Can Go Wrong

Ask:

What are the most common reasons an API request might fail? Explain using the restaurant analogy -- what could go wrong between ordering and getting your food?

Understanding failure modes early makes error handling feel natural later.

21.5 From Concept to Code

Now that you understand the pattern, let’s see it in Python.

Your First API Call

Let’s start with something fun – getting a random joke:

import requests

# Make a request to the joke API
response = requests.get("https://official-joke-api.appspot.com/random_joke")

# Convert the response from JSON to a Python dictionary
# JSON is a standard text format for sending data between programs
joke_data = response.json()

# Display the joke
print(joke_data['setup'])
print(joke_data['punchline'])
ImportantInstalling Libraries

This chapter uses the requests library. When AI suggests libraries, always ask: “How do I install this library? What does it do that Python can’t do by itself?”

21.6 How Web Requests Work

When your program “talks” to the internet, it follows a simple conversation pattern:

  1. Request: “Hey weather service, what’s the temperature in Boston?”
  2. Response: “It’s 72°F, partly cloudy”

Every response comes with a status code – a number that tells you whether things went well. Think of it like a delivery receipt: 200 means “here’s your data,” 401 means “you’re not authorized,” 404 means “we don’t have that,” and 500 means “something broke on our end.” You don’t need to memorize these; you just need to know that 200 means success and anything else means something went wrong.

The Request-Response Cycle

def get_weather(city):
    """Get current weather for a city"""
    # 1. Build the request URL
    base_url = "http://api.weatherapi.com/v1/current.json"
    params = {
        'key': 'your_api_key_here',
        'q': city
    }
    
    # 2. Send the request
    response = requests.get(base_url, params=params)
    
    # 3. Check if it worked
    if response.status_code == 200:
        # 4. Extract the data
        data = response.json()
        return data['current']['temp_f']
    else:
        return None
TipAI Partnership Pattern

When working with new APIs, ask AI: “I want to use the [service] API. Show me the simplest possible example that gets one piece of data.”

21.7 Working with JSON Responses

Most APIs return data in JSON (JavaScript Object Notation) format. JSON looks almost identical to Python dictionaries and lists, which is why response.json() converts the response directly into Python data structures you already know how to use. If you’ve worked with dictionaries, you already know how to work with JSON.

Exploring API Responses

When you get data from an API, explore it first:

def explore_api_response(url):
    """Explore what an API returns"""
    response = requests.get(url)
    data = response.json()
    
    # Print the structure
    print("Response contains:")
    for key in data.keys():
        print(f"  - {key}: {type(data[key])}")
    
    return data

# Try it with a quote API
quote_data = explore_api_response("https://api.quotable.io/random")
WarningExpression Explorer: Dictionary Access

When you see data['current']['temp_f'], you’re accessing nested dictionaries. Ask AI: “Show me how to safely access nested dictionary values when keys might not exist.”

21.8 API Keys: Your Program’s ID Card

Some APIs are open to everyone (like the joke API above), but many require an API key – a unique string that identifies your program. Think of it like a library card: the library is free to use, but they need to know who you are so they can manage how many books you borrow. API keys let services track usage and prevent abuse.

Getting and Using API Keys

  1. Sign up at the API provider’s website
  2. Get your key from your account dashboard
  3. Keep it secret - never put keys in your code!
  4. Use it in requests as shown below
def get_news_headlines():
    """Get top news headlines"""
    # DON'T DO THIS - key exposed in code!
    # api_key = "abc123mysecretkey"
    
    # DO THIS - read from environment or file
    with open('api_keys.txt', 'r') as f:
        api_key = f.readline().strip()
    
    url = "https://newsapi.org/v2/top-headlines"
    params = {
        'apiKey': api_key,
        'country': 'us',
        'pageSize': 5
    }
    
    response = requests.get(url, params=params)
    return response.json()

21.9 Building a Weather Dashboard

Now that you understand the building blocks – requests, responses, JSON, and API keys – let’s combine them into something useful. This weather comparison tool applies every concept from the previous sections:

def create_weather_dashboard(cities):
    """Compare weather across multiple cities"""
    api_key = load_api_key('weather_key.txt')
    weather_data = []
    
    for city in cities:
        url = f"http://api.weatherapi.com/v1/current.json"
        params = {'key': api_key, 'q': city}
        
        response = requests.get(url, params=params)
        if response.status_code == 200:
            data = response.json()
            weather_data.append({
                'city': city,
                'temp': data['current']['temp_f'],
                'condition': data['current']['condition']['text'],
                'humidity': data['current']['humidity']
            })
    
    # Display the dashboard
    print("\n🌤️  WEATHER DASHBOARD  🌤️")
    print("=" * 40)
    for weather in weather_data:
        print(f"\n{weather['city']}:")
        print(f"  Temperature: {weather['temp']}°F")
        print(f"  Condition: {weather['condition']}")
        print(f"  Humidity: {weather['humidity']}%")

21.10 Handling API Errors Gracefully

Unlike files on your computer, APIs depend on the internet, remote servers, and services you don’t control. Things will go wrong, and your program needs to handle failures without crashing:

Common API Problems

  1. No Internet Connection - Can’t reach the server
  2. Invalid API Key - Authentication failed
  3. Rate Limiting - Too many requests
  4. Server Errors - API is down
  5. Invalid Data - Unexpected response format

Error Handling Strategies

def safe_api_call(url, params=None):
    """Make an API call with error handling"""
    try:
        response = requests.get(url, params=params, timeout=5)
        
        # Check status code
        if response.status_code == 200:
            return response.json()
        elif response.status_code == 401:
            print("Error: Invalid API key")
        elif response.status_code == 429:
            print("Error: Too many requests - slow down!")
        else:
            print(f"Error: {response.status_code}")
            
    except requests.ConnectionError:
        print("Error: No internet connection")
    except requests.Timeout:
        print("Error: Request timed out")
    except Exception as e:
        print(f"Unexpected error: {e}")
    
    return None
ImportantRate Limiting Reality

Most free APIs limit how many requests you can make. Always check the documentation and add delays between requests if needed:

import time
time.sleep(1)  # Wait 1 second between requests

21.11 Creating a Currency Converter

With error handling in place, let’s build something practical – a live currency converter. Notice how this example uses a free API that doesn’t require a key, making it a great one to try yourself:

def get_exchange_rate(from_currency, to_currency):
    """Get current exchange rate"""
    url = "https://api.exchangerate-api.com/v4/latest/" + from_currency
    
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        rate = data['rates'].get(to_currency)
        return rate
    return None

def convert_currency(amount, from_currency, to_currency):
    """Convert between currencies"""
    rate = get_exchange_rate(from_currency, to_currency)
    
    if rate:
        converted = amount * rate
        print(f"{amount} {from_currency} = {converted:.2f} {to_currency}")
        print(f"Exchange rate: 1 {from_currency} = {rate} {to_currency}")
    else:
        print("Could not get exchange rate")

# Use it
convert_currency(100, "USD", "EUR")

21.12 Working with Different API Types

You’ll encounter different styles of APIs as you explore. For now, REST APIs are all you need – they’re the most common and the simplest to understand. The others are worth knowing about so you recognize the terms when AI mentions them.

REST APIs (Most Common)

  • Request specific URLs
  • Get JSON responses
  • Like ordering from a menu

Real-time APIs

  • Continuous data streams
  • Like a news ticker
  • More complex to handle

GraphQL APIs

  • Request exactly what you need
  • Like a customizable menu
  • Growing in popularity

21.13 Building a News Aggregator

Let’s create a program that collects news from multiple sources:

def get_tech_news():
    """Get latest technology news"""
    api_key = load_api_key('news_key.txt')
    
    # Get news from API
    url = "https://newsapi.org/v2/top-headlines"
    params = {
        'apiKey': api_key,
        'category': 'technology',
        'pageSize': 10
    }
    
    response = requests.get(url, params=params)
    if response.status_code == 200:
        articles = response.json()['articles']
        
        # Display headlines
        print("\n📰 LATEST TECH NEWS")
        print("=" * 50)
        for i, article in enumerate(articles, 1):
            print(f"\n{i}. {article['title']}")
            print(f"   Source: {article['source']['name']}")
            print(f"   {article['description'][:100]}...")

# Run the aggregator
get_tech_news()

21.14 API Best Practices

1. Cache Responses

Don’t request the same data repeatedly:

cache = {}

def get_cached_weather(city):
    if city not in cache:
        cache[city] = fetch_weather_from_api(city)
    return cache[city]

2. Handle Timeouts

Networks can be slow:

response = requests.get(url, timeout=5)  # 5 second timeout

3. Validate Data

APIs can return unexpected data:

def safe_get(data, *keys):
    """Safely navigate nested dictionaries"""
    for key in keys:
        if isinstance(data, dict):
            data = data.get(key)
        else:
            return None
    return data

# Use: safe_get(data, 'current', 'temp_f')

21.15 Creating Your API Toolkit

Once you’ve written a few API calls, you’ll notice you’re repeating the same steps: build a URL, send a request, check the status, parse the JSON. That repetition is a signal that it’s time to build reusable tools:

class APIClient:
    """Reusable API client"""
    
    def __init__(self, base_url, api_key=None):
        self.base_url = base_url
        self.api_key = api_key
        self.session = requests.Session()
    
    def get(self, endpoint, params=None):
        """Make a GET request"""
        url = self.base_url + endpoint
        
        if self.api_key:
            if params is None:
                params = {}
            params['api_key'] = self.api_key
        
        try:
            response = self.session.get(url, params=params)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            print(f"API Error: {e}")
            return None

# Use your toolkit
weather_client = APIClient("http://api.weatherapi.com/v1/", api_key="your_key")
data = weather_client.get("current.json", {"q": "Boston"})

21.16 Real Project: Multi-Source Dashboard

Let’s combine multiple APIs into one useful program:

def create_morning_briefing():
    """Get weather, news, and quote for the day"""
    print("\n☀️ GOOD MORNING! Here's your briefing:\n")
    
    # Weather
    weather = get_weather("New York")
    if weather:
        print(f"🌡️ Weather: {weather['temp']}°F, {weather['condition']}")
    
    # Motivational quote
    quote = get_daily_quote()
    if quote:
        print(f"\n💭 Quote of the day: \"{quote['content']}\"")
        print(f"   - {quote['author']}")
    
    # Top news
    news = get_headlines(3)
    if news:
        print("\n📰 Top Headlines:")
        for headline in news:
            print(f"  • {headline}")
    
    # Currency rates
    rates = get_currency_rates("USD", ["EUR", "GBP", "JPY"])
    if rates:
        print("\n💱 Currency Rates:")
        for currency, rate in rates.items():
            print(f"  • 1 USD = {rate} {currency}")

21.17 Common AI Complications

When you ask AI to help you make API calls, it tends to build a spaceship when you need a bicycle. Here’s a typical example of what AI produces when you ask “How do I get data from an API?”:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class ResilientAPIClient:
    """Production-grade API client with retry logic,
    session management, exponential backoff, and
    custom exception hierarchy."""

    def __init__(self, base_url, api_key, max_retries=3,
                 backoff_factor=0.5, timeout=(3.05, 27)):
        self.session = requests.Session()
        retry_strategy = Retry(
            total=max_retries,
            backoff_factor=backoff_factor,
            status_forcelist=[429, 500, 502, 503, 504]
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.session.headers.update({
            'Authorization': f'Bearer {api_key}',
            'Accept': 'application/json'
        })
        self.base_url = base_url
        self.timeout = timeout

    def get(self, endpoint, params=None):
        try:
            response = self.session.get(
                f"{self.base_url}/{endpoint}",
                params=params, timeout=self.timeout
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as e:
            raise APIError(f"HTTP error: {e}") from e
        except requests.exceptions.ConnectionError as e:
            raise NetworkError(f"Connection failed: {e}") from e

That’s 35+ lines before you’ve even asked for any data! For learning and most small projects, all you need is this:

import requests

response = requests.get("https://api.example.com/data")
if response.status_code == 200:
    data = response.json()
    print(data)
else:
    print(f"Something went wrong: {response.status_code}")
TipSimplification Strategy

When AI gives you a complex API client with sessions, retry adapters, custom exceptions, and connection pooling, ask: “Can you show me the simplest version that makes one successful request and handles basic errors with try/except?” You can always add complexity later when you understand why you need it.

Watch for these specific over-engineering patterns:

  • Session objects when you’re making a single request
  • Retry logic with exponential backoff when you’re just learning how APIs work
  • Custom exception hierarchies when a simple try/except tells you everything you need to know
  • Connection pooling and adapter mounting which are performance optimizations for production servers, not learning exercises

Start simple. Add complexity only when a real problem demands it.

21.18 Common Pitfalls and Solutions

Pitfall 1: Hardcoding API Keys

Problem: Keys in code are security risks Solution: Use environment variables or secure files

Pitfall 2: No Error Handling

Problem: Program crashes when API fails Solution: Always use try/except blocks

Pitfall 3: Ignoring Rate Limits

Problem: API blocks your requests Solution: Add delays and check documentation

Pitfall 4: Not Checking Response Status

Problem: Assuming all requests succeed Solution: Always check status_code

21.19 Practice Projects

Project 1: Weather Tracker

  • Track weather for multiple cities
  • Store historical data
  • Find weather patterns
  • Alert for extreme conditions

Project 2: Stock Portfolio Monitor

  • Track stock prices
  • Calculate gains/losses
  • Set price alerts
  • Generate reports

Project 3: News Sentiment Analyzer

  • Collect news articles
  • analyse headlines
  • Track topics over time
  • Create summaries

21.20 Looking Ahead

Next chapter, you’ll learn to create interactive programs with graphical interfaces. Instead of just printing to the console, your programs will have buttons, windows, and visual elements that users can click and interact with!

21.21 Chapter Summary

You’ve learned to: - Understand how APIs work - Make web requests from Python - Handle JSON responses - Manage API keys securely - Build programs that use live data - Handle errors gracefully

Your programs are no longer isolated - they’re connected to the world’s information!

21.22 Reflection Prompts

  1. API Design: What makes a good API vs a frustrating one?
  2. Error Planning: What could go wrong with internet-connected programs?
  3. Privacy Concerns: What data should programs be careful about?
  4. Future APIs: What APIs would you like to exist?

Remember: The internet is your program’s library. APIs are the librarians that help you find exactly what you need!