Use Requests’ json= parameter: pass a Python dictionary or list, set a finite timeout, check the HTTP status, then parse the response. A reliable minimal pattern is requests.post(url, json=payload, timeout=10) followed by response.raise_for_status() and response.json().
The recommended pattern
For an API that expects a JSON request body, pass the Python object through json=. Requests serializes the object for you and uses the JSON request workflow.
import requests
url = "https://api.example.com/items"
payload = {"name": "Alice", "active": True}
response = requests.post(url, json=payload, timeout=10)
response.raise_for_status()
result = response.json()
print(result)
This example does four separate jobs:
payloadis a normal JSON-serializable Python object.json=payloadcreates the request body using JSON encoding.timeout=10prevents the call from waiting indefinitely.raise_for_status()rejects unsuccessful HTTP responses before the response is treated as a success.
The current Requests documentation identifies version 2.34.2 and lists official support for Python 3.10 and newer (documentation accessed in 2026). Check your own environment if you maintain an older Python runtime or a pinned dependency set.
json= versus data= and files=
These parameters describe different body formats. Choosing the wrong one is a common reason an API reports a missing field or an unsupported content type.
#1 Best Overall
| Goal | Requests call | What is sent |
|---|---|---|
| JSON API body | requests.post(url, json=payload) |
Requests serializes the object through its JSON workflow. |
| Form submission | requests.post(url, data=form_data) |
A dictionary is form-encoded. |
| Multipart upload | requests.post(url, files=files) |
Requests builds a multipart body for files and fields. |
| Already serialized body | requests.post(url, data=json_text) |
You provide the exact text and are responsible for the appropriate headers. |
Do not provide competing body mechanisms accidentally. Requests ignores json= when either data or files is supplied. If you pass both while expecting JSON, the value in json= will not be used.
Why manual json.dumps() can produce the wrong header
This code serializes the object yourself:
import json
import requests
payload = {"name": "Alice", "active": True}
json_text = json.dumps(payload)
response = requests.post(
"https://api.example.com/items",
data=json_text,
timeout=10,
)
The body is now a string, but this form does not add Content-Type: application/json automatically. An API that uses the content type to select its parser may reject it or interpret it as an unknown body format.
If you deliberately pre-serialize the body, set the header yourself:
response = requests.post(
"https://api.example.com/items",
data=json_text,
headers={"Content-Type": "application/json"},
timeout=10,
)
For ordinary JSON APIs, json=payload is shorter and avoids this header trap. Use manual serialization only when you need control over the exact serialized text or are integrating with a service that requires a special encoding process.
Free tools Windows power users keep installed
One-click scans. No signup required.
Building a production-ready JSON POST
Send authentication and other headers
Authentication is independent of JSON encoding. Add the headers required by the API while keeping the body in json=.
Rank #2
import requests
url = "https://api.example.com/items"
payload = {"name": "Alice", "active": True}
headers = {
"Authorization": "Bearer YOUR_TOKEN",
"Accept": "application/json",
}
response = requests.post(
url,
json=payload,
headers=headers,
timeout=10,
)
response.raise_for_status()
print(response.json())
Do not put a secret token inside the JSON object unless the API explicitly defines it as a body field. Keep credentials in environment variables or your application’s secret store.
Use a session for repeated calls
A Session lets related requests share configuration such as headers and authentication.
import os
import requests
payload = {"name": "Alice", "active": True}
headers = {
"Authorization": f"Bearer {os.environ['API_TOKEN']}",
"Accept": "application/json",
}
with requests.Session() as session:
session.headers.update(headers)
response = session.post(
"https://api.example.com/items",
json=payload,
timeout=10,
)
response.raise_for_status()
result = response.json()
print(result)
Send nested objects and arrays
JSON can contain nested dictionaries and lists as long as every value is JSON serializable.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemspayload = {
"customer": {
"name": "Alice",
"contact": {"email": "alice@example.com"},
},
"tags": ["new", "priority"],
"active": True,
}
response = requests.post(
"https://api.example.com/customers",
json=payload,
timeout=10,
)
response.raise_for_status()
Python booleans and None are converted to JSON true, false, and null. Values such as an open file object, a set, or a custom class are not automatically JSON serializable; convert them to strings, lists, numbers, dictionaries, or null-compatible values first.
Checking whether the API call succeeded
Check HTTP status before treating the result as success
A server can return a JSON error document with an unsuccessful HTTP status. Parsing that document does not make the request successful.
response = requests.post(
"https://api.example.com/items",
json={"name": "Alice"},
timeout=10,
)
response.raise_for_status()
result = response.json()
raise_for_status() raises a Requests HTTP error for unsuccessful responses. If you need custom handling, inspect response.status_code instead:
if 200 <= response.status_code < 300:
print("Request succeeded")
else:
print("Request failed:", response.status_code, response.text)
Use the status code defined by the API contract. A successful creation is often represented by a 2xx status, but the exact code and response fields belong to the service you are calling.
Parse JSON defensively
response.json() decodes a JSON response. It raises requests.exceptions.JSONDecodeError when the response is not valid JSON, including a response with no body such as HTTP 204 No Content.
import requests
try:
response = requests.post(
"https://api.example.com/items",
json={"name": "Alice"},
timeout=10,
)
response.raise_for_status()
except requests.exceptions.Timeout:
print("The server did not respond before the timeout")
except requests.exceptions.RequestException as exc:
print(f"HTTP or network failure: {exc}")
else:
if response.status_code == 204 or not response.content:
result = None
else:
try:
result = response.json()
except requests.exceptions.JSONDecodeError:
print("The server returned a non-JSON success response")
print(response.text)
else:
print(result)
Only parse JSON when the endpoint promises a JSON response and the response has content. For diagnostics, response.text gives decoded text and response.content gives the raw response bytes.
Timeouts, retries and safe failure handling
Always choose a finite timeout appropriate to the API. A timeout is not a guarantee that the server stopped processing your request; it only limits how long your client waits. For a POST that may create a record, retrying blindly can create duplicates if the first request reached the server but the response was lost.
- Use an idempotency key when the API supports one for create or payment operations.
- Retry only when the API documents a safe policy and the failure is plausibly transient.
- Log the status code and a request identifier, but never log access tokens or sensitive payload fields.
- Keep the original payload available when you need to inspect or replay a failed request.
Requests exposes timeout and other request options directly. Set the timeout on every call or on a wrapper used by your application rather than relying on an indefinite default.
Recommended Free Tools
Equivalent requests with cURL and Node.js
When debugging an API, reproducing the same JSON body in another client helps distinguish a Python issue from a server or credential issue.
cURL
curl -X POST "https://api.example.com/items"
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer YOUR_TOKEN"
--data '{"name":"Alice","active":true}'
Node.js using built-in fetch
const payload = { name: 'Alice', active: true };
const res = await fetch('https://api.example.com/items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN'
},
body: JSON.stringify(payload)
});
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${await res.text()}`);
}
const result = res.status === 204 ? null : await res.json();
console.log(result);
Unlike Requests’ json= convenience parameter, JavaScript’s fetch requires you to call JSON.stringify() and set the content type explicitly.
Common errors and fixes
“The API says the body is missing”
Confirm that you used json=payload, not a misspelled argument, and that another data or files argument is not overriding it. Print a non-secret representation of the payload before sending.
“Unsupported media type” or “expected application/json”
You may have supplied a serialized string through data= without a content-type header. Switch to json=payload, or add Content-Type: application/json when manual serialization is intentional.
Best Value
JSONDecodeError after a successful status
The response may be empty, may be HTTP 204, or may contain HTML or plain text rather than JSON. Check response.status_code, response.headers, and response.text before calling response.json().
TypeError: Object of type ... is not JSON serializable
Convert the unsupported value to a JSON-compatible representation. For example, convert a set to a list and a date or decimal to the string or number format required by the API.
The request hangs or fails intermittently
Add a finite timeout, verify DNS and network access, and inspect whether the failure is a connection error or an HTTP error. A timeout exception means the client did not receive a response within the configured interval; it does not identify whether the server completed the operation.
The server returns 401, 403 or 422
- 401: check the token, its expiration and the required authentication scheme.
- 403: the identity may be valid but lack permission, or the API may restrict the resource.
- 422: the JSON was understood but failed validation; inspect the error fields and compare names, types and required values with the API contract.
Or skip the browser setup
If the task behind your integration is obtaining a clean screenshot of a website rather than implementing browser automation yourself, ScreenshotNeo provides a single HTTP request. Its API accepts a URL and returns PNG, JPEG, WebP or PDF output. The request can be made from Python, cURL or Node.js just like any other HTTP endpoint.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Before capture, ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; other listed plans are Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000) and Business ($249 for 1,000,000). Yearly billing gives two months free, and every feature is available on every plan. See the ScreenshotNeo API documentation for request options and response behavior, then sign up for the free plan.
Request checklist
- Confirm the endpoint expects JSON rather than a form or multipart body.
- Pass a dictionary or list with
json=payload. - Do not combine
json=withdata=orfiles=unintentionally. - Set authentication and any required headers separately.
- Use a finite timeout.
- Call
raise_for_status()or handlestatus_codebefore parsing. - Handle empty or non-JSON responses before calling
response.json(). - Protect secrets and avoid unsafe automatic retries for non-idempotent operations.
Frequently Asked Questions
Can a JSON POST return no body even when it succeeds?
Yes. An API may report success with HTTP 204 No Content. Check the status and response content before calling response.json().
What should I preserve when investigating a failed request?
Keep the endpoint, status code, non-sensitive response text, timeout setting and a redacted copy of the payload. Never include access tokens or confidential fields in logs.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

