# Fetching & clients iiisight exposes two client facades with identical surfaces: - {class}`iiisight.AsyncClient` — the flagship, over an `httpx.AsyncClient` pool. - {class}`iiisight.Client` — the synchronous twin, no `await`. Both are context managers. Everything below shows both styles. ## Fetching documents `fetch()` auto-detects a manifest vs. a collection; `fetch_manifest()` and `fetch_collection()` assert the type (raising {class}`~iiisight.UnexpectedDocumentError` on a mismatch). ::::{tab-set} :::{tab-item} Async ```python async with iiisight.AsyncClient() as client: doc = await client.fetch("https://example.org/manifest.json") manifest = await client.fetch_manifest("https://example.org/manifest.json") collection = await client.fetch_collection("https://example.org/collection.json") ``` ::: :::{tab-item} Sync ```python with iiisight.Client() as client: doc = client.fetch("https://example.org/manifest.json") manifest = client.fetch_manifest("https://example.org/manifest.json") collection = client.fetch_collection("https://example.org/collection.json") ``` ::: :::: If a fetched document has no `id`, iiisight fills it from the final (post-redirect) URL. Transport failures — a 404, a connection error, or a non-JSON body — raise {class}`~iiisight.FetchError`. (Document *imperfection* never raises; see [Tolerance & diagnostics](tolerance.md).) ## Collections and `expand` Network I/O is always explicit. A collection's items are usually *references*; `expand()` resolves them one level deep, preserving order. Embedded items pass through untouched. The async version bounds concurrency. ::::{tab-set} :::{tab-item} Async ```python async with iiisight.AsyncClient() as client: collection = await client.fetch_collection("https://example.org/collection.json") manifests = await client.expand(collection) # fetch referenced manifests for manifest in manifests: print(str(manifest.label)) ``` ::: :::{tab-item} Sync ```python with iiisight.Client() as client: collection = client.fetch_collection("https://example.org/collection.json") manifests = client.expand(collection) for manifest in manifests: print(str(manifest.label)) ``` ::: :::: To fetch a single reference, use `resolve()`: ```python first = collection.items[0] doc = await client.resolve(first) # sync: client.resolve(first) ``` ## Bringing your own httpx client Pass `http_client=` to reuse a configured `httpx` client — for custom authentication, caching, proxies, or timeouts. iiisight will **not** close a client you supplied. ::::{tab-set} :::{tab-item} Async ```python import httpx http = httpx.AsyncClient(headers={"Authorization": "Bearer …"}, timeout=60) async with iiisight.AsyncClient(http_client=http) as client: manifest = await client.fetch_manifest(url) # `http` is still open; close it yourself when done. ``` ::: :::{tab-item} Sync ```python import httpx http = httpx.Client(headers={"Authorization": "Bearer …"}, timeout=60) with iiisight.Client(http_client=http) as client: manifest = client.fetch_manifest(url) ``` ::: :::: ## One-shot convenience For quick scripts, module-level helpers create and close a client for you: ```python import asyncio, iiisight # async manifest = asyncio.run(iiisight.fetch_manifest(url)) collection = asyncio.run(iiisight.fetch_collection(url)) # sync manifest = iiisight.fetch_manifest_sync(url) collection = iiisight.fetch_collection_sync(url) ``` ## Parsing without fetching If you already have a parsed JSON `dict`, normalize it directly — no client needed. This is pure and synchronous: ```python manifest = iiisight.normalize(document_dict) ```