How to call an API in Python
Learn how to call an API in Python. This guide covers different methods, tips, real-world examples, and common error debugging.

You can use Python to call APIs, a core skill for developers. This allows applications to fetch data from external services and unlocks powerful integrations for any project you build.
In this article, you'll find key techniques, practical tips, and real-world applications. You'll also get debugging advice to help you confidently integrate external data into your Python projects.
Using the requests library for a basic API call
import requests
response = requests.get("https://jsonplaceholder.typicode.com/posts/1")
data = response.json()
print(data)--OUTPUT--{'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'}
The requests library is a popular choice for making HTTP requests because it simplifies the process. In this example, requests.get() sends a request to the specified URL, which is a placeholder API endpoint for a single post. This function returns a Response object containing all the information the server sent back.
The most important step is using the .json() method. This method takes the raw JSON data from the response and automatically converts it into a Python dictionary. This makes the data immediately accessible and easy to work with in your code, without needing to parse it manually.
Basic API techniques
With a basic GET request covered, you're ready to tackle more common API tasks like filtering results, sending data, and handling authentication.
Working with query parameters using requests
import requests
params = {"postId": 1, "limit": 3}
response = requests.get("https://jsonplaceholder.typicode.com/comments", params=params)
comments = response.json()
print(f"Found {len(comments)} comments")
print(comments[0]["email"])--OUTPUT--Found 3 comments
Eliseo@gardner.biz
Query parameters let you filter or customize the data you receive from an API. Instead of manually building the URL string, the requests library simplifies this with the params argument in the get() function.
- You pass a dictionary of key-value pairs to
params. - The library automatically formats and appends them to the URL for you.
In this example, {"postId": 1, "limit": 3} asks the API for comments associated with post ID 1 and limits the results to three, making your request more specific and efficient.
Making POST requests with JSON data
import requests
new_post = {
"title": "My New Post",
"body": "This is the content of my post",
"userId": 1
}
response = requests.post("https://jsonplaceholder.typicode.com/posts", json=new_post)
print(f"Status code: {response.status_code}")
print(response.json())--OUTPUT--Status code: 201
{'title': 'My New Post', 'body': 'This is the content of my post', 'userId': 1, 'id': 101}
While GET requests fetch data, POST requests send data to create something new on the server. In this case, you're creating a new post.
- First, you define a Python dictionary called
new_postwith the data for your new entry. - Then, you use
requests.post(), passing the URL and your dictionary to thejsonparameter. The library handles converting your dictionary into JSON format. - A
201status code confirms the resource was successfully created. The API's response includes the new post, now with anidassigned by the server.
Handling API authentication
import requests
api_key = "your_api_key_here"
headers = {"Authorization": f"Bearer {api_key}"}
response = requests.get(
"https://api.example.com/data",
headers=headers
)
print(f"Status: {response.status_code}")
print("Authenticated request completed")--OUTPUT--Status: 200
Authenticated request completed
Many APIs require authentication to protect data. This example uses a common method—sending an API key in the request headers. You create a dictionary called headers to hold this information, which proves your identity to the server.
- The
Authorizationkey is standard for this purpose. Its value often starts withBearerfollowed by your unique API key. - You pass this dictionary to the
headersparameter in yourrequests.get()call. The library then includes it with your request, granting you access.
Advanced API techniques
Building on those foundational skills, you can now write more powerful API integrations by handling asynchronous calls, creating reusable clients, and implementing robust error handling.
Working with async API calls using aiohttp
import aiohttp
import asyncio
async def fetch_data(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()
async def main():
tasks = [
fetch_data(f"https://jsonplaceholder.typicode.com/posts/{i}")
for i in range(1, 4)
]
results = await asyncio.gather(*tasks)
for result in results:
print(f"Post title: {result['title'][:30]}...")
asyncio.run(main())--OUTPUT--Post title: sunt aut facere repellat provi...
Post title: qui est esse...
Post title: ea molestias quasi exercitatio...
When you need to make many API calls, running them one by one can be slow. The aiohttp library, paired with Python's built-in asyncio, lets you perform these requests concurrently. This means your program doesn't have to wait for one request to finish before starting the next, which saves a lot of time and is memory-efficient.
- The code uses
asyncandawaitsyntax to define non-blocking network operations. - Instead of waiting for each request,
asyncio.gather()runs all your API call tasks at the same time. - It then collects the results once they are all complete, making your data fetching far more efficient.
Creating a reusable API client class
class ApiClient:
def __init__(self, base_url, api_key=None):
self.base_url = base_url
self.session = requests.Session()
if api_key:
self.session.headers.update({"Authorization": f"Bearer {api_key}"})
def get(self, endpoint, params=None):
url = f"{self.base_url}/{endpoint}"
response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()
client = ApiClient("https://jsonplaceholder.typicode.com")
users = client.get("users")
print(f"Retrieved {len(users)} users")
print(f"First user: {users[0]['name']}")--OUTPUT--Retrieved 10 users
First user: Leanne Graham
Wrapping your API logic in a class like ApiClient keeps your code organized and DRY—Don't Repeat Yourself. This approach centralizes all your API-related functions, making them easier to manage and reuse across your project.
- The
__init__method sets up arequests.Session()object, which efficiently reuses connections and can store headers for authentication across all requests. - Methods like
getsimplify calls by only requiring an endpoint, while handling URL construction internally. - It includes
response.raise_for_status(), a crucial step that automatically checks for HTTP errors and stops your code if a request fails.
Implementing error handling and retries
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504])
session.mount('https://', HTTPAdapter(max_retries=retries))
try:
response = session.get("https://api.example.com/potentially-unstable-endpoint")
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
print(f"API request failed: {e}")--OUTPUT--API request failed: HTTPSConnectionPool(host='api.example.com', port=443): Max retries exceeded with url: /potentially-unstable-endpoint
APIs can sometimes fail due to temporary server issues. Instead of letting your application crash, you can build in resilience. This code configures a requests session to automatically retry failed requests—a best practice for creating reliable software.
- The
Retryobject specifies the rules. Here, it'll attempt a request up to three times (total=3). - A
backoff_factoradds a smart delay between retries so you don't overwhelm the server. - The
status_forcelistensures it only retries on specific server errors, like503 Service Unavailable.
Finally, the try...except block catches any error that persists after all retries, preventing a crash. For more complex error scenarios, you can also use automated code repair to identify and fix issues in your API integration code.
Move faster with Replit
Replit is an AI-powered development platform where all Python dependencies are pre-installed, so you can skip setup and start coding instantly. This lets you move from piecing together individual techniques to building complete applications with Agent 4, which handles everything from code and APIs to deployment based on your description.
Instead of just learning the methods, you can describe the app you want to build and let the Agent take it from idea to a working product. For example, you could create:
- A real-time crypto dashboard that uses
GETrequests with query parameters to fetch and display live prices for specific coins. - A social media scheduler that uses
POSTrequests to automatically publish content to different platforms at set times. - A data aggregation tool that uses
aiohttpfor asynchronous calls to efficiently pull user data from multiple API endpoints.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
When working with APIs, you'll run into common issues like timeouts, bad server responses, and URL encoding problems.
Handling timeout errors with the timeout parameter
An API server might take too long to respond, leaving your application stuck waiting indefinitely. By default, requests will wait forever for a response, which can freeze your program. The code below shows what happens when a request hangs without a timeout.
import requests
response = requests.get("https://api.example.com/data")
data = response.json()
print("Data retrieved successfully")
The program hangs because the requests.get() call waits indefinitely for an unresponsive server. The following example demonstrates how to prevent your application from getting stuck.
import requests
response = requests.get("https://api.example.com/data", timeout=5)
data = response.json()
print("Data retrieved successfully")
Adding timeout=5 to your requests.get() call is the solution. This parameter sets a deadline for the server to respond, preventing your program from hanging indefinitely.
- It tells
requeststo wait no more than five seconds for a response. - If the server doesn't reply in time, the library raises a
Timeoutexception, which you can handle instead of letting your program freeze.
It's a crucial safeguard for any external network request, as you can't predict when a service will be slow or unavailable.
Checking status codes before calling json()
Not all API responses are successful. If you request a non-existent endpoint, the server returns an error, not the JSON data you expect. Calling .json() on such a response will crash your program. The following code demonstrates this common pitfall.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/nonexistent")
data = response.json()
print(data)
The server returns a 404 error, so the response body contains an error message instead of JSON. Calling .json() fails because it can't decode the content. The code below demonstrates the correct way to handle this.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/nonexistent")
if response.status_code == 200:
data = response.json()
print(data)
else:
print(f"Error: {response.status_code}, {response.text[:50]}")
To avoid crashes, always check the API's response before trying to use it. The solution is to wrap your .json() call in a simple if statement. You check if response.status_code is 200 (success). If it is, you proceed. If not, the else block catches the error, printing the status and preventing your app from breaking. This is a simple but crucial step for writing robust code that interacts with any external service.
Handling URL encoding issues with special characters
Handling URL encoding issues with special characters
Special characters like the ampersand (&) or spaces in a URL can break your API request because they have reserved meanings. If you don't encode them properly, the server will misinterpret the URL. The following code shows what happens when a search term breaks the request.
import requests
search_term = "coffee & tea"
response = requests.get(f"https://api.example.com/search?q={search_term}")
results = response.json()
print(f"Found {len(results)} results")
The f-string inserts the & directly into the URL, which the server misreads as a parameter separator. This breaks the query, making it look for q=coffee instead of the full phrase. The code below demonstrates the proper way to build the URL.
import requests
from urllib.parse import quote
search_term = "coffee & tea"
encoded_term = quote(search_term)
response = requests.get(f"https://api.example.com/search?q={encoded_term}")
results = response.json()
print(f"Found {len(results)} results")
The urllib.parse.quote() function solves this by converting special characters like & and spaces into a URL-safe format, such as %26. This ensures the server correctly interprets your entire search term instead of splitting it. You should always use this function when building URLs with user input or any text that might contain special characters. This simple step prevents unexpected behavior and ensures your API requests work as intended.
Real-world applications
Now that you've seen how to handle common errors, you can apply these API skills to build practical, data-driven applications.
Creating a simple URL shortener client with requests
This example puts theory into practice by showing how a single GET request to a public API can power a useful tool like a URL shortener.
import requests
def shorten_url(long_url):
api_url = f"https://tinyurl.com/api-create.php?url={long_url}"
response = requests.get(api_url)
return response.text
original_url = "https://www.example.com/really/long/url/that/is/hard/to/share"
short_url = shorten_url(original_url)
print(f"Original URL: {original_url}")
print(f"Shortened URL: {short_url}")
This function, shorten_url, shows how to interact with a simple text-based API. It constructs the request URL using an f-string, embedding the long URL as a query parameter for the TinyURL service. You could also build this tool using vibe coding to describe what you want rather than writing the code manually.
- The
requests.get()call sends the request to the API endpoint. - Instead of parsing JSON, the function returns
response.text. This is because the API directly sends back the shortened URL as a plain text string, a common pattern for simple services.
Downloading and analyzing JSON data sets
This example combines an API request with data processing to fetch a complete dataset, parse it, and calculate key statistics.
import requests
import statistics
def analyze_population_data():
url = "https://restcountries.com/v3.1/region/europe?fields=name,population"
response = requests.get(url)
countries = response.json()
populations = [country['population'] for country in countries]
return {
'count': len(populations),
'min': min(populations),
'max': max(populations),
'mean': statistics.mean(populations)
}
stats = analyze_population_data()
print(f"Analyzed {stats['count']} European countries:")
print(f"Smallest population: {stats['min']:,}")
print(f"Largest population: {stats['max']:,}")
print(f"Average population: {stats['mean']:,.0f}")
This function, analyze_population_data, fetches data for European countries from the REST Countries API. It uses a list comprehension to efficiently extract population numbers from the JSON response before calculating basic statistics. Similar data processing patterns are used when reading CSV files for analysis.
- The API URL includes
?fields=name,populationto request only the necessary data, reducing the payload size. - A list comprehension,
[country['population'] for country in countries], creates a new list containing only population figures for easy analysis. - The
statisticslibrary is then used to calculate the mean from this list.
Get started with Replit
Turn these techniques into a real tool. Describe what you want to build to Replit Agent, like “build a crypto dashboard that fetches live prices” or “create a tool to post updates to a Discord channel.”
Replit Agent will write the code, test for errors, and deploy your application for you. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



