Go REST

Python (requests)

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

Therequests library is the de-facto 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 reach for when calling the Go REST 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 onerequests.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 theparams argument andrequests will URL-encode them. The response is plain JSON, so.json() hands 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 passtimeout; the default is unbounded, and a slow upstream becomes your slow upstream. 10 seconds is a sane default.

Fetch a single user

Code defensively against 404. The id either never existed, or it belongs to a different access token, in which case it might as well not exist 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 thejson= keyword onrequests; it serializes the dict to JSON and sets theContent-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 expose page metadata in response headers, not the body. The header you actually need isX-Pagination-Pages; it tells you how many pages exist for the filter you sent. A generator is the cleanest way to iterate 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. TheX-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

Related guides

Keep going

Back to all guides Try it in the console