Call an API with Python requests

Port the JavaScript helper to Python, learn the two places requests can put your data, and stop opening a fresh connection for every call.

Python's requests library has been the default HTTP client for more than a decade, and for good reason: the common case is one line. That convenience hides three decisions you should make on purpose, though: where your data goes in the request, what counts as failure, and whether each call opens a new connection. This lesson makes each one explicit and ends with a helper you can drop into any script.

Everything runs on Python 3.9 or newer with requests installed:

python -m pip install requests

As in the last lesson, the key lives in an environment variable called APIVERVE_API_KEY, never in the file.

One call, read carefully

first_call.py
import os
import requests

res = requests.get(
    "https://api.apiverve.com/v1/dictionary",
    params={"word": "retry"},
    headers={"x-api-key": os.environ["APIVERVE_API_KEY"]},
    timeout=15,
)

body = res.json()
print(body["data"]["definitionCount"], body["data"]["definitions"][0])

Compare it with the JavaScript version and the differences are all in your favour:

  • params= builds and encodes the query string for you. You never write ?word= by hand, so a value with &, spaces or + in it arrives intact.
  • headers= is a plain dict. Header names are case-insensitive on the wire, so x-api-key and X-API-Key are the same header; lowercase is the convention.
  • os.environ["APIVERVE_API_KEY"] uses square brackets on purpose. If the variable is missing you get a KeyError naming it, immediately. os.environ.get(...) would hand None to requests, which drops a header whose value is None entirely, and you would be debugging an edge rejection instead of a missing variable.
  • timeout=15 is not optional in spirit. requests has no default timeout: without one, a stalled connection can hang your script indefinitely. Lesson three goes deeper; for now, always pass it.

res.json() parses the body. Unlike fetch, it is synchronous, because by the time requests.get returns, the whole body has been downloaded.

Try it liveDictionary2 credits
GEThttps://api.apiverve.com/v1/dictionary
curl "https://api.apiverve.com/v1/dictionary?word=retry" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

data.definitionCount tells you how many entries are in data.definitions.

params versus json

The question beginners ask most about requests is which keyword to put their data in. It depends on where the API expects it, and that is set by the HTTP method on the API's reference page.

KeywordWhere the data goesTypical methodContent-Type set for you
params=Query string in the URLGETnone
json=Request body, serialised as JSONPOSTapplication/json
data=Request body, form-encoded (dict) or raw (str/bytes)POSTapplication/x-www-form-urlencoded for a dict

Every API used in this course is a GET, so params= is the answer. You need json= the moment you send a body. APIVerve's batch requests are a real example: one POST that carries many inputs for the same endpoint in a requests array.

batch_emails.py
import os
import requests

res = requests.post(
    "https://api.apiverve.com/v1/emailvalidator/batch",
    json={"requests": [
        {"email": "[email protected]"},
        {"email": "not-an-email"},
    ]},
    headers={"x-api-key": os.environ["APIVERVE_API_KEY"]},
    timeout=30,
)
body = res.json()
for item in body["results"]:
    print(item["status"], item["error"])

Two mistakes show up here constantly:

  • data= with a dict when the API wants JSON. The body is sent form-encoded (requests=...), the server cannot find the field it expected, and you get a 400. If you use data=json.dumps(payload) instead, you must also set Content-Type: application/json yourself. json= does both, which is why it is the right default.
  • params= on a POST, or json= on a GET. The data is sent somewhere the server is not looking. Because undeclared parameters are dropped silently rather than rejected, a misplaced parameter can produce a successful response to a different question.
Check yourself

An endpoint is documented as POST with a JSON body. Which call sends the body correctly?

Answer the check above to continue.

Run every example on your own key. Free plan, no card.

Scaling up?

Volume pricing, custom SLAs, and dedicated support for high-traffic teams.

Contact sales