How to use an API in Python
Learn to use APIs in Python with this guide. Explore different methods, get tips, see real-world applications, and learn to debug common errors.

APIs allow applications to communicate and share data. Python's simple syntax and powerful libraries make it a top choice to connect with APIs for countless web services and data sources.
In this article, you'll explore techniques and tips to use APIs. You will find real-world applications and receive advice to debug your code for successful API integration.
Basic API request with requests
import requests
response = requests.get("https://api.github.com/users/python")
data = response.json()
print(f"User: {data['login']}, Followers: {data['followers']}")--OUTPUT--User: python, Followers: 10923
The requests library is the standard for making HTTP requests in Python, abstracting away the complexities of network communication. The code uses requests.get() to fetch data from the GitHub API endpoint for the user "python." This function sends a GET request, which is the conventional method for retrieving information from a web server.
Once the server sends back a response, the .json() method is crucial. It decodes the response's JSON-formatted text into a native Python dictionary. This conversion makes the data immediately accessible, allowing you to work with it using familiar dictionary keys like data['login'].
Common API interaction techniques
Building on that basic request, you'll often need to customize your interactions with parameters, headers, and authentication to get the exact data you need.
Using parameters and headers with requests
import requests
params = {"q": "python", "sort": "stars"}
headers = {"Accept": "application/json", "User-Agent": "PythonTutorial"}
response = requests.get("https://api.github.com/search/repositories",
params=params, headers=headers)
results = response.json()
print(f"Found {results['total_count']} Python repositories")--OUTPUT--Found 4456789 Python repositories
You can customize requests using parameters and headers. The params argument refines your query, letting you filter or sort results without manually building a complex URL. The headers dictionary sends metadata along with your request.
- The
paramsdictionary in the example searches for repositories with "python" and sorts them by stars. - The
headerstell the server what kind of response you want ("Accept": "application/json") and identify your application ("User-Agent"), which is often required by APIs.
The requests library handles the encoding for you, keeping your code clean.
Handling JSON responses
import requests
import json
response = requests.get("https://api.github.com/users/python")
pretty_json = json.dumps(response.json(), indent=2)
print("User data preview:")
print(pretty_json[:100] + "...")--OUTPUT--User data preview:
{
"login": "python",
"id": 1525981,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjE1MjU5ODE=",
"avatar_url": "https://...
While response.json() is great for accessing data, sometimes you need to inspect the raw JSON structure. The built-in json library's dumps() function handles this by converting dictionaries to JSON, transforming a Python dictionary back into a formatted JSON string.
- Setting
indent=2adds whitespace to create a "pretty-printed" output that's easy for humans to read. - This technique is invaluable for debugging and understanding the layout of nested data returned by an API.
Working with authentication
import requests
from requests.auth import HTTPBasicAuth
url = "https://api.example.com/protected-resource"
response = requests.get(
url,
auth=HTTPBasicAuth('username', 'password')
# Alternative: headers={"Authorization": "Bearer your_token_here"}
)
print(f"Status: {response.status_code}, Authenticated: {response.ok}")--OUTPUT--Status: 200, Authenticated: True
Many APIs require authentication to access protected data, and the requests library offers straightforward ways to handle this.
- For basic access, you can pass a
requests.auth.HTTPBasicAuthobject with your username and password to theauthparameter. - Alternatively, many modern APIs use bearer tokens. You'd include this token in the
headersdictionary with theAuthorizationkey.
After the request, checking the response.ok attribute is a quick way to verify if your authentication succeeded.
Advanced API techniques
Now that you've handled basic requests, you'll need more advanced techniques to manage performance, organize your code, and work around API limitations.
Asynchronous API requests with aiohttp
import asyncio
import aiohttp
async def fetch_data(session, url):
async with session.get(url) as response:
return await response.json()
async def main():
urls = ["https://api.github.com/users/python", "https://api.github.com/users/django"]
async with aiohttp.ClientSession() as session:
results = await asyncio.gather(*[fetch_data(session, url) for url in urls])
for i, result in enumerate(results):
print(f"API {i+1}: {result['login']} has {result['followers']} followers")
asyncio.run(main())--OUTPUT--API 1: python has 10923 followers
API 2: django has 8754 followers
When you need to make many API calls, waiting for each one to finish is inefficient. The aiohttp library solves this by enabling asynchronous requests, so your program doesn't have to pause. It works with Python's asyncio to handle multiple network operations at once in a memory-efficient way.
- The
asyncandawaitkeywords define non-blocking functions that can run in the background. aiohttp.ClientSessionmanages a pool of connections for making requests efficiently.asyncio.gatheris the key—it launches multiple API calls concurrently and waits for them all to complete, dramatically speeding up data collection.
Creating API wrapper classes
class GithubAPI:
def __init__(self, token=None):
self.base_url = "https://api.github.com"
self.headers = {"Authorization": f"token {token}" if token else None}
def get_user(self, username):
import requests
response = requests.get(f"{self.base_url}/users/{username}", headers=self.headers)
return response.json()
api = GithubAPI()
print(f"User data: {api.get_user('python')['login']}")--OUTPUT--User data: python
An API wrapper class, like the GithubAPI example, organizes your code by bundling related API logic into a single, reusable object using techniques for creating classes in Python. This approach keeps your main script clean and makes interacting with the API much more intuitive.
- The
__init__method centralizes configuration, setting up details like the base URL and authentication headers just once. - Methods such as
get_userabstract away the request logic. You can simply callapi.get_user('python')without repeatedly building URLs or handling headers. - This structure improves maintainability—if the API changes, you only need to update the class in one place.
Handling pagination and rate limits
import requests
def get_paginated_data(url, page=1, per_page=30):
response = requests.get(f"{url}?page={page}&per_page={per_page}")
data = response.json()
remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
print(f"Items received: {len(data)}, API calls remaining: {remaining}")
return data
items = get_paginated_data("https://api.github.com/repos/python/cpython/issues")--OUTPUT--Items received: 30, API calls remaining: 59
APIs often return large result sets in chunks. This process is called pagination. The get_paginated_data function handles this by using URL parameters like page and per_page to request a specific slice of data, keeping your requests manageable.
- Most APIs also enforce rate limits, which cap how many requests you can make in a given time.
- You can track your usage by checking the response headers. The code inspects
response.headers.get('X-RateLimit-Remaining')to see how many calls you have left, which is essential for building reliable applications.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. You can move from learning individual techniques to building complete apps with Agent 4, which handles everything from writing code to connecting APIs and deployment.
Instead of piecing together code, describe the app you want to build and the Agent will take it from idea to a working product, like:
- A repository tracker that fetches and displays the most starred Python projects from GitHub, automatically handling paginated results.
- A developer comparison tool that asynchronously pulls follower and repository counts for multiple GitHub users at once.
- A user info exporter that extracts specific details like location and bio from a list of usernames and saves the data.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
You'll inevitably face challenges like request errors, timeouts, and missing data, but Python provides straightforward ways to handle them with code repair techniques.
Handling API request errors with try-except
API requests don't always succeed. You might encounter network problems or try to access a nonexistent endpoint. Without error handling, a failed requests.get() call will likely crash your application. The following code demonstrates this exact scenario with an invalid URL.
import requests
response = requests.get("https://api.nonexistent-domain.com/data")
data = response.json()
print(f"User: {data['name']}")
The requests.get() call to a nonexistent domain raises an exception, which immediately halts the script. The following code shows how to catch this error and prevent your application from crashing.
import requests
try:
response = requests.get("https://api.nonexistent-domain.com/data", timeout=5)
response.raise_for_status()
data = response.json()
print(f"User: {data['name']}")
except requests.exceptions.RequestException as e:
print(f"API request failed: {e}")
To prevent crashes, you should wrap your API calls in a try-except block using proper try and except blocks. This solution is more robust because it anticipates potential failures.
- It sets a
timeouton the request so your program doesn’t hang indefinitely waiting for a slow or nonexistent server. - The
response.raise_for_status()method actively checks for HTTP errors—like 404 or 500—and raises an exception if one occurs. - The
exceptblock catches anyrequests.exceptions.RequestException, letting you handle the failure gracefully instead of stopping the script.
Fixing timeout issues in API calls
If an API server is slow to respond, your request can hang indefinitely and freeze your application. By default, the requests library doesn't have a timeout, so it will wait forever. The code below shows this in action with a slow server.
import requests
response = requests.get("https://api.slow-server.com/data")
data = response.json()
print(f"Retrieved {len(data)} items")
The script hangs because the requests.get() call waits indefinitely for the slow server to respond. The following code demonstrates how to gracefully handle such delays and keep your application responsive.
import requests
response = requests.get("https://api.slow-server.com/data", timeout=5)
data = response.json()
print(f"Retrieved {len(data)} items")
The fix is simple: add a timeout parameter to your requests.get() call. Setting timeout=5 instructs the library to wait only five seconds for a response before giving up. If the server is too slow, a Timeout exception is raised, preventing your script from hanging. This is crucial for any API call, as network conditions and server load are often unpredictable, and it keeps your application responsive.
Dealing with missing JSON keys in API responses
APIs don't always return every piece of data you expect—a user might not have a bio, or a field could be optional. If you try to access a key that doesn't exist in the JSON response, your script will crash with a KeyError. The following code demonstrates this exact issue.
import requests
response = requests.get("https://api.github.com/users/nonexistent_user")
data = response.json()
print(f"Username: {data['login']}, Bio: {data['bio']}")
The API returns an error message for a nonexistent user, so the response object lacks a bio key. This triggers a KeyError. The following code shows how to safely handle these cases without crashing your script.
import requests
response = requests.get("https://api.github.com/users/nonexistent_user")
data = response.json()
username = data.get('login', 'Unknown')
bio = data.get('bio', 'No bio available')
print(f"Username: {username}, Bio: {bio}")
To avoid a KeyError, use the dictionary’s .get() method instead of bracket notation. It allows you to specify a default value to use if a key is missing.
- For example,
data.get('bio', 'No bio available')returns the bio if it exists, or the default string if it doesn't.
This is essential when dealing with optional API fields or error responses, ensuring your code handles incomplete data gracefully without crashing.
Real-world applications
With these robust techniques for interacting with APIs, you can now move from theory to practice and build useful, data-driven tools through vibe coding.
Creating a currency converter with exchange rate API
You can put your API skills to work by building a currency converter that pulls live data from a public exchange rate service.
import requests
amount = 100
from_currency = "USD"
to_currency = "EUR"
response = requests.get(f"https://open.er-api.com/v6/latest/{from_currency}")
rates = response.json()["rates"]
converted = amount * rates[to_currency]
print(f"{amount} {from_currency} = {converted:.2f} {to_currency}")
This script fetches live currency exchange rates from a public API. It’s a practical example of how to use variables to build a dynamic request URL, inserting the from_currency to get the right data.
- Once the
requests.get()call returns a response, the code immediately parses the JSON. - It accesses the nested
"rates"dictionary to find the specific conversion value for theto_currency. - The final converted amount is calculated by multiplying the initial
amountwith the target currency's rate.
Automating GitHub issue tracking with the API
You can also use the GitHub API to automate project management by tracking new issues in a repository.
import requests
import datetime
repo = "python/cpython"
created_after = datetime.datetime.now() - datetime.timedelta(days=7)
date_str = created_after.strftime("%Y-%m-%d")
response = requests.get(
f"https://api.github.com/repos/{repo}/issues",
params={"state": "open", "since": date_str}
)
issues = response.json()
print(f"New issues in the last week: {len(issues)}")
print(f"Latest issue: {issues[0]['title'] if issues else 'None'}")
This script combines the datetime and requests libraries to fetch time-sensitive data from the GitHub API, demonstrating techniques for working with time in Python. It dynamically calculates a date from one week ago to define a specific timeframe for the query.
- The
strftimefunction formats this date into theYYYY-MM-DDstring required by the API'ssinceparameter. - This parameter, along with
state: "open", is passed in theparamsdictionary to filter the results for open issues. - The code then displays a count of the new issues and the title of the first one returned.
Get started with Replit
Now, turn these techniques into a real tool. Describe what you want to build to Replit Agent, like “a currency converter for USD, EUR, and JPY” or “a dashboard for new GitHub issues.”
The Agent writes the code, tests for errors, and deploys your application, handling the entire development cycle 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.



