On this page
Introduction
HTTPX is a powerful and user-friendly HTTP client that allows developers to make HTTP requests with ease.
It offers support for asynchronous programming, HTTP/2, and connection pooling.
Getting Started
Clone the httpx-basic project:
git clone https://gitlab.com/xtec/python/httpx-basic.gitStart the FastAPI server:
cd httpx-basicuv run app/main.pyOpen the client.py file that is on the project root:
import json
import httpx
def main():
response = httpx.get('http://localhost:8000/person/1') data = response.json() print(json.dumps(data, indent=4))
if __name__ == "__main__": main()This script imports the httpx package and makes a GET request to the endpoint http://localhost:8000/person/1
Run this script; you should observe output similar to the following:
{ "id": 1, "name": "David", "surname": "de Mingo"}The server answers with a JSON object containing the data of person with id 1.
Response
When you make an HTTP request using httpx, the response object contains important information about the server’s reply. This includes the status code, headers, and response body, which help determine whether the request was successful and how to process the returned data.
Modify your client.py file to inspect different parts of the response:
import httpx
def main(): response = httpx.get('http://localhost:8000/person/1') print(f"Status Code: {response.status_code}") print(f"Headers: {response.headers}") print(f"Content: {response.text}")
if __name__ == "__main__": main()This script displays important response details. Here’s what each part of the response object represents:
status_code– Indicates the HTTP status of the request (e.g.,200for success,404for not found).headers– A dictionary containing metadata about the response, such as content type, server information, and caching policies.text– The full response body returned as a string.json()– Parses the response body as JSON, converting it into a Python dictionary for easy data manipulation.
After running the script, you should see something like:
Status Code: 200Headers: Headers({'content-length': '44', 'content-type': 'application/json', 'server': 'granian', 'date': 'Thu, 01 Jan 2026 16:37:25 GMT'})Content: {"name":"David","surname":"de Mingo","id":1}Status Code
The first thing to do when receiving a response is to check the status code to make sure the request was successful before processing the data.
If you try to get the person with id 10, the server will respond with a status_code of 404, which is the code for “Not Found”.
Write a code that handles the case when the person is not found (status code 404) and prints an appropriate message.
response = httpx.get('http://localhost:8000/person/3')if response.status_code != 200: print(f"{response.status_code} Person not found")else: data = response.json() print(json.dumps(data, indent=4))Headers
HTTP headers provide additional information about the response, such as the content type or the server that sent the response.
Headers({'content-length': '44', 'content-type': 'application/json', 'server': 'granian', 'date': 'Thu, 01 Jan 2026 16:37:25 GMT'})Understanding basic request methods
Now that you understand how HTTPX handles responses, let’s explore different types of HTTP requests beyond GET.
HTTPX supports a variety of methods, including POST, PUT, DELETE, and query parameters with GET, allowing you to interact with APIs that require data submission or modifications.
POST
A POST request is typically used to send data to a server, such as creating a new resource or submitting a form.
Remove all contents from client.py and replace it with the following code to send a POST request with JSON data:
response = httpx.post('http://localhost:8000/person', json={'name': 'Susan', 'surname': 'Sarandon'})print(response.status_code)print(json.dumps(response.json(), indent=4))Run the script and observe the output:
201{ "name": "Susan", "surname": "Sarandon", "id": 3}The server responds with a 201 status code, indicating that the request was successful and a new resource was created.
The response body contains the details of the newly created person, including their assigned ID.
Write a script that gets person with id 3 and prints the person’s name.‘
response = httpx.get('http://localhost:8000/person/3')data = response.json()print(data['name'])PUT
While POST is used to create new resources, a PUT request is used to update existing ones.
When you need to modify an entry rather than add a new one, PUT ensures that the existing data is replaced with the updated values.
response = httpx.put('http://localhost:8000/person/3', json={'id': 3, 'name': 'Susan', 'surname': 'Ferguson'})data = response.json()print(json.dumps(data, indent=4))This request updates an existing resource with new data, ensuring changes are correctly reflected on the server.
DELETE
If you need to remove a resource instead, a DELETE request is used.
This is particularly useful for deleting records from a database or removing items from an API:
response = httpx.delete('http://localhost:8000/person/3')print(response.status_code)The first time you run this script, a response status code of 204 indicates that the resource was successfully deleted.
If you try to delete a resource that doesn’t exist, the server responds with a 404 status code, indicating that the resource was not found.
Run the script again to see this behavior.
Handling query parameters in a GET request
You can request all persons from the server by sending a GET request without any parameters:
response = httpx.get('http://localhost:8000/person')print(json.dumps(response.json(), indent=2))[ { "name": "David", "surname": "de Mingo", "id": 1 }, { "name": "Roser", "surname": "Salietti", "id": 2 }]The server responds with a list of all persons, and it can be a very long list.
Query parameters allow you to filter the results returned by the API based on specific criteria.
To filter persons by name, you can send a GET request with the name parameter set to the desired value.
Modify client.py to send a GET request with a query parameter name=Roser to filter the results to only include persons with the name “Roser”.
response = httpx.get('http://localhost:8000/person', params={'name': 'Roser'})print(json.dumps(response.json(), indent=2))The response contains a list of persons with the name “Roser”.
[ { "name": "Roser", "surname": "Salietti", "id": 2 }]The ? character is used to separate the base URL from the query parameters.
Binary
The response content can also be accessed as bytes, for non-text responses:
r.contentFor example, to save this image from binary data returned by a request,

you can use the following code:
file = "donald-duck.png"
r = httpx.get(f"https://xtec.dev/python/httpx/basic/{file}")with open(file, 'wb') as file: print(r.status_code) if r.status_code == 200: file.write(r.content)And with the pillow library you can open the image directly from the response content:
from io import BytesIOfrom PIL import Image
file = "donald-duck.png"
r = httpx.get(f"https://xtec.dev/python/httpx/basic/{file}")if r.status_code == 200: i = Image.open(BytesIO(r.content)) print(i.format) print(i.size) i.show()The i.show() command in main.py is executed, but the image is not displayed to the user. This is often because PIL’s show() method relies on external image viewers, which may not be configured or available in some environments.
Download and reduce the image:
file = "donald-duck.png"
r = httpx.get(f"https://xtec.dev/python/httpx/basic/{file}")if r.status_code == 200: i = Image.open(BytesIO(r.content)) i = i.reduce(2) i.show()Any gzip and deflate HTTP response encodings will automatically be decoded for you.
If brotlipy is installed, then the brotli response encoding will be supported.
If zstandard is installed, then zstd response encodings will also be supported.
PokéAPI
PokéAPI has all Pokémon data you’ll ever need in one place, easily accessible through a modern free open-source RESTful API.
To get data about the Pokémon Charmeleon, you can make a GET request to the following endpoint: https://pokeapi.co/api/v2/pokemon/charmeleon
Get all abilities of Charmeleon.
import json
import httpx
response = httpx.get("https://pokeapi.co/api/v2/pokemon/charmeleon")data = response.json()# print(json.dumps(data["abilities"], indent=4))
abilities = [item["ability"]["name"] for item in data["abilities"]]print(abilities)Build a mini Pokédex that shows a Pokémon’s name, image, and base stats when searched.
OpenWeatherMap
Weather API allows you to get real-time weather data from anywhere in the world.
Create an account in https://openweathermap.org
After you log in, you will get the API key, and within the next couple of hours, it will be activated and ready to use.
You can get the current weather data for any city in the world by sending a GET request to the following endpoint: api.openweathermap.org/data/2.5/weather?q=London,uk&APPID=bec22f999bee89918a7bad44194ca151
Get the current temperature for a given city.
import httpx
API_KEY = "cd35702dc899be040b23662bf856adce"
#city = input("Enter city: ")city = "Barcelona"
response = httpx.get(f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid=bec22f999bee89918a7bad44194ca151&units=metric")if response.status_code != 200: print("Error: ", response.json()["message"])else: data = response.json()
print('Temperature:', data['main']['temp'], '°C') # ...