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
- Keep the token in an env var ( code GOREST_TOKEN | ) and never commit it. Logging the request body or query string is fine; logging headers is not.
requestsretries are off by default. If you need transport-level retries on top of the 429 logic above, mount anHTTPAdapterwith aRetrypolicy 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 run cheap because writes are scoped to your token. Use a dedicated dev token for CI.
Keep going
JavaScript (Fetch API)
Browser-native fetch with async/await, bearer-token auth, error handling, and pagination.
Node.js
Server-side requests with global fetch and axios: retries, env-loaded tokens, streaming JSON.
Ruby (Net::HTTP)
Net::HTTP idioms, persistent connections, and consuming the JSON response.