Making API Requests
Learn to send data and make different types of API requests
Making API Requests
GET Requests
GET retrieves data from the server. We covered basics earlier, here's more detail.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/posts/1")
if response.status_code == 200:
post = response.json()
print("Title:", post["title"])
print("Body:", post["body"])What this does: Gets post with ID 1 from test API.
POST Requests
POST sends data to create new resources.
import requests
new_post = {
"title": "My New Post",
"body": "This is the content",
"userId": 1
}
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json=new_post
)
if response.status_code == 201:
print("Created successfully!")
created = response.json()
print("New ID:", created["id"])What this does:
- Sends data as JSON
- Creates new post
- Server responds with created resource
- Status 201 means created
PUT Requests
PUT updates existing resources completely.
import requests
updated_post = {
"id": 1,
"title": "Updated Title",
"body": "Updated content",
"userId": 1
}
response = requests.put(
"https://jsonplaceholder.typicode.com/posts/1",
json=updated_post
)
if response.status_code == 200:
print("Updated successfully!")
print(response.json())What this does: Replaces entire post with ID 1.
PATCH Requests
PATCH updates only specific fields.
import requests
partial_update = {
"title": "New Title Only"
}
response = requests.patch(
"https://jsonplaceholder.typicode.com/posts/1",
json=partial_update
)
if response.status_code == 200:
print("Partial update successful!")Difference between PUT and PATCH:
- PUT: Replace entire resource
- PATCH: Update only specified fields
DELETE Requests
DELETE removes resources.
import requests
response = requests.delete(
"https://jsonplaceholder.typicode.com/posts/1"
)
if response.status_code == 200:
print("Deleted successfully!")
elif response.status_code == 204:
print("Deleted (no content returned)")What status 204 means: Deleted successfully, no data to return.
Sending Form Data
Some APIs expect form data instead of JSON.
import requests
form_data = {
"username": "john",
"email": "john@example.com"
}
response = requests.post(
"https://api.example.com/register",
data=form_data
)Difference:
- json= sends JSON
- data= sends form data
Query Parameters
Add parameters to URL for filtering and searching.
import requests
params = {
"userId": 1,
"completed": "false"
}
response = requests.get(
"https://jsonplaceholder.typicode.com/todos",
params=params
)
todos = response.json()
print("Found", len(todos), "todos")What this creates: URL: /todos?userId=1&completed=false
Custom Headers
Add headers for authentication or content type.
import requests
headers = {
"Authorization": "Bearer your_token_here",
"Content-Type": "application/json",
"User-Agent": "MyApp/1.0"
}
response = requests.get(
"https://api.example.com/data",
headers=headers
)Common headers:
- Authorization: API key or token
- Content-Type: Data format you're sending
- Accept: Data format you want back
- User-Agent: Your application name
API Authentication
API Key in Header
import requests
headers = {
"X-API-Key": "your_api_key_here"
}
response = requests.get(
"https://api.example.com/data",
headers=headers
)API Key in URL
import requests
params = {
"api_key": "your_api_key_here"
}
response = requests.get(
"https://api.example.com/data",
params=params
)Bearer Token
import requests
headers = {
"Authorization": "Bearer your_access_token"
}
response = requests.get(
"https://api.example.com/data",
headers=headers
)Uploading Files
Send files to API.
import requests
files = {
"file": open("document.pdf", "rb")
}
response = requests.post(
"https://api.example.com/upload",
files=files
)
print("Upload status:", response.status_code)Important: Use "rb" mode for binary files.
Session Objects
Use sessions for multiple requests to same API.
import requests
session = requests.Session()
session.headers.update({
"Authorization": "Bearer your_token"
})
response1 = session.get("https://api.example.com/users")
response2 = session.get("https://api.example.com/posts")
session.close()Why use sessions:
- Reuse authentication
- Keep cookies
- Connection reuse (faster)
Handling Redirects
import requests
response = requests.get(
"https://api.example.com/old-url",
allow_redirects=True
)
print("Final URL:", response.url)
print("History:", response.history)What this shows: If API redirects to new URL, shows where you ended up.
Practice Example
The scenario: Build a simple blog post manager using API.
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def get_all_posts():
response = requests.get(BASE_URL + "/posts")
if response.status_code == 200:
posts = response.json()
print("Total posts:", len(posts))
return posts
else:
print("Error getting posts")
return []
def get_post(post_id):
response = requests.get(BASE_URL + "/posts/" + str(post_id))
if response.status_code == 200:
post = response.json()
print("Title:", post["title"])
print("Body:", post["body"])
return post
else:
print("Post not found")
return None
def create_post(title, body, user_id):
new_post = {
"title": title,
"body": body,
"userId": user_id
}
response = requests.post(
BASE_URL + "/posts",
json=new_post
)
if response.status_code == 201:
created = response.json()
print("Post created with ID:", created["id"])
return created
else:
print("Failed to create post")
return None
def update_post(post_id, title, body):
updates = {
"title": title,
"body": body
}
response = requests.patch(
BASE_URL + "/posts/" + str(post_id),
json=updates
)
if response.status_code == 200:
print("Post updated successfully")
return response.json()
else:
print("Failed to update post")
return None
def delete_post(post_id):
response = requests.delete(
BASE_URL + "/posts/" + str(post_id)
)
if response.status_code == 200:
print("Post deleted successfully")
return True
else:
print("Failed to delete post")
return False
posts = get_all_posts()
post = get_post(1)
new_post = create_post("My Title", "My Content", 1)
if new_post:
update_post(new_post["id"], "Updated Title", "Updated Content")
delete_post(1)What this program does:
- Gets all posts
- Gets specific post
- Creates new post
- Updates the new post
- Deletes a post
- All operations with error handling
Retry Logic
Automatically retry failed requests.
import requests
import time
def make_request_with_retry(url, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
if attempt < max_retries - 1:
wait_time = 2 * (attempt + 1)
print("Retry in", wait_time, "seconds...")
time.sleep(wait_time)
else:
print("Max retries reached")
raise
response = make_request_with_retry("https://api.example.com/data")What this does: Retries up to 3 times with increasing wait time.
Key Points to Remember
GET retrieves data, POST creates, PUT updates completely, PATCH updates partially, DELETE removes.
Use json= parameter to send JSON data. Use data= for form data.
Add headers= for authentication and custom headers. Most APIs require API keys or tokens.
Use Session objects for multiple requests to same API. Faster and cleaner than individual requests.
Always check status_code before processing response. Use try-except for error handling.
Common Mistakes
Mistake 1: Wrong method
requests.get(url, json=data) # GET doesn't send body!
requests.post(url, json=data) # Correct for sending dataMistake 2: Forgetting json=
requests.post(url, data) # Sends as form data
requests.post(url, json=data) # Sends as JSONMistake 3: Not handling authentication
requests.get(url) # May get 401 error
requests.get(url, headers={"Authorization": token}) # CorrectMistake 4: Exposing API keys Don't put API keys in code. Use environment variables.
What's Next?
You now know how to make API requests. Next, you'll learn about API authentication and pagination - handling auth tokens and working with large datasets.