Rest Api
Master building robust API clients, handling authentication, retries, pagination, webhooks, and async operations for production-grade integrations
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
- Understanding REST APIs and HTTP methods
- Building reusable API client classes
- Error handling, timeouts, and retry logic
- Authentication strategies (Bearer tokens, OAuth2, API keys)
- Async API clients with aiohttp for high concurrency
- Response validation with Pydantic
- Pagination patterns (offset, cursor, token-based)
- File uploads and downloads
- Webhook handling and signature verification
- Rate limiting and circuit breaker patterns
What Is a REST API Client?
A REST API client is code that sends HTTP requests to external services and receives structured responses. Every modern application integrates with external APIs for payments, data, AI, authentication, and more.
Concept
Real-World Analogy
What It Does
GET Request
Reading a menu
Fetches data without changing anything
POST Request
Placing an order
Creates new data on the server
PUT/PATCH Request
Modifying your order
Updates existing data
DELETE Request
Canceling your order
Removes data from the server
Headers
Your membership card
Passes authentication and metadata
Core Capabilities
- HTTP methods: GET, POST, PUT, PATCH, DELETE
- Query parameters and JSON request bodies
- Headers and authentication
- Timeouts and error handling
- Automatic retries with exponential backoff
- Rate limiting and caching
Common Use Cases
- Payment processing (Stripe, PayPal)
- Social media integrations (Twitter, LinkedIn)
- AI APIs (OpenAI, Anthropic)
- Cloud services (AWS, GCP, Azure)
- Microservice communication
- Analytics and monitoring
HTTP Methods Overview
GET - Retrieve Data
Fetch resources without side effects. Safe and idempotent.
POST - Create Resources
PUT/PATCH - Update
Modify existing resources. PUT replaces, PATCH updates partially.
DELETE - Remove
The Requests Library
requests is Python's most popular HTTP library. Simple, elegant, and widely used.
Installation
Key Features
- Clean API for all HTTP methods
- Automatic JSON encoding/decoding
- Session objects for connection pooling
- Cookie persistence
- SSL verification
Building Reusable API Clients
Instead of scattering API calls throughout your codebase, create a dedicated client class that centralizes:
- Base URL configuration
- Authentication headers
- Common request patterns
- Error handling
- Connection pooling via sessions
This pattern is used by every major Python SDK (Stripe, OpenAI, AWS, etc.).
Error Handling & Retries
Types of Failures
- Network errors - Connection failures, DNS issues
- Timeouts - Server doesn't respond in time
- 4xx errors - Client errors (bad request, unauthorized)
- 5xx errors - Server errors (should retry)
- 429 - Rate limiting (wait and retry)
Retry Strategy
- Use exponential backoff: 1s, 2s, 4s, 8s...
- Add jitter (randomness) to prevent thundering herd
- Limit max retry attempts (typically 3-5)
- Only retry safe operations (GET, not POST)
- Respect Retry-After headers
Authentication Strategies
Bearer Tokens
API Keys
OAuth 2.0
- Access tokens (short-lived)
- Refresh tokens (long-lived)
- Automatic token refresh on expiry
Async API Clients with aiohttp
For high-performance applications that need to make many concurrent API calls, async clients are essential.
Benefits of Async
- Handle thousands of concurrent requests
- Non-blocking I/O operations
- Better resource utilization
- Essential for web servers and background workers
Use Cases
- Web scraping at scale
- Batch API processing
- Real-time data aggregation
Pagination Patterns
Offset-Based Pagination
Simple but can be inefficient for large datasets.
Cursor-Based Pagination
Token-Based Pagination
Webhooks & Security
What Are Webhooks?
Webhooks are reverse APIs - the external service calls your endpoint when events occur.
- Stripe → payment succeeded
- GitHub → new commit pushed
- Twilio → message received
HMAC Signature Verification
Always verify webhook signatures to prevent fake requests:
- Compute HMAC of request body
- Compare with received signature
- Use constant-time comparison
- Reject invalid signatures immediately
Rate Limiting & Circuit Breakers
Rate Limiting
Prevent exceeding API quotas by implementing client-side rate limiting:
- Token bucket algorithm
- Sliding window counters
- Respect server rate limit headers
- Queue requests when limit reached
Circuit Breaker Pattern
Prevent cascading failures when external services are down:
- Closed - Normal operation
- Open - Service considered down, fail fast
- Half-Open - Test if service recovered
Response Validation with Pydantic
External APIs can return inconsistent data. Use Pydantic to validate and transform responses:
- Catch missing or renamed fields immediately
- Automatic type conversion
- Custom validators for business rules
- Generate JSON schemas automatically
- Prevent downstream errors from bad data
This is critical for production systems that depend on external data.
File Operations
Uploading Files
Use multipart/form-data for file uploads. Key considerations:
- Open files in binary mode
- Set appropriate timeouts
- Handle upload progress for large files
- Implement chunked uploads for reliability
Downloading Files
NEVER load entire files into memory. Use streaming:
- Use stream=True parameter
- Process chunks incrementally
- Prevents memory exhaustion
- Essential for videos, datasets, archives
Professional SDK Structure
Well-designed API clients follow this structure:
This separation makes the codebase maintainable, testable, and extensible.
Best Practices Summary
✓ Always Do
- Set timeouts on every request
- Implement retry logic with exponential backoff
- Validate responses with Pydantic
- Use session objects for connection pooling
- Log all API interactions
- Handle rate limits gracefully
- Verify webhook signatures
- Store secrets in environment variables
✗ Never Do
- Make requests without timeouts
- Retry without backoff
- Trust external data without validation
- Load large files entirely into memory
- Hardcode API keys in source code
- Ignore pagination
- Skip error handling
Key Takeaways
- REST API clients bridge your application with external services
- Always implement timeouts, retries, and proper error handling
- Use reusable client classes to centralize API logic
- Async clients enable high-performance concurrent operations
- Validate responses to prevent downstream failures
- Implement rate limiting and circuit breakers for resilience
- Verify webhook signatures to ensure security
- Follow professional SDK patterns for maintainable code
📋 Quick Reference — REST API Clients
Syntax
What it does
requests.get(url, timeout=5)
Make a GET request with timeout
requests.post(url, json=data)
POST JSON body
response.raise_for_status()
Raise exception on 4xx/5xx
requests.Session()
Reuse connection pool and headers
httpx.AsyncClient()
Async HTTP client
You can now build robust API clients with auth, retry logic, rate limiting, and async support for high-performance integrations.
Up next: DevOps Automation — use Python to automate infrastructure, deployments, and CI/CD tasks.
Practice quiz
Which HTTP method fetches data without changing anything on the server?
- GET
- POST
- DELETE
- PUT
Answer: GET. GET retrieves resources and is safe and idempotent — it has no side effects.
Which method is used to create a new resource on the server?
- GET
- POST
- PATCH
- DELETE
Answer: POST. POST submits data to create new resources (like placing an order).
What is the difference between PUT and PATCH?
- PUT replaces the resource; PATCH updates it partially
- PUT deletes; PATCH creates
- They are identical
- PATCH replaces; PUT updates partially
Answer: PUT replaces the resource; PATCH updates it partially. PUT replaces the whole resource; PATCH applies a partial update.
In requests, which call makes a GET with a timeout?
- requests.fetch(url)
- requests.get(url, timeout=5)
- requests.read(url)
- requests.GET(url)
Answer: requests.get(url, timeout=5). requests.get(url, timeout=5) issues a GET and fails cleanly if the server is too slow.
How do you send a JSON body in a POST with requests?
- requests.post(url, data=data)
- requests.post(url, json=data)
- requests.post(url, body=data)
- requests.post(url, params=data)
Answer: requests.post(url, json=data). json=data serializes the dict and sets Content-Type to application/json automatically.
What does response.raise_for_status() do?
- Prints the status code
- Raises an exception on 4xx or 5xx responses
- Returns True for any response
- Retries the request
Answer: Raises an exception on 4xx or 5xx responses. It turns a failed (4xx/5xx) response into a catchable HTTPError instead of continuing silently.
A 429 status code means what?
- Not found
- Server error
- Rate limited — slow down and retry
- Success
Answer: Rate limited — slow down and retry. 429 Too Many Requests signals rate limiting; respect the Retry-After header and back off.
Which status code family indicates a SERVER error worth retrying?
- 2xx
- 3xx
- 4xx
- 5xx
Answer: 5xx. 5xx codes are server-side failures; 4xx are client errors that should not simply be retried.
What is the recommended retry strategy for transient failures?
- Retry instantly forever
- Exponential backoff with a max attempt limit
- Never retry
- Retry only POST requests
Answer: Exponential backoff with a max attempt limit. Use exponential backoff (1s, 2s, 4s...) with jitter and a cap, retrying safe operations.
Why use a requests.Session() object?
- To make requests slower
- For connection pooling and shared headers/cookies
- To avoid timeouts
- It is required for every request
Answer: For connection pooling and shared headers/cookies. A Session reuses the underlying connection pool and persists headers and cookies across calls.
Continue this course
- Previous: SQLite & ORM
- Next: DevOps Automation