API (Application Programming Interface) allows different software applications to communicate. REST APIs use HTTP requests to GET (retrieve), POST (create), PUT (update), and DELETE data. The requests library in Python makes it easy to send HTTP requests and handle responses. APIs typically return data in JSON format, which we learned to parse on Day 20.
# Working with APIs - Requests Library
import requests
import json
import time
print("WORKING WITH APIS - REQUESTS LIBRARY")
print("=" * 60)
# Note: Install requests library first: pip install requests
# Example 1: Basic GET request
print("\n1. BASIC GET REQUEST")
print("-" * 30)
# Using a free public API for testing
print("Making request to JSONPlaceholder API...")
# In real code:
# response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
# For demonstration without internet, simulate response
class MockResponse:
def __init__(self, status_code, text):
self.status_code = status_code
self.text = text
self.headers = {'Content-Type': 'application/json'}
def json(self):
return json.loads(self.text)
def raise_for_status(self):
if self.status_code != 200:
raise Exception(f"HTTP Error: {self.status_code}")
# Simulate successful API response
mock_data = '''
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
'''
response = MockResponse(200, mock_data)
print(f"Status Code: {response.status_code}")
print(f"Content Type: {response.headers.get('Content-Type')}")
print(f"\nResponse JSON:")
print(json.dumps(response.json(), indent=2))
# Extract data
post_data = response.json()
print(f"\nExtracted Data:")
print(f"Title: {post_data['title']}")
print(f"User ID: {post_data['userId']}")
print(f"Body preview: {post_data['body'][:50]}...")
# Example 2: Different HTTP methods
print("\n\n2. HTTP METHODS")
print("-" * 30)
print("Common HTTP methods for REST APIs:")
print("GET - Retrieve data")
print("POST - Create new data")
print("PUT - Update existing data")
print("DELETE - Remove data")
print("PATCH - Partial update")
# Simulate different requests
def demonstrate_http_methods():
base_url = "https://api.example.com/users"
print("\nSimulating API calls:")
# GET - Retrieve users
print("1. GET /users - Retrieve all users")
# response = requests.get(base_url)
print(" Returns: List of users")
# GET with ID - Retrieve specific user
print("\n2. GET /users/123 - Retrieve user with ID 123")
# response = requests.get(f"{base_url}/123")
print(" Returns: User details")
# POST - Create new user
print("\n3. POST /users - Create new user")
new_user = {
"name": "John Doe",
"email": "john@example.com",
"age": 30
}
# response = requests.post(base_url, json=new_user)
print(f" Sending: {json.dumps(new_user, indent=2)}")
print(" Returns: Created user with ID")
# PUT - Update user
print("\n4. PUT /users/123 - Update user")
updated_user = {"name": "John Updated", "age": 31}
# response = requests.put(f"{base_url}/123", json=updated_user)
print(" Returns: Updated user")
# DELETE - Remove user
print("\n5. DELETE /users/123 - Delete user")
# response = requests.delete(f"{base_url}/123")
print(" Returns: Success status")
demonstrate_http_methods()
# Example 3: Working with query parameters
print("\n\n3. QUERY PARAMETERS")
print("-" * 30)
print("Adding query parameters to API requests:")
# Simulate search with parameters
search_params = {
"q": "python programming",
"page": 1,
"limit": 10,
"sort": "relevance"
}
print(f"\nSearch Parameters: {search_params}")
print("\nEquivalent URL: https://api.example.com/search?q=python+programming&page=1&limit=10&sort=relevance")
# In real code:
# response = requests.get(
# "https://api.example.com/search",
# params=search_params
# )
# Example 4: Headers and Authentication
print("\n\n4. HEADERS AND AUTHENTICATION")
print("-" * 30)
print("Common headers in API requests:")
headers = {
"User-Agent": "MyPythonApp/1.0",
"Content-Type": "application/json",
"Accept": "application/json",
}
print(f"\nHeaders: {json.dumps(headers, indent=2)}")
# Authentication examples
print("\nAuthentication methods:")
print("1. API Key:")
print(" headers = {'X-API-Key': 'your_api_key_here'}")
print(" response = requests.get(url, headers=headers)")
print("\n2. Bearer Token (JWT):")
print(" headers = {'Authorization': 'Bearer your_token_here'}")
print("\n3. Basic Auth:")
print(" from requests.auth import HTTPBasicAuth")
print(" response = requests.get(url, auth=HTTPBasicAuth('username', 'password'))")
# Example 5: Error handling with APIs
print("\n\n5. ERROR HANDLING")
print("-" * 30)
def make_api_request(url):
"""Make API request with comprehensive error handling"""
try:
# Simulate different responses
scenarios = [
(200, 'Success'),
(400, 'Bad Request'),
(401, 'Unauthorized'),
(404, 'Not Found'),
(429, 'Too Many Requests'),
(500, 'Internal Server Error')
]
import random
status_code, message = random.choice(scenarios)
print(f"Request to: {url}")
print(f"Status Code: {status_code} ({message})")
if status_code == 200:
return {"status": "success", "data": {"message": "API call successful"}}
elif status_code == 400:
return {"status": "error", "message": "Bad request - check your parameters"}
elif status_code == 401:
return {"status": "error", "message": "Unauthorized - check your API key"}
elif status_code == 404:
return {"status": "error", "message": "Resource not found"}
elif status_code == 429:
return {"status": "error", "message": "Rate limit exceeded - wait and retry"}
elif status_code == 500:
return {"status": "error", "message": "Server error - try again later"}
except requests.exceptions.Timeout:
return {"status": "error", "message": "Request timed out"}
except requests.exceptions.ConnectionError:
return {"status": "error", "message": "Connection error"}
except Exception as e:
return {"status": "error", "message": f"Unexpected error: {str(e)}"}
# Test error handling
print("Testing API error handling:")
for i in range(3):
result = make_api_request(f"https://api.example.com/data/{i}")
print(f"Result: {result['status']} - {result.get('message', 'Success')}")
print()
# Example 6: Working with real public API (simulated)
print("\n\n6. WORKING WITH REAL PUBLIC API (SIMULATED)")
print("-" * 30)
# Note: These are simulated responses to avoid actual API calls
class WeatherAPI:
"""Simulated weather API client"""
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://api.weather.example.com"
def get_current_weather(self, city):
"""Get current weather for a city"""
# Simulate API response
weather_data = {
"location": {
"name": city,
"country": "US",
"localtime": "2024-03-15 14:30"
},
"current": {
"temp_c": 22.5,
"temp_f": 72.5,
"condition": {
"text": "Sunny",
"icon": "//cdn.weatherapi.com/weather/64x64/day/113.png"
},
"wind_kph": 15.2,
"humidity": 65,
"feelslike_c": 23.1
}
}
return {
"status": "success",
"data": weather_data
}
def get_forecast(self, city, days=3):
"""Get weather forecast"""
# Simulate forecast data
forecast = {
"location": {"name": city},
"forecast": {"forecastday": []}
}
for i in range(days):
date = f"2024-03-{15 + i}"
forecast["forecast"]["forecastday"].append({
"date": date,
"day": {
"maxtemp_c": 20 + i,
"mintemp_c": 10 + i,
"condition": {"text": ["Sunny", "Partly Cloudy", "Rainy"][i % 3]}
}
})
return {
"status": "success",
"data": forecast
}
# Use the weather API
print("Using Weather API:")
weather_client = WeatherAPI("your_api_key_here")
# Get current weather
result = weather_client.get_current_weather("New York")
if result["status"] == "success":
data = result["data"]
print(f"\nCurrent Weather in {data['location']['name']}:")
print(f"Temperature: {data['current']['temp_c']}°C ({data['current']['temp_f']}°F)")
print(f"Condition: {data['current']['condition']['text']}")
print(f"Feels like: {data['current']['feelslike_c']}°C")
print(f"Humidity: {data['current']['humidity']}%")
print(f"Wind: {data['current']['wind_kph']} km/h")
# Get forecast
print("\n3-Day Forecast:")
forecast_result = weather_client.get_forecast("New York", 3)
if forecast_result["status"] == "success":
forecast = forecast_result["data"]["forecast"]["forecastday"]
for day in forecast:
print(f"{day['date']}: {day['day']['maxtemp_c']}°C / {day['day']['mintemp_c']}°C - {day['day']['condition']['text']}")
# Example 7: Building a complete API client
print("\n\n7. BUILDING A COMPLETE API CLIENT")
print("-" * 30)
class GitHubAPI:
"""GitHub API client for demonstration"""
def __init__(self, username=None, token=None):
self.base_url = "https://api.github.com"
self.username = username
self.token = token
self.headers = {
"Accept": "application/vnd.github.v3+json"
}
if token:
self.headers["Authorization"] = f"token {token}"
def get_user_info(self, username):
"""Get GitHub user information"""
# Simulate API response
user_data = {
"login": username,
"name": "John Doe",
"public_repos": 24,
"followers": 128,
"following": 56,
"created_at": "2018-03-15T10:30:00Z"
}
return user_data
def get_user_repos(self, username, sort="updated", per_page=10):
"""Get user's repositories"""
# Simulate repositories
repos = []
repo_names = ["web-scraper", "todo-app", "weather-app", "blog", "ecommerce"]
for i, name in enumerate(repo_names):
repos.append({
"name": name,
"description": f"A {name.replace('-', ' ')} project",
"stargazers_count": i * 5 + 3,
"forks_count": i * 2 + 1,
"updated_at": f"2024-03-{10 + i}T14:30:00Z",
"html_url": f"https://github.com/{username}/{name}"
})
return repos
def create_repo(self, name, description="", private=False):
"""Create a new repository"""
if not self.token:
return {"error": "Authentication required"}
# Simulate creation
new_repo = {
"name": name,
"full_name": f"{self.username}/{name}",
"description": description,
"private": private,
"html_url": f"https://github.com/{self.username}/{name}",
"created_at": "2024-03-15T14:30:00Z"
}
return new_repo
# Use GitHub API
print("Using GitHub API Client:")
github = GitHubAPI(username="johncoder", token="fake_token")
# Get user info
print("\nUser Information:")
user_info = github.get_user_info("johncoder")
print(f"Username: {user_info['login']}")
print(f"Name: {user_info.get('name', 'Not provided')}")
print(f"Public Repos: {user_info['public_repos']}")
print(f"Followers: {user_info['followers']}")
print(f"Account created: {user_info['created_at'][:10]}")
# Get repositories
print("\nTop Repositories:")
repos = github.get_user_repos("johncoder", per_page=3)
for repo in repos:
print(f"\n{repo['name']}:")
print(f" {repo['description']}")
print(f" Stars: {repo['stargazers_count']}, Forks: {repo['forks_count']}")
print(f" Updated: {repo['updated_at'][:10]}")
print(f" URL: {repo['html_url']}")
# Example 8: Rate limiting and retries
print("\n\n8. RATE LIMITING AND RETRIES")
print("-" * 30)
print("Handling API rate limits:")
class APIClientWithRetry:
"""API client with retry logic"""
def __init__(self, max_retries=3):
self.max_retries = max_retries
def make_request_with_retry(self, url):
"""Make request with exponential backoff retry"""
for attempt in range(self.max_retries):
print(f"Attempt {attempt + 1}/{self.max_retries}")
# Simulate request
# In real code: response = requests.get(url)
# Simulate different outcomes
import random
outcomes = ["success", "rate_limit", "server_error", "timeout"]
outcome = random.choice(outcomes)
if outcome == "success":
print(" ✓ Request successful")
return {"status": "success", "data": "API response data"}
elif outcome == "rate_limit":
print(f" ⚠ Rate limited. Waiting...")
wait_time = (2 ** attempt) # Exponential backoff: 1, 2, 4 seconds
# time.sleep(wait_time)
elif outcome == "server_error":
print(f" ⚠ Server error (500). Waiting...")
wait_time = (2 ** attempt)
# time.sleep(wait_time)
elif outcome == "timeout":
print(f" ⚠ Request timed out")
if attempt < self.max_retries - 1:
print(f" Retrying...")
print(" ✗ All retries failed")
return {"status": "error", "message": "Failed after retries"}
# Test retry logic
print("\nTesting retry logic:")
client = APIClientWithRetry(max_retries=3)
result = client.make_request_with_retry("https://api.example.com/data")
print(f"\nFinal result: {result['status']}")
# Example 9: Real-world project - News API client
print("\n\n9. REAL-WORLD PROJECT: NEWS API CLIENT")
print("-" * 30)
class NewsAPIClient:
"""Client for news API (simulated)"""
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://newsapi.org/v2"
def get_top_headlines(self, country="us", category=None, page_size=10):
"""Get top headlines"""
# Simulate news data
headlines = []
categories = ["technology", "business", "entertainment", "health", "science"]
for i in range(page_size):
cat = category if category else categories[i % len(categories)]
headlines.append({
"title": f"Breaking News in {cat.title()} #{i+1}",
"description": f"This is a sample news article about {cat}.",
"url": f"https://example.com/news/{i+1}",
"publishedAt": f"2024-03-{15 - i}T10:30:00Z",
"source": {"name": f"News Source {i+1}"},
"category": cat
})
return {
"status": "ok",
"totalResults": len(headlines),
"articles": headlines
}
def search_news(self, query, from_date=None, to_date=None, sort_by="relevancy"):
"""Search news articles"""
# Simulate search results
results = []
for i in range(5):
results.append({
"title": f"{query.title()} News {i+1}",
"description": f"This article discusses {query} in detail.",
"url": f"https://example.com/{query.replace(' ', '-')}-{i+1}",
"publishedAt": "2024-03-15T10:30:00Z",
"source": {"name": "Tech News"}
})
return {
"status": "ok",
"totalResults": len(results),
"articles": results
}
# Use news API
print("News API Client Demo:")
news_client = NewsAPIClient("your_news_api_key")
# Get top headlines
print("\nTop Headlines (Technology):")
top_news = news_client.get_top_headlines(country="us", category="technology", page_size=3)
for i, article in enumerate(top_news["articles"], 1):
print(f"\n{i}. {article['title']}")
print(f" Source: {article['source']['name']}")
print(f" Published: {article['publishedAt'][:10]}")
print(f" {article['description'][:60]}...")
# Search news
print("\n\nSearch Results for 'Python':")
search_results = news_client.search_news("python")
for i, article in enumerate(search_results["articles"][:2], 1):
print(f"\n{i}. {article['title']}")
print(f" {article['description']}")
print(f" Read more: {article['url']}")