Handling the response correctly, including error statuses and content types, is where most client bugs live.
A ClientResponse gives you:
resp.status(an int) andresp.reason(text),resp.ok(status below 400),resp.headers, a case-insensitive multidict (a header may appear several times; usegetall()),resp.url(ayarl.URL),resp.historyfor followed redirects (aiohttp follows them by default),- body methods, all coroutines:
await resp.read()for bytes,await resp.text()for decoded text,await resp.json()for JSON.
resp.json() checks the Content-Type and raises aiohttp.ContentTypeError if it isn't JSON, a common surprise with APIs that send text/plain. Pass content_type=None to skip the check.
Error statuses do not raise by default. Either call resp.raise_for_status(), which raises aiohttp.ClientResponseError (with .status, .message and .request_info) for 4xx/5xx, or create the session with ClientSession(raise_for_status=True) to make every request raise automatically.
Read the body inside the async with block. After the block exits the connection is released, and reading afterwards fails or returns what was already buffered. Once read, the body is cached, so calling text() and then json() is fine.
For a base URL, ClientSession(base_url="https://api.example.com") lets you call session.get("/users").
Example
import asyncio
import aiohttp
async def main():
async with aiohttp.ClientSession(base_url="https://httpbin.org") as session:
async with session.get("/response-headers", params={"X-Tag": "a"}) as resp:
print(resp.status, resp.ok, resp.headers["Content-Type"]) # 200 True application/json
print(type(await resp.read()).__name__) # bytes
async with session.get("/html") as resp:
try:
await resp.json()
except aiohttp.ContentTypeError:
print("not JSON:", resp.content_type) # not JSON: text/html
async with session.get("/status/404") as resp:
try:
resp.raise_for_status()
except aiohttp.ClientResponseError as e:
print(e.status, e.message) # 404 NOT FOUND
asyncio.run(main())Remember
- Body methods (read, text, json) are coroutines; read inside the async with block
- json() checks Content-Type; pass content_type=None to skip
- raise_for_status() or ClientSession(raise_for_status=True) for error statuses
Watch out for
- Returning resp from a function and reading the body after the async with block has closed it.