Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To call an HTTP API from Python, choose the endpoint and method documented by its provider, add the required parameters, headers, body, and authentication, send the request with a finite timeout, then check the HTTP status before parsing the response. The requests library is the most concise general-purpose option; Python’s built-in urllib.request avoids a third-party dependency.

What an API request contains

An API interaction is a request followed by a response. The provider’s documentation is authoritative for the URL, HTTP method, parameter names, authentication scheme, request body, response format, pagination, quotas, and error behavior. No single header or login method works for every API.

  • URL: the resource or operation endpoint, often including a version such as /v1/.
  • Method: commonly GET, POST, PUT, or DELETE; use exactly what the endpoint specifies.
  • Query parameters: filters, limits, search terms, or pagination values appended to a GET request.
  • Headers: metadata such as Accept, Content-Type, authorization, correlation IDs, or conditional-request values.
  • Body: data sent with methods such as POST or PUT, often as JSON.
  • Authentication: an API key, bearer token, Basic or Digest credentials, OAuth flow, signed request, or another provider-specific scheme.

HTTP status classes provide the first result signal: 2xx indicates success, 3xx redirection, 4xx a client-side problem, and 5xx a server-side problem. A response can contain valid JSON and still represent an error, so decoding JSON is not a substitute for checking status.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose a Python HTTP client

Requests for concise application code

Install the third-party client in the environment that runs your program:

python -m pip install requests

Requests supplies method functions, query parameters, JSON bodies, headers, authentication helpers, sessions, connection pooling, and exceptions in a compact interface. Its current documentation identifies version 2.34.2 and official support for Python 3.10 and later; verify compatibility against the version you install because this is version-sensitive.

urllib.request when dependencies are not acceptable

urllib.request is included with Python and provides URL opening plus common facilities such as redirects, cookies, authentication, and proxies. It is more verbose, but useful for small utilities, restricted build environments, or examples that must use only the standard library.

Decision Prefer Requests Prefer urllib.request
Dependency policy A third-party package is permitted Only the standard library is permitted
Code style Short method calls and straightforward JSON handling More explicit request and response objects
Repeated calls Sessions and pooled connections are convenient Possible, but requires more manual setup
Authentication helpers Built-in Basic and Digest support; OAuth commonly uses requests-oauthlib Build the required headers or handlers yourself

No universal performance winner is established here. Select the client your deployment and team can maintain.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Send a first request with Requests

GET with query parameters

Replace the example URL and parameter names with those in your provider’s documentation. Keep a finite timeout so a stalled connection cannot wait forever.

import requests

url = "https://api.example.com/v1/items"

try:
    response = requests.get(
        url,
        params={"limit": 10},
        headers={"Accept": "application/json"},
        timeout=10,
    )
    response.raise_for_status()
    data = response.json()
except requests.exceptions.Timeout:
    print("The API request timed out")
except requests.exceptions.HTTPError as exc:
    print(f"The API returned an unsuccessful HTTP status: {exc}")
except requests.exceptions.RequestException as exc:
    print(f"The request failed: {exc}")
except requests.exceptions.JSONDecodeError:
    print("The response body was not valid JSON")
else:
    print(data)

params is URL-encoded by Requests. raise_for_status() raises an HTTP error for an unsuccessful status; only after it succeeds should this example decode JSON. Depending on the installed Requests version and Python runtime, the JSON decoding exception may be exposed through requests.exceptions.JSONDecodeError or the underlying JSON library’s decoding exception; catch the documented exception for your environment if you need portable handling.

POST a JSON body

payload = {"name": "Ada", "role": "reader"}

response = requests.post(
    "https://api.example.com/v1/users",
    json=payload,
    headers={"Accept": "application/json"},
    timeout=10,
)
response.raise_for_status()
created = response.json()
print(created)

Use json= for a JSON request body; Requests serializes it and sets the appropriate content type. Use data= only when the API explicitly expects form data, bytes, or another encoding.

Authenticate without exposing secrets

Read the authentication section for the exact header or flow. A bearer-token API commonly expects a header like this, but do not assume it unless the provider says so:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import os
import requests

token = os.environ["EXAMPLE_API_TOKEN"]
response = requests.get(
    "https://api.example.com/v1/profile",
    headers={
        "Authorization": f"Bearer {token}",
        "Accept": "application/json",
    },
    timeout=10,
)
response.raise_for_status()
print(response.json())

Load credentials from an environment or deployment secret store and never commit real keys to source control, print them, or include them in URLs unless the API specifically requires that form. Requests documents Basic and Digest authentication through auth; OAuth integrations commonly use requests-oauthlib. API-key placement varies: it might be a header, query parameter, or signed value.

response = requests.get(
    "https://api.example.com/v1/account",
    auth=(os.environ["API_USER"], os.environ["API_PASSWORD"]),
    timeout=10,
)
response.raise_for_status()

Use methods according to their semantics

GET requests a current representation, POST asks the server to process supplied content, PUT is intended to replace a target representation, and DELETE requests removal. An individual API can specialize these meanings, so its endpoint contract wins.

Safe and idempotent are different properties. GET, HEAD, OPTIONS, and TRACE are safe. Safe methods, PUT, and DELETE are idempotent in the HTTP specification: repeating the same request is intended to have the same effect, although logging and other incidental effects can still occur. A POST that creates a record or triggers a payment is normally not idempotent. Do not blindly retry it after a lost connection: the server may have completed the operation even though the response never reached your program. Use a provider-supported idempotency key or an operation-status lookup when available.

Inspect status, headers, and body when debugging

When a call fails, record enough diagnostic context without logging credentials or sensitive payloads:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Confirm the URL, API version, HTTP method, and URL encoding.
  2. Check required headers, content type, authorization format, and body schema.
  3. Read the status code and response headers, including rate-limit or request-ID headers supplied by the service.
  4. Inspect the response body for structured error details; a 4xx or 5xx body can still be valid JSON.
  5. Check whether the server returned no content before calling response.json().
response = requests.get("https://api.example.com/v1/items", timeout=10)
print(response.status_code)
print(dict(response.headers))
print(response.text[:1000])

Use response.json() only when the documented success or error format is JSON and the body is present. Empty or invalid content raises a decoding error.

Reuse connections with a Session

A Requests Session persists cookies and can reuse pooled connections across calls. That is useful for APIs that require a login cookie, shared headers, or many requests to one host.

import requests

with requests.Session() as session:
    session.headers.update({"Accept": "application/json"})
    session.headers["Authorization"] = "Bearer " + token

    first = session.get("https://api.example.com/v1/items", timeout=10)
    first.raise_for_status()
    second = session.get("https://api.example.com/v1/items/next", timeout=10)
    second.raise_for_status()
    print(first.json(), second.json())

A session does not make unsafe retries safe and does not replace a timeout on each request.

Standard-library alternative with urllib.request

import json
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError

params = urlencode({"limit": 10})
request = Request(
    f"https://api.example.com/v1/items?{params}",
    headers={"Accept": "application/json"},
    method="GET",
)

try:
    with urlopen(request, timeout=10) as response:
        status = response.status
        body = response.read()
        if not 200 <= status < 300:
            raise RuntimeError(f"Unexpected HTTP status: {status}")
        data = json.loads(body)
        print(data)
except HTTPError as exc:
    print(f"HTTP error {exc.code}: {exc.read().decode(errors='replace')}")
except URLError as exc:
    print(f"Network error: {exc.reason}")

For a JSON POST, encode the payload with json.dumps(payload).encode("utf-8"), pass it as data, and add Content-Type: application/json.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle pagination, rate limits, and retries carefully

Pagination is not standardized across providers. Documentation may require page numbers, offsets, cursors, continuation tokens, or a URL supplied in a response link. Follow that API’s stopping condition instead of assuming that page=2 exists.

Finite timeouts protect availability, but they do not tell you whether the server applied a request. For transient connection failures or selected 5xx responses, a bounded backoff can be appropriate for safe or idempotent operations. Do not automatically repeat a non-idempotent operation without a reliable safety mechanism. Respect documented rate limits and any Retry-After value rather than generating a faster failure loop.

Common failures and fixes

401 or 403

Usually the token is missing, expired, scoped incorrectly, or sent in the wrong location. Compare the exact authorization scheme, account permissions, host, and API version with the provider’s documentation.

400 or 422

The request reached the service but failed validation. Check parameter spelling and types, required fields, JSON structure, date formats, and whether a value belongs in the query string rather than the body.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

404

Verify the base URL, version, path, resource identifier, and whether the API uses a different region or environment.

429

You exceeded a quota or rate limit. Slow down, honor server guidance, and use the provider’s pagination and batching facilities. Do not treat retries as free capacity.

Timeout, connection, or TLS errors

Check DNS, proxy and firewall settings, certificate trust, hostname spelling, and service availability. Increase the timeout only when the operation legitimately needs more time; keep it finite and separate connect/read limits if your client configuration requires that distinction.

JSON decoding error

Print the status, content type, and a safe excerpt of the body. The endpoint may have returned HTML, an empty 204 response, plain text, or a JSON error with an unexpected schema.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your API workflow needs screenshots of web pages, ScreenshotNeo provides a direct HTTP endpoint rather than requiring you to install and control a browser. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and whether it was billed. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Python example (see the ScreenshotNeo API documentation):

import requests

r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

The equivalent cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Node.js works too:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account.

FAQ

Should I use GET or POST for a search?

Use the method specified by that endpoint. Many read-only searches use GET with query parameters, but some APIs deliberately define POST searches for complex filters or privacy reasons.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can a successful HTTP status still contain an application error?

Yes. Some services return a 2xx response with an error field in the JSON. After checking status, validate the response schema and any provider-specific success indicator.

How do I know whether a retry duplicated an operation?

A client-side timeout cannot prove whether the server applied the request. Use an idempotency key or query the operation’s status when the API offers one; otherwise, avoid an automatic retry for a non-idempotent action.

Frequently Asked Questions

Should I use GET or POST for a search?

Use the method specified by that endpoint. Many read-only searches use GET with query parameters, but some APIs deliberately define POST searches for complex filters or privacy reasons.

Can a successful HTTP status still contain an application error?

Yes. Some services return a 2xx response with an error field in the JSON. After checking status, validate the response schema and any provider-specific success indicator.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I know whether a retry duplicated an operation?

A client-side timeout cannot prove whether the server applied the request. Use an idempotency key or query the operation’s status when the API offers one; otherwise, avoid an automatic retry for a non-idempotent action.

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.