Back to Blog
Python

Python httpx GET and POST with Headers, Parameters, and JSON

python httpx get post headers parameters and json requests: Learn how to make GET and POST requests with httpx, including headers, query parameters, and JSON bodies, w...

httpxpython-http-clientjson-requestsquery-parametersapi-integration
Illustration of a Python HTTP client sending GET and POST requests with headers, query parameters, and JSON payloads.

When you need to interact with an HTTP API from Python, httpx offers a modern, feature-rich client that handles GET and POST requests cleanly. This article focuses on the practical aspects of python httpx get post headers parameters and json requests — specifically how to set headers, pass query parameters, send JSON payloads, and interpret responses correctly.

Making a GET Request with Headers and Query Parameters

A GET request often needs custom headers (e.g., authentication tokens) and query parameters to filter or paginate results. httpx accepts both through dedicated arguments.

import httpx headers = { "Authorization": "Bearer your-token", "Accept": "application/json", } params = { "page": 2, "limit": 50, "status": "active", } response = httpx.get( "https://api.example.com/users", headers=headers, params=params, ) print(response.status_code) print(response.json())

The params argument serializes the dictionary into a query string automatically. httpx handles URL encoding, so values with spaces or special characters are escaped correctly. If you need to send the same parameter multiple times, pass a list of values instead of a single string.

params = {"id": [1, 2, 3]} # Results in ?id=1&id=2&id=3

Headers are passed as a plain dictionary. Keys are case-insensitive, but httpx preserves the casing you provide when sending the request.

Sending a POST Request with a JSON Body

POST requests typically carry a JSON payload. httpx provides the json argument, which automatically serializes a Python dictionary to JSON and sets the Content-Type header to application/json.

import httpx payload = { "name": "Ada Lovelace", "role": "analyst", "active": True, } response = httpx.post( "https://api.example.com/users", json=payload, headers={"Authorization": "Bearer your-token"}, ) print(response.status_code) print(response.json())

Using json= is the most straightforward way for JSON APIs. It avoids manual serialization and header management. The request body is encoded to UTF-8, and the Content-Type header is set for you.

Choosing Between params, data, and json

httpx distinguishes between three ways to send request data. Understanding the difference prevents subtle bugs in API integrations.

ArgumentUse caseContent-TypeExample body
paramsQuery string in the URLNone (appended to URL)?page=2&limit=50
dataForm-encoded or raw bytesapplication/x-www-form-urlencoded (default)name=Ada&role=analyst
jsonJSON payloadapplication/json{"name":"Ada"}

Use params for filtering and pagination that belong in the URL. Use json when the API expects a JSON object. Use data when you need to send form fields or raw bytes, such as when uploading a file or mimicking a browser form submission.

# Form-encoded POST response = httpx.post("https://httpbin.org/post", data={"key": "value"}) # Raw bytes POST with open("file.bin", "rb") as f: response = httpx.post("https://upload.example.com", content=f.read())

If you pass both data and json, httpx raises an error because the two are mutually exclusive. The same applies to combining params with a URL that already contains a query string — httpx merges them, but explicit params is clearer.

Reading Response Content and Status Codes

After a request completes, the Response object gives you access to the status code, headers, and body. The most common pattern is to check the status before parsing the body.

response = httpx.get("https://api.example.com/status") if response.status_code == 200: data = response.json() else: print(f"Request failed: {response.status_code}")

response.json() parses the body as JSON. If the body is not valid JSON, it raises json.JSONDecodeError. For non-JSON responses, use response.text for a string or response.content for raw bytes.

httpx also exposes response.headers, a case-insensitive mapping of response headers. This is useful when the API returns pagination links or rate-limit information.

rate_limit_remaining = response.headers.get("X-RateLimit-Remaining")

Handling Errors, Timeouts, and Retries

Network requests fail for many reasons: timekyouts, connection errors, or invalid status codes. httpx`` raises httpx.RequestErrorfor network-level failures andhttpx.HTTPStatusErrorfor 4xx/5xx responses when you callresponse.raise_for_status()`.

import httpx url = "https://api.example.com/data" try: response = httpx.get( url, timeout=5.0, headers={"Authorization": "Bearer token"}, ) response.raise_for_status() except httpx.TimeoutException: print("The request timed out.") except httpx.HTTPStatusError as exc: print(f"HTTP error: {exc.response.status_code}") except httpx.RequestError as exc: print(f"Network error: {exc}")

Set a timeout on every request to avoid hanging indefinitely. httpx supports separate timeouts for connect, read, write, and pool operations via a Timeout object, but a single float is sufficient for most scripts.

timeout = httpx.Timeout(5.0, connect=2.0) response = httpx.get(url, timeout=timeout)

For retries, httpx does not include built-in retry logic. You can implement a simple loop with time.sleep or use a third-party library like tenacity. Keep in mind that retrying a POST request can duplicate side effects unless the API is idempotent.

Reusing Clients for Connection Pooling and Performance

Creating a new httpx.Client for every request prevents connection reuse and repeats TLS handshakes. For scripts that make multiple requests to the same host, reuse a single client instance.

import httpx with httpx.Client( base_url="https://api.example.com", headers={"Authorization": "Bearer token"}, timeout=10.0, ) as client: response1 = client.get("/users", params={"page": 1}) response2 = client.post("/users", json={"name": "Grace"})

A Client maintains a connection pool, reuses TCP connections, and applies default headers and timeouts to every request. This reduces latency and CPU usage when you issue many requests in a loop or a batch job.

The base_url argument lets you use relative paths, which keeps code concise and avoids repeating the origin. If you need different headers for a specific request, pass them at the call site; they are merged with the client defaults.

Async Requests with httpx

For concurrent workloads, httpx provides an async API that mirrors the sync interface. Use httpx.AsyncClient with await inside an async function.

import httpx import asyncio async def fetch_status(url: str) -> int: async with httpx.AsyncClient() as client: response = await client.get(url) return response.status_code async def main(): urls = ["https://example.com", "https://httpbin.org/get"] results = await asyncio.gather(*(fetch_status(u) for u in urls)) print(results) asyncio.run(main())

Async requests are useful when you need to issue many independent requests concurrently, such as checking multiple health endpoints or fetching data from several services. The async client also supports connection pooling, but it requires an event loop and is not a drop-in replacement for sync code in a regular script.

Where Configuration Commonly Breaks

A frequent mistake is mixing params with a URL that already contains a query string. httpx merges them, but if the same key appears in both, the params value takes precedence. This can lead to unexpected behavior when the URL is generated dynamically.

Another issue is sending a dict with data= when the API expects JSON. This results in a form-encoded body, and the server may reject it or parse it incorrectly. Always match the argument to the API's expected content type.

Finally, remember that response.json() does not verify the status code. A 404 response might return a JSON error object, and your code could process it as if it were successful data. Always check response.status_code or call raise_for_status() before parsing the body.

These patterns cover the core of python httpx get post headers parameters and json requests. By using the correct arguments and understanding response behavior, you can build reliable HTTP clients for any API integration.

python httpx get post headers parameters and json requests: | RYUSLOG DEV