🔗 Handling API Requests

APIs (Application Programming Interfaces) let you fetch data from web services, integrate with external platforms, and build connected applications. Python makes it easy to work with RESTful APIs and process JSON responses.

import urllib.request
import json

# Simple API request using built-in urllib
def get_api_data(url):
    try:
        with urllib.request.urlopen(url) as response:
            data = response.read()
            return json.loads(data)
    except Exception as e:
        print(f"Error fetching data: {e}")
        return None

# Example with a placeholder API
api_url = "https://jsonplaceholder.typicode.com/posts/1"
post_data = get_api_data(api_url)

if post_data:
    print(f"Post ID: {post_data['id']}")
    print(f"Title: {post_data['title']}")
    print(f"Body: {post_data['body'][:100]}...")

🎯 Understanding API Requests

APIs use HTTP methods to communicate, with JSON being the most common data format for responses.

Basic GET Requests

import urllib.request
import urllib.parse
import json

def fetch_user_posts(user_id):
    """Fetch posts for a specific user"""
    base_url = "https://jsonplaceholder.typicode.com/posts"
    params = {'userId': user_id}
    
    # Build URL with parameters
    url = f"{base_url}?{urllib.parse.urlencode(params)}"
    
    try:
        with urllib.request.urlopen(url) as response:
            if response.status == 200:
                data = json.loads(response.read())
                return data
            else:
                print(f"Error: HTTP {response.status}")
                return []
    except Exception as e:
        print(f"Request failed: {e}")
        return []

def fetch_weather_data():
    """Simulate weather API call"""
    # Mock weather data (replace with real API)
    mock_data = {
        "temperature": 22,
        "humidity": 65,
        "conditions": "Partly Cloudy",
        "location": "San Francisco"
    }
    return mock_data

# Test the functions
posts = fetch_user_posts(1)
print(f"Found {len(posts)} posts for user 1")
if posts:
    print(f"First post: {posts[0]['title']}")

weather = fetch_weather_data()
print(f"Weather: {weather['temperature']}°C, {weather['conditions']}")

POST Requests with Data

import urllib.request
import urllib.parse
import json

def create_post(title, body, user_id):
    """Create a new post via API"""
    url = "https://jsonplaceholder.typicode.com/posts"
    
    # Prepare data
    post_data = {
        'title': title,
        'body': body,
        'userId': user_id
    }
    
    # Convert to JSON and encode
    json_data = json.dumps(post_data)
    data = json_data.encode('utf-8')
    
    # Create request with headers
    req = urllib.request.Request(url, data=data)
    req.add_header('Content-Type', 'application/json')
    
    try:
        with urllib.request.urlopen(req) as response:
            if response.status == 201:  # Created
                result = json.loads(response.read())
                return result
            else:
                print(f"Error: HTTP {response.status}")
                return None
    except Exception as e:
        print(f"Request failed: {e}")
        return None

def send_form_data(name, email):
    """Send form data (application/x-www-form-urlencoded)"""
    url = "https://httpbin.org/post"  # Test endpoint
    
    # Prepare form data
    form_data = {
        'name': name,
        'email': email
    }
    
    # Encode as form data
    data = urllib.parse.urlencode(form_data).encode('utf-8')
    
    req = urllib.request.Request(url, data=data)
    req.add_header('Content-Type', 'application/x-www-form-urlencoded')
    
    try:
        with urllib.request.urlopen(req) as response:
            result = json.loads(response.read())
            return result.get('form', {})
    except Exception as e:
        print(f"Form submission failed: {e}")
        return {}

# Test POST request
new_post = create_post("My New Post", "This is the content of my post.", 1)
if new_post:
    print(f"Created post with ID: {new_post['id']}")

# Test form submission
form_result = send_form_data("Alice Johnson", "alice@example.com")
print(f"Form submission result: {form_result}")

📋 HTTP Status Codes Reference

CodeMeaningDescription
200OKRequest successful
201CreatedResource created successfully
400Bad RequestInvalid request format
401UnauthorizedAuthentication required
404Not FoundResource doesn't exist
500Server ErrorInternal server error

🔧 Error Handling and Retries

Robust API Request Handler

import urllib.request
import urllib.error
import json
import time

def api_request_with_retry(url, method='GET', data=None, headers=None, max_retries=3):
    """Make API request with retry logic and error handling"""
    
    if headers is None:
        headers = {}
    
    # Set default headers
    if 'User-Agent' not in headers:
        headers['User-Agent'] = 'Python API Client 1.0'
    
    for attempt in range(max_retries):
        try:
            # Create request
            req = urllib.request.Request(url, data=data, headers=headers, method=method)
            
            with urllib.request.urlopen(req, timeout=10) as response:
                status_code = response.status
                response_data = response.read()
                
                # Parse JSON if content type indicates JSON
                content_type = response.headers.get('Content-Type', '')
                if 'application/json' in content_type:
                    try:
                        parsed_data = json.loads(response_data)
                    except json.JSONDecodeError:
                        parsed_data = response_data.decode('utf-8')
                else:
                    parsed_data = response_data.decode('utf-8')
                
                return {
                    'success': True,
                    'status_code': status_code,
                    'data': parsed_data,
                    'headers': dict(response.headers)
                }
                
        except urllib.error.HTTPError as e:
            status_code = e.code
            error_msg = f"HTTP {status_code}: {e.reason}"
            
            if status_code >= 500 and attempt < max_retries - 1:
                # Retry on server errors
                print(f"Server error, retrying in {2 ** attempt} seconds...")
                time.sleep(2 ** attempt)
                continue
            else:
                return {
                    'success': False,
                    'status_code': status_code,
                    'error': error_msg
                }
                
        except urllib.error.URLError as e:
            error_msg = f"Network error: {e.reason}"
            if attempt < max_retries - 1:
                print(f"Network error, retrying in {2 ** attempt} seconds...")
                time.sleep(2 ** attempt)
                continue
            else:
                return {
                    'success': False,
                    'error': error_msg
                }
        
        except Exception as e:
            return {
                'success': False,
                'error': f"Unexpected error: {str(e)}"
            }
    
    return {
        'success': False,
        'error': f"Max retries ({max_retries}) exceeded"
    }

# Test robust API handler
def test_api_endpoints():
    """Test various API scenarios"""
    
    # Test successful request
    result = api_request_with_retry("https://jsonplaceholder.typicode.com/posts/1")
    if result['success']:
        print(f"Success: Got post with title: {result['data']['title']}")
    else:
        print(f"Failed: {result['error']}")
    
    # Test 404 error
    result = api_request_with_retry("https://jsonplaceholder.typicode.com/posts/99999")
    if result['success']:
        print("Success: Found post")
    else:
        print(f"Expected 404: {result['error']}")

test_api_endpoints()

🎯 Key Takeaways

🚀 What's Next?

Learn how to work with databases to store and query structured data efficiently.

Continue to: Work with Databases

Was this helpful?

😔Poor
🙁Fair
😊Good
😄Great
🤩Excellent