Skip to content

Python (requests)

Calls with the requests library, JSON bodies, query filtering, and dataclass parsing.

The requests library is the standard HTTP client for Python. It hides the parts of HTTP you do not want to think about (connection pooling, charset negotiation, JSON encoding) and exposes the parts you do. This guide covers the patterns you use when calling the GoRest API from a script, a Django view, a Celery task, or a Jupyter notebook.

Install

pip install requests

If you control a long-running process (a worker, a server), open exactly one requests.Session and reuse it. Sessions hold an HTTP connection pool and let you set defaults like the bearer token once.

import os, time, requests

API = "https://gorest.co.in/public/v2"

s = requests.Session()
s.headers.update({
    "Authorization": f"Bearer {os.environ['GOREST_TOKEN']}",
    "Accept": "application/json",
    "Content-Type": "application/json",
})

List users

Pass filters as a dict to the params argument and requests will URL-encode them. The response is plain JSON, so .json() gives back a Python list of dicts directly.

r = s.get(f"{API}/users", params={"status": "active", "page": 1}, timeout=10)
r.raise_for_status()
users = r.json()
print(len(users), "users on this page")

Always pass timeout; the default is unbounded, and a slow upstream becomes your slow upstream. 10 seconds is a sane default.

Fetch a single user

Handle 404 in your code. The id either never existed, or it belongs to a different access token, in which case it is the same as not existing for you.

def get_user(user_id):
    r = s.get(f"{API}/users/{user_id}", timeout=10)
    if r.status_code == 404:
        return None
    r.raise_for_status()
    return r.json()

Create, update, delete

Use the json= keyword on requests; it serializes the dict to JSON and sets the Content-Type header for you (overriding the session default if needed). 201 is success for create, 200 for partial update, 204 for delete.

# Create
payload = {
    "name": "Pranav Kapoor",
    "email": f"pranav-{int(time.time())}@example.com",
    "gender": "male",
    "status": "active",
}
r = s.post(f"{API}/users", json=payload, timeout=10)
r.raise_for_status()
created = r.json()

# Partial update
r = s.patch(f"{API}/users/{created['id']}",
            json={"status": "inactive"}, timeout=10)
r.raise_for_status()

# Delete
r = s.delete(f"{API}/users/{created['id']}", timeout=10)
assert r.status_code == 204

Validation errors come back as 422 with a list of {"field": "...", "message": "..."} dicts. Treat them as user-facing errors, not server errors:

r = s.post(f"{API}/users", json={"name": "x"}, timeout=10)
if r.status_code == 422:
    for err in r.json():
        print(f"{err['field']}: {err['message']}")

Pagination

List endpoints give page metadata in response headers, not the body. The header you actually need is X-Pagination-Pages; it tells you how many pages exist for the filter you sent. A generator is the cleanest way to go through everything:

def page_users(**filters):
    page = 1
    while True:
        r = s.get(f"{API}/users",
                  params={**filters, "page": page},
                  timeout=10)
        r.raise_for_status()
        batch = r.json()
        if not batch:
            return
        for user in batch:
            yield user
        total = int(r.headers.get("X-Pagination-Pages", 1))
        if page >= total:
            return
        page += 1

for u in page_users(status="active"):
    print(u["email"])

Retry on rate limits

Hitting 429 means your token has used its budget for the minute. The X-RateLimit-Reset header is in seconds; sleep that long and try again. Wrap the session in a small retry helper so callers do not have to think about it:

def request_with_retry(method, url, *, max_attempts=3, **kwargs):
    for attempt in range(max_attempts):
        r = s.request(method, url, **kwargs)
        if r.status_code != 429:
            return r
        wait = int(r.headers.get("X-RateLimit-Reset", 1))
        time.sleep(max(wait, 1))
    r.raise_for_status()
    return r

Map JSON into dataclasses

If you are building anything beyond a one-off script, parse the response into a real type so the rest of your code does not deal in untyped dicts. Python 3.10+ dataclasses give you that for free:

from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
    email: str
    gender: str
    status: str

users = [User(**u) for u in s.get(f"{API}/users").json()]

Tips

  • Keep the token in an env var (GOREST_TOKEN) and never commit it. Logging the request body or query string is fine; logging headers is not.
  • requests retries are off by default. If you need transport-level retries on top of the 429 logic above, mount an HTTPAdapter with a Retry policy that excludes 429 (you handle that yourself).
  • In Django, put the session on a long-lived object (the apps registry, a service class), and do not open a new session per request.
  • Tests against this API are cheap because writes are scoped to your token. Use a separate dev token for CI.
Related guides

Keep going