Introduction to APIs and HTTP Communication in Python
Course: Python Training
Topic: Introduction to APIs and HTTP Communication in Python
Course Link: Python Training Course
Demo Link: Register for a Course Demo
Website: JustAcademy Homepage
1. Introduction to APIs
An API (Application Programming Interface) is a set of rules and conventions that allows two software applications to communicate with each other. It defines how one application can request data or functionality from another application without needing to understand its internal implementation.
For example, a Python application can communicate with a weather service API to retrieve the current temperature of a city. The API receives the request, processes it, and returns the result in a structured format such as JSON.
Real-World Examples of APIs
- Weather APIs: Provide temperature, humidity, forecasts, and weather alerts.
- Payment APIs: Enable applications to integrate online payment services.
- Maps APIs: Provide location, distance, navigation, and geographical data.
- Social media APIs: Allow authorized applications to access supported platform features.
- Authentication APIs: Support user identity verification and access management.
- E-commerce APIs: Provide product information, order details, inventory, and shipping information.
Why Are APIs Important?
- They allow applications built with different technologies to communicate.
- They enable developers to reuse existing services and functionality.
- They reduce development time and duplication of work.
- They support integrations between websites, mobile applications, and external services.
- They help separate application components into manageable services.
- They make it easier to exchange data between different systems.
2. How an API Works
Most web APIs follow a client-server communication model. The client requests information or an operation, and the server processes the request and sends a response.
- Client: The application that needs information or wants to perform an operation.
- Request: The client sends an HTTP request to an API endpoint.
- Server: The server receives the request and checks its validity and permissions.
- Processing: The server performs the requested operation, potentially accessing a database or another service.
- Response: The server returns an HTTP status code and, when appropriate, response data.
- Client handling: The client examines the response and uses the returned information.
API Communication Flow
Python Client
|
| HTTP Request
v
API Server
|
| Validate and Process Request
v
Database or External Service
|
| Return Result
v
API Server
|
| HTTP Response (JSON)
v
Python Client
Python can act as an API client when consuming external services. It can also act as an API server when exposing functionality through frameworks such as Flask or FastAPI.
3. Understanding HTTP
HTTP stands for Hypertext Transfer Protocol. It is an application-layer protocol used to exchange information between clients and servers. Web browsers, mobile applications, and Python programs commonly use HTTP to communicate with web servers.
HTTPS is HTTP protected by Transport Layer Security (TLS). When correctly configured, HTTPS encrypts data in transit and helps authenticate the server.
Important Characteristics of HTTP
- Request-response model: A client sends a request and receives a response.
- Statelessness: HTTP does not automatically remember previous requests. Applications can maintain state using sessions, cookies, tokens, or server-side storage.
- Methods: HTTP supports methods such as GET, POST, PUT, PATCH, and DELETE.
- Headers: Requests and responses can contain metadata about content, authentication, caching, and other behavior.
- Status codes: Responses communicate whether a request succeeded or encountered an error.
4. Structure of an HTTP Request
An HTTP request generally contains a method, a URL or target path, headers, and optionally a request body.
| Component |
Description |
Example |
| HTTP method |
Indicates the intended operation. |
GET, POST |
| URL or endpoint |
Identifies the resource or operation. |
https://api.example.com/users |
| Headers |
Provide request metadata. |
Accept: application/json |
| Query parameters |
Pass filtering or search values in the URL. |
?page=1&limit=10 |
| Request body |
Contains data submitted to the server. |
A JSON object |
Example HTTP Request
GET /users?page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
This request asks the server for the first page of users and indicates that the client prefers JSON data. The example domain is illustrative and is not a working API endpoint.
5. Structure of an HTTP Response
An HTTP response is sent by the server after processing a request. It usually contains a status code, response headers, and an optional response body.
| Component |
Purpose |
| Status code |
Indicates the result of the request. |
| Response headers |
Describe the content and provide response metadata. |
| Response body |
Contains returned data, an error message, or other content when provided. |
Example JSON Response
{
"id": 101,
"name": "Aarav",
"email": "[email protected]"
}
This response contains a user object. The client can parse the JSON and access individual fields using Python dictionary syntax.
6. Common HTTP Methods
HTTP methods indicate the intended action for a resource. The API determines which methods are supported by each endpoint.
| Method |
Purpose |
Example |
| GET |
Retrieve information. |
Fetch a list of products. |
| POST |
Submit data or create a resource. |
Create a customer record. |
| PUT |
Replace a resource representation. |
Replace a complete user profile. |
| PATCH |
Partially update a resource. |
Change a user's email address. |
| DELETE |
Request removal of a resource. |
Delete a product. |
Important: GET should not be used for operations intended to modify server-side data. PUT is generally designed to be idempotent, meaning repeating the same request should have the same intended effect. POST is not generally idempotent by default. Actual behavior depends on the API's implementation.
7. HTTP Status Codes
HTTP status codes indicate the outcome of a request. They are divided into five main categories.
| Range |
Category |
Examples |
| 100–199 |
Informational |
100 Continue |
| 200–299 |
Success |
200 OK, 201 Created, 204 No Content |
| 300–399 |
Redirection |
301 Moved Permanently, 304 Not Modified |
| 400–499 |
Client errors |
400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests |
| 500–599 |
Server errors |
500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
Frequently Used Status Codes
- 200 OK: The request succeeded.
- 201 Created: A resource was created successfully.
- 204 No Content: The request succeeded without a response body.
- 400 Bad Request: The request is invalid or cannot be processed.
- 401 Unauthorized: Valid authentication credentials are missing or invalid.
- 403 Forbidden: The server refuses to authorize the requested operation.
- 404 Not Found: The resource or endpoint could not be found.
- 429 Too Many Requests: The client exceeded a request limit.
- 500 Internal Server Error: The server encountered an unexpected problem.
8. Understanding JSON in Python APIs
JSON stands for JavaScript Object Notation. It is a lightweight text-based format used to exchange structured data between applications. JSON is widely used by web APIs because it is readable and supported by many programming languages.
JSON Data Types
- String:
"Python"
- Number:
25 or 19.5
- Boolean:
true or false
- Null:
null
- Array:
["Python", "SQL", "Flask"]
- Object:
{"name": "Aarav", "age": 25}
Convert Python Data into JSON
import json
student = {
"name": "Aarav",
"course": "Python",
"completed": True
}
json_text = json.dumps(student)
print(json_text)
Convert JSON into Python Data
import json
json_text = '{"name": "Aarav", "age": 25}'
student = json.loads(json_text)
print(student["name"])
print(student["age"])
Python's json module converts JSON objects into dictionaries, arrays into lists, and JSON Boolean and null values into the corresponding Python values. Python uses True, False, and None, while JSON uses true, false, and null.
9. Setting Up Python for HTTP Communication
Python includes standard-library tools for network communication. However, the third-party Requests library is a popular choice because it makes common HTTP operations straightforward.
Step 1: Check Python Installation
python --version
Step 2: Install the Requests Library
python -m pip install requests
If your environment uses the python3 command, use python3 -m pip install requests where appropriate. A virtual environment is recommended for managing project dependencies.
Step 3: Import Requests
import requests
10. Making a GET Request
A GET request retrieves information from an API. It is commonly used to fetch user details, products, articles, weather information, and other resources.
Example: Retrieve a Post
import requests
url = "https://jsonplaceholder.typicode.com/posts/1"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
post = response.json()
print("Post ID:", post["id"])
print("Title:", post["title"])
print("Body:", post["body"])
except requests.exceptions.Timeout:
print("The request timed out.")
except requests.exceptions.HTTPError as error:
print("HTTP error:", error)
except requests.exceptions.RequestException as error:
print("Request failed:", error)
Explanation
requests.get(url) sends an HTTP GET request.
timeout=10 configures connection and response waiting limits.
raise_for_status() raises an HTTP error for unsuccessful status codes.
response.json() parses a valid JSON response into Python data.
- The
try and except blocks help handle request failures.
JSONPlaceholder is a public testing API. It is useful for learning, but its behavior and availability are not guaranteed. Consult the documentation of the actual API you intend to use.
11. Inspecting the HTTP Response
The Requests response object provides useful properties for understanding the server's reply.
import requests
url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.get(url, timeout=10)
print("Status code:", response.status_code)
print("Response URL:", response.url)
print("Content type:", response.headers.get("Content-Type"))
print("Response text:", response.text[:200])
| Property or Method |
Description |
response.status_code |
Returns the HTTP status code. |
response.headers |
Provides response headers. |
response.text |
Returns the response body as text. |
response.content |
Returns the response body as bytes. |
response.json() |
Parses a JSON response. |
response.url |
Provides the final response URL. |
response.raise_for_status() |
Raises an exception for unsuccessful HTTP status codes. |
Do not assume every response contains valid JSON. A server may return an empty response, HTML error page, or plain text. Check the status code and content type when necessary.
12. Sending Query Parameters
Query parameters are used to filter, search, sort, or paginate API results. They appear in the URL after a question mark, with multiple parameters separated by ampersands.
For example, https://api.example.com/products?category=books&limit=10 requests products in the books category with a limit of ten, assuming the API supports those parameters.
Python Example
import requests
url = "https://jsonplaceholder.typicode.com/posts"
params = {
"userId": 1,
"_limit": 5
}
response = requests.get(
url,
params=params,
timeout=10
)
response.raise_for_status()
posts = response.json()
for post in posts:
print(post["id"], post["title"])
Using the params argument is generally safer and more convenient than manually constructing a URL because Requests handles URL encoding.
13. Sending HTTP Headers
Headers carry metadata about a request. APIs may require headers to specify the expected response format, identify the client, or provide authentication credentials.
import requests
url = "https://api.example.com/profile"
headers = {
"Accept": "application/json",
"User-Agent": "PythonLearningApp/1.0"
}
response = requests.get(
url,
headers=headers,
timeout=10
)
print("Status code:", response.status_code)
The endpoint above is an illustrative placeholder. Common headers include:
Accept: Specifies the response content types the client can accept.
Content-Type: Describes the format of the request body.
Authorization: Carries authentication credentials when required.
User-Agent: Identifies the client application.
Cache-Control: Communicates caching preferences or requirements.
14. Sending POST Requests with JSON
POST requests are often used to submit information or create a resource. The Requests library can automatically convert a Python dictionary to JSON using the json argument.
import requests
url = "https://jsonplaceholder.typicode.com/posts"
new_post = {
"title": "Learning Python APIs",
"body": "Practicing HTTP communication with Requests.",
"userId": 1
}
try:
response = requests.post(
url,
json=new_post,
timeout=10
)
response.raise_for_status()
if response.content:
print(response.json())
else:
print("The request succeeded without a response body.")
except requests.exceptions.RequestException as error:
print("Request failed:", error)
Why Use the JSON Argument?
- It serializes the Python object into JSON.
- It sets the appropriate JSON content type.
- It makes the request easier to read and maintain.
JSONPlaceholder is a testing service and may simulate record creation rather than permanently storing data. Real APIs may require authentication, validation, and additional fields.
15. Understanding PUT, PATCH, and DELETE
PUT Request
PUT is commonly used to replace a resource representation.
import requests
url = "https://api.example.com/users/10"
updated_user = {
"name": "Aarav Sharma",
"email": "[email protected]"
}
response = requests.put(
url,
json=updated_user,
timeout=10
)
print(response.status_code)
PATCH Request
PATCH is commonly used to update selected fields of an existing resource.
import requests
url = "https://api.example.com/users/10"
changes = {
"email": "[email protected]"
}
response = requests.patch(
url,
json=changes,
timeout=10
)
print(response.status_code)
DELETE Request
DELETE requests ask the server to remove a resource or make it unavailable according to the API's design.
import requests
url = "https://api.example.com/users/10"
response = requests.delete(url, timeout=10)
print("Status code:", response.status_code)
These endpoints are examples only. Use real modification and deletion requests only when you are authorized to access the service and understand the consequences.
16. API Authentication
Many APIs protect their data and operations through authentication. Authentication identifies the caller, while authorization determines which resources or actions that caller is allowed to access.
Common Authentication Methods
- API key: A key used to identify an application or client.
- Bearer token: A token commonly sent in the Authorization header.
- OAuth 2.0: An authorization framework that supports controlled access to protected resources.
- Basic authentication: Sends a username and password encoded using Base64. It should only be used over HTTPS and when supported by the API.
- Session-based authentication: Uses session identifiers, often stored in cookies.
Example: Bearer Token Authentication
import os
import requests
token = os.environ.get("API_TOKEN")
if not token:
raise RuntimeError("Set the API_TOKEN environment variable.")
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json"
}
response = requests.get(
"https://api.example.com/profile",
headers=headers,
timeout=10
)
response.raise_for_status()
print(response.json())
The URL is a placeholder for an authenticated API endpoint. Store real credentials in environment variables or a secrets manager. Do not hard-code tokens into source files or publish them in public repositories.
17. Using Sessions in Requests
A Requests session can reuse connections and preserve cookies and configuration across multiple requests. Sessions are useful when an API requires related requests to share cookies or common headers.
import requests
with requests.Session() as session:
session.headers.update({
"Accept": "application/json"
})
first_response = session.get(
"https://jsonplaceholder.typicode.com/posts/1",
timeout=10
)
first_response.raise_for_status()
second_response = session.get(
"https://jsonplaceholder.typicode.com/posts/2",
timeout=10
)
second_response.raise_for_status()
print(first_response.json()["title"])
print(second_response.json()["title"])
A session does not automatically authenticate a user. Authentication details must be configured according to the API's requirements.
18. Error Handling in API Communication
API requests can fail because of connection problems, timeouts, invalid URLs, authentication errors, rate limits, or server failures. Proper error handling prevents an application from crashing unexpectedly and helps developers diagnose problems.
| Exception |
Possible Cause |
Recommended Action |
Timeout |
The connection or response took too long. |
Check service performance and consider a safe retry. |
ConnectionError |
The connection could not be established or was interrupted. |
Check connectivity, DNS, and service availability. |
HTTPError |
An unsuccessful HTTP status code was raised. |
Inspect the status code and API error details. |
RequestException |
A general Requests-related failure occurred. |
Handle as a broader request error. |
JSONDecodeError |
The response body is not valid JSON. |
Check the response body and content type. |
Example: Robust Error Handling
import requests
url = "https://jsonplaceholder.typicode.com/posts/1"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
try:
data = response.json()
print("Title:", data["title"])
except requests.exceptions.JSONDecodeError:
print("The server did not return valid JSON.")
except requests.exceptions.Timeout:
print("The request timed out.")
except requests.exceptions.HTTPError as error:
print("HTTP error:", error)
except requests.exceptions.ConnectionError:
print("Could not connect to the API.")
except requests.exceptions.RequestException as error:
print("Request failed:", error)
Use exception classes supported by your installed Requests version. Keep error messages useful, but do not expose credentials, tokens, private user data, or sensitive server details in logs.
19. Timeouts, Retries, and Rate Limits
Timeouts
A timeout prevents a client from waiting indefinitely for a connection or response. Requests accepts a single timeout value or a tuple specifying connection and read timeouts.
response = requests.get(
"https://jsonplaceholder.typicode.com/posts/1",
timeout=(3, 10)
)
Here, the connection timeout is 3 seconds and the read timeout is 10 seconds. A read timeout generally limits how long the client waits between received bytes; it is not necessarily a strict total deadline for the entire operation.
Retries
Retries can help recover from temporary failures. However, they must be configured carefully. Repeating a request that changes data can accidentally create duplicate records or repeat an operation. Prefer retrying safe operations and follow the API's documented retry rules.
Rate Limits
Rate limits restrict the number of requests allowed within a time period. A server may return 429 Too Many Requests when a client exceeds the limit.
- Check the API's rate-limit documentation.
- Respect the
Retry-After header when provided.
- Avoid sending repeated requests unnecessarily.
- Use caching and pagination where appropriate.
20. Working with API Pagination
Pagination divides a large collection into smaller responses. APIs may use page numbers, offsets, cursors, or next-page URLs to support pagination.
Example: Page-Based Pagination
import requests
base_url = "https://api.example.com/products"
page = 1
page_size = 20
while True:
params = {
"page": page,
"limit": page_size
}
response = requests.get(
base_url,
params=params,
timeout=10
)
response.raise_for_status()
products = response.json()
if not products:
break
for product in products:
print(product)
if len(products) < page_size:
break
page += 1
This is an illustrative example that assumes the API returns a list of products for each page. Actual APIs may return a JSON object with fields such as items, next, or next_cursor. Always follow the API's pagination documentation.
21. Validating API Response Data
Data returned by an external API should not be assumed to be complete or correctly formatted. Validate required fields and data types before using the information in your application.
def get_user_name(user_data):
if not isinstance(user_data, dict):
raise ValueError("Expected a dictionary.")
name = user_data.get("name")
if not isinstance(name, str) or not name.strip():
raise ValueError("A valid name is required.")
return name.strip()
example_user = {
"id": 101,
"name": "Aarav"
}
print(get_user_name(example_user))
Validation helps prevent errors caused by missing keys, unexpected data types, and malformed values. Larger projects can use validation libraries or typed data models to manage complex responses.
22. Introduction to REST APIs
REST stands for Representational State Transfer. It is an architectural style commonly used for designing web APIs around resources. REST-style APIs often use URLs to identify resources and HTTP methods to express operations.
| Endpoint |
Method |
Typical Purpose |
/products |
GET |
Retrieve a list of products. |
/products/25 |
GET |
Retrieve product 25. |
/products |
POST |
Create a product. |
/products/25 |
PUT |
Replace product 25. |
/products/25 |
PATCH |
Update selected fields of product 25. |
/products/25 |
DELETE |
Delete product 25. |
These examples demonstrate common conventions rather than universal rules. Not every HTTP API follows all REST architectural constraints.
23. Creating a Simple API Using Flask
Python can be used to build API servers as well as consume external APIs. Flask is a lightweight web framework that allows developers to create routes and return JSON responses.
Install Flask
python -m pip install flask
Example Flask API
from flask import Flask, jsonify
app = Flask(__name__)
products = [
{"id": 1, "name": "Keyboard", "price": 1200},
{"id": 2, "name": "Mouse", "price": 650},
{"id": 3, "name": "Monitor", "price": 8500}
]
@app.get("/api/products")
def get_products():
return jsonify(products)
@app.get("/api/products/<int:product_id>")
def get_product(product_id):
for product in products:
if product["id"] == product_id:
return jsonify(product)
return jsonify({"error": "Product not found"}), 404
if __name__ == "__main__":
app.run(debug=True)
Run the Application
- Save the code in a file named
app.py.
- Install Flask using
python -m pip install flask.
- Run the application using
python app.py.
- Visit
http://127.0.0.1:5000/api/products to retrieve all products.
- Visit
http://127.0.0.1:5000/api/products/1 to retrieve a specific product.
The application stores example products in memory, so the data is not persistent. Debug mode is intended for local development only. Use a suitable production server and secure configuration when deploying a real application.
24. API Security Best Practices
- Use HTTPS: Protect data and credentials during transmission.
- Protect API keys: Store secrets in environment variables or a secrets manager.
- Validate input: Check data received from users and external services.
- Apply authorization: Verify that users have permission to access or modify requested resources.
- Use least privilege: Give applications only the permissions they need.
- Handle errors safely: Avoid exposing internal stack traces or sensitive implementation details.
- Respect rate limits: Avoid excessive requests and follow server retry instructions.
- Keep dependencies updated: Use maintained libraries and install security updates.
- Verify TLS certificates: Do not disable certificate verification in production code.
- Protect logs: Avoid recording passwords, tokens, and sensitive personal information.
25. Common API Communication Problems
| Problem |
Possible Cause |
Solution |
| Connection error |
Network, DNS, or server problem. |
Check connectivity, hostname, and service availability. |
| Timeout |
The connection or response takes too long. |
Configure suitable timeouts and investigate service performance. |
| 401 Unauthorized |
Missing or invalid credentials. |
Check the authentication requirements. |
| 403 Forbidden |
The caller lacks permission. |
Review authorization rules and access rights. |
| 404 Not Found |
Incorrect endpoint or missing resource. |
Verify the URL, resource ID, and API version. |
| 429 Too Many Requests |
Rate limit exceeded. |
Reduce request frequency and follow retry instructions. |
| 500-series error |
Server-side or upstream failure. |
Check service status and retry safely when appropriate. |
| JSON parsing error |
The response is empty or contains invalid JSON. |
Inspect the response body, status, and content type. |
26. Practical Exercises
Exercise 1: Fetch a Post
- Send a GET request to
https://jsonplaceholder.typicode.com/posts/1.
- Print the status code, title, and body.
- Handle network errors and unsuccessful HTTP responses.
Exercise 2: Retrieve Multiple Records
- Retrieve posts using the
userId query parameter.
- Print the ID and title of every returned post.
- Experiment with different query parameter values.
Exercise 3: Send JSON Data
- Create a Python dictionary containing a title, body, and user ID.
- Submit it using a POST request and the
json argument.
- Inspect the status code and response body.
Exercise 4: Create a Flask API
- Create an API that returns a list of books or products.
- Add an endpoint that retrieves a record by ID.
- Return a 404 response if the requested record does not exist.
Exercise 5: Improve Reliability
- Add a timeout to every outgoing request.
- Handle connection errors and unsuccessful HTTP responses.
- Validate required fields in the returned JSON.
- Ensure sensitive information is not printed in logs.
27. Interview Questions and Answers
Q1. What is an API?
Answer: An API is an interface that defines how software components communicate and exchange data or functionality.
Q2. What is HTTP?
Answer: HTTP is an application-layer protocol used for communication between clients and servers.
Q3. What is the difference between HTTP and HTTPS?
Answer: HTTPS uses TLS to protect HTTP communication through encryption and server authentication. Plain HTTP does not provide TLS protection.
Q4. What is a REST API?
Answer: A REST API is designed around REST architectural principles, commonly using resource-oriented endpoints and HTTP methods.
Q5. What is JSON?
Answer: JSON is a text-based format used to exchange structured data between applications.
Q6. How do you make an HTTP request in Python?
Answer: One common method is to use Requests, such as requests.get(url, timeout=10).
Q7. What does raise_for_status() do?
Answer: It raises an HTTP error when the response has an unsuccessful HTTP status code.
Q8. What is the difference between GET and POST?
Answer: GET is generally used to retrieve information, whereas POST is commonly used to submit data or create resources.
Q9. Why should API requests have timeouts?
Answer: Timeouts prevent an application from waiting indefinitely for a connection or response.
Q10. What is an API endpoint?
Answer: An endpoint is a URL or URL path through which a client accesses a particular resource or API operation.
Q11. What does HTTP 404 mean?
Answer: It means the requested resource or endpoint could not be found.
Q12. How can you protect an API token in Python?
Answer: Store it in an environment variable or secrets manager, avoid committing it to source control, and never expose it in logs or public responses.
28. Quick Revision Checklist
- Understand what APIs are and how clients communicate with servers.
- Know the structure of HTTP requests and responses.
- Understand GET, POST, PUT, PATCH, and DELETE methods.
- Recognize common HTTP status codes.
- Understand JSON and Python's JSON support.
- Install and use the Requests library.
- Send query parameters, headers, and JSON bodies.
- Inspect responses and handle HTTP errors.
- Use timeouts and configure retries carefully.
- Understand authentication, pagination, and rate limits.
- Recognize common REST API patterns.
- Create a basic API using Flask.
- Apply security and data validation best practices.
29. Conclusion
APIs and HTTP communication are essential concepts for Python developers working with web applications, automation, data integration, and backend services. Understanding requests, responses, HTTP methods, status codes, JSON, authentication, and error handling makes it possible to build reliable API clients and services.
Start by retrieving data from a public testing API, then practise sending JSON requests, handling failures, and creating a small Flask API. These exercises establish a strong foundation for advanced topics such as database integration, OAuth, asynchronous requests, and production deployment.
30. Additional Learning Resources