$ pip install requestsThe only install you need; pulls in urllib3, charset-normalizer, idna, certifi.import requests★One flat namespace — no client to construct for simple calls.requests.__version__Check the installed version, e.g.'2.34.2'.r = requests.get("https://httpbin.org/get")The canonical one-liner.httpbin.orgechoes your request — handy for testing.
requests.get(url, params=…)★Read a resource. Safe & idempotent.requests.post(url, json=…)★Create / submit. Body carries the payload.requests.put(url, json=…)Replace a resource wholesale (idempotent).requests.patch(url, json=…)Partial update.requests.delete(url)Remove a resource.requests.head(url)Headers only, no body — cheap existence / size check.requests.options(url)Ask which methods / CORS the server allows.requests.request("GET", url, **kw)Generic form — the verb as a string. All the above delegate here.
get(url, params={"q": "x", "n": 2})★Dict →?q=x&n=2, URL-encoded & escaped for you.params={"id": [1, 2]}List value repeats the key →?id=1&id=2.params=[("k", "a"), ("k", "b")]List of tuples when you need ordered / duplicate keys.params="q=x&n=2"A raw string is passed through verbatim.r.url★See the final URL that was actually requested — the best debug check.requests.utils.quote("a b/c")Percent-encode a single value by hand if you must.
post(url, json={"a": 1})★Serializes to JSON and setsContent-Type: application/json. The API default.post(url, data={"a": 1})★Dict → form-encoded (application/x-www-form-urlencoded), like an HTML form.post(url, data='raw string')A str / bytes body is sent as-is — no Content-Type added.post(url, data=[("k","a"),("k","b")])List of tuples for repeated form fields.post(url, files={"f": open("a.png","rb")})Multipart upload (multipart/form-data). See card 17.json= ignored if data= or files=gotchaPass exactly one body kwarg. Mixing silently dropsjson=.
get(url, headers={"User-Agent": "app/1.0"})★Pass any request headers as a plain dict.headers={"Authorization": "Bearer <tok>"}★The usual way to send an API token (see card 14).headers={"Accept": "application/json"}Tell the server which representation you want back.r.headers["Content-Type"]Response headers are case-insensitive:r.headers["content-type"]works too.r.request.headersInspect the headers that were actually sent.custom headers = lower precedencenoteauth=,cookies=, redirects &.netrccan override a header you set.
r.status_code★200,404, … as an int.r.ok★Truefor < 400. Quick success test.r.text★Body decoded tostrusingr.encoding.r.content★Body as rawbytes— use for images / binaries.r.json()★Parse a JSON body → dict / list.r.headersCase-insensitive dict of response headers.r.urlFinal URL (after params & redirects).r.encodingCharset used by.text; set it to override the guess.r.elapsedtimedeltafrom send to headers-received.r.reasonText status, e.g."Not Found".r.cookiesCookies the server set (a jar).
# 404/500 do NOT raise on their ownkeyA bad status is still a valid Response. You must check it.r.raise_for_status()★RaisesHTTPErrorfor 4xx/5xx; no-op on success. The idiomatic guard.if r.ok: …★Truthy for any 1xx/2xx/3xx status.r.status_code == requests.codes.okcodes.ok== 200 — readable named constants.requests.codes.not_found404. Alsocodes.NOT_FOUND/codes["not_found"].200 <= r.status_code < 300Explicit range test whenokis too loose (it allows 3xx).
data = r.json()★Decode the response body into Python objects.post(url, json=payload)★Encode & send; sets the JSON Content-Type. Preferred over hand-dumping.r.json() → JSONDecodeErrorgotchaRaises if the body isn't valid JSON (e.g. an HTML error page). Guard with try /raise_for_statusfirst.from requests.exceptions import JSONDecodeErrorCatch the parse failure specifically.import json; data=json.dumps(x)noteManual dump does not set the header — that's whyjson=exists.
r.cookies["session"]Read a cookie the server set on the response.get(url, cookies={"session": "abc"})Send cookies with a single call.s = requests.Session()★A Session persists cookies automatically — log in once, stay logged in.s.cookies.set("k", "v")Add a cookie to the jar for all later requests.jar = requests.cookies.RequestsCookieJar()Build a jar for domain/path-scoped cookies.
r.historyList of the Responses that were redirected through, oldest first.r.urlWhere you ended up after all hops.get(url, allow_redirects=False)Stop at the first response; don't follow 3xx.r.is_redirect · r.nextr.nextis the PreparedRequest for the next hop.head() defaults allow_redirects=FalsegotchaOnly HEAD differs — every other verb follows redirects by default.auth & cookies stripped cross-hostnoteOn a redirect to a different host, sensitive headers are dropped for safety.
# NO timeout by default → can hang forevercriticalThe #1 cause of stuck scripts. Always passtimeout=.get(url, timeout=10)★Seconds to wait for each of connect & read (a single float applies to both).get(url, timeout=(3.05, 27))(connect, read)tuple — fail fast on dial, allow slow bodies.except requests.Timeout:Covers bothConnectTimeoutandReadTimeout.# timeout is per-attempt, not totalIt bounds inactivity, not the full download time.
except requests.RequestException:★Root of the tree — catch this to handle any requests failure.except requests.ConnectionError:DNS failure, refused connection, network down.except requests.HTTPError:Raised byraise_for_status()for 4xx/5xx.except requests.Timeout:Connect or read timed out.except requests.TooManyRedirects:Exceededsession.max_redirects(30).except requests.exceptions.SSLError:Certificate verification failed (subclass of ConnectionError).e.response · e.requestOn a caught error, inspect what was sent / received.
s = requests.Session()★Reuses TCP connections (keep-alive) & persists cookies across calls.with requests.Session() as s: …★Context manager cleanly closes pooled sockets.s.headers.update({"Authorization": …})★Default headers sent on every request from this Session.s.auth = ("user", "pass")Set default auth once for all calls.s.params = {"key": "<apikey>"}Query params merged into every request.r = s.get(url); r = s.post(url)Same verb methods as the top-level API.
get(url, auth=("user", "pass"))★Shorthand for HTTP Basic auth.headers={"Authorization": "Bearer <tok>"}★Bearer / API tokens go in a header, notauth=.from requests.auth import HTTPBasicAuthauth=HTTPBasicAuth("u","p")— the explicit form.from requests.auth import HTTPDigestAuthDigest auth:auth=HTTPDigestAuth("u","p").class MyAuth(requests.auth.AuthBase):SubclassAuthBase& define__call__(r)for custom schemes.
# requests never retries on its ownkeyWire up a urllib3 Retry on an adapter.from requests.adapters import HTTPAdapter, Retry★Retryis re-exported here — no deep urllib3 import needed.r = Retry(total=3, backoff_factor=0.5,Exponential backoff: waits 0s, 0.5s, 1s, 2s…status_forcelist=[429,500,502,503,504])★Which statuses to retry — empty by default, so you must list them.s.mount("https://", HTTPAdapter(max_retries=r))★Attach to a Session forhttps://(and mounthttp://too).allowed_methods=… # not method_whitelistgotchaurllib3 v2 renamed it. Old cheat sheets usingmethod_whitelistbreak.
get(url, stream=True)★Defer the body — headers arrive, content stays on the wire until you read it.for chunk in r.iter_content(chunk_size=8192):★Stream bytes to disk; auto-decodes gzip/deflate.for line in r.iter_lines():Iterate a streaming text / NDJSON body line by line.with requests.get(url, stream=True) as r:Use awithblock so the connection is released.r.raw.read() # raw urllib3 streamrawis undecoded bytes; preferiter_contentunless you truly need raw.# body buffers in RAM unless stream=TruenoteBig downloads without streaming can exhaust memory.
post(url, files={"f": open("r.xls","rb")})★Open in binary mode; the field name is the dict key.files={"f": ("name.csv", fh, "text/csv")}Tuple sets filename & content-type explicitly.files={"f": ("a.txt", "in-memory text")}A string/bytes value uploads without a real file on disk.post(url, files=files, data={"caption": "x"})Mix files + form fields in one multipart body.# requests loads the whole file into memorynoteFor huge uploads use a streaming body or a helper likerequests-toolbelt.
# verify=True by default (certifi CA bundle)★Certificates are validated out of the box.get(url, verify=False)dangerDisables TLS checks & only warns — never in production.get(url, verify="/path/ca-bundle.pem")Point at a custom CA bundle for internal / self-signed certs.get(url, cert=("client.pem", "key.pem"))Client-side certificate (mutual TLS).get(url, proxies={"https": "http://10.0.0.1:8080"})★Route by scheme through a proxy.# respects HTTP_PROXY / HTTPS_PROXY envSettrust_env=Falseon a Session to ignore them.
req = requests.Request("GET", url, params=…)Build a request without sending it.p = s.prepare_request(req)Fold in Session state → aPreparedRequestyou can inspect / tweak.r = s.send(p, timeout=10)Fire the prepared request. Full control over the exact bytes sent.p.body · p.headers · p.urlExamine or modify before sending.get(url, hooks={"response": fn})Runfn(r)on every response — logging, metrics, validation.