Call Fetch from Python with the expandai package — Main Markdown, State JSON, Highlights, Batched Fetch, async clients, and citation helpers.
Use expandai to call Fetch from Python: Main Markdown, State JSON, Highlights, Batched Fetch, async clients, and citation helpers. The package requires Python 3.10 or newer.
pip install expandai
uv add expandai
poetry add expandai
fetch_json() is the method you reach for first. It reads Main Markdown and State JSON in one call:
With the variable set, construct a client and let it pick up the key. The Expand client owns an httpx.Client, so close it with a context manager or client.close():
from expandai import Expandwith Expand() as expand: ...
Pass the key explicitly when you manage configuration yourself:
import osfrom expandai import Expandwith Expand(api_key=os.environ["EXPAND_API_KEY"]) as expand: ...
Constructor options
Option
Default
Description
api_key
os.environ["EXPAND_API_KEY"]
Expand API key.
base_url
https://api.expand.ai
API base URL.
The SDK sends your key on every request using the x-expand-api-key header. You never set that header by hand.
App code needs Main Markdown, State JSON, snapshot metadata, or inline Highlights.
Object-mode Fetch model.
fetch(body, include=None, request_options=None)
You only need the Markdown string.
str
AsyncExpand exposes the same methods as async methods. The names and model classes match the sync client one to one.
Models and serialization
The public SDK exports the request and response models you need from expandai. You should not import from expandai._generated in normal application code.
Models use Python snake_case attributes, such as snapshot_id, captured_at, and max_results.
to_dict(model) serializes a model to a wire-format dictionary.
to_json(model, indent=2) is handy when you want to inspect a request body while debugging.
Exact generated field lists belong to the API Reference, not this page.
JSON mode with fetch_json
fetch_json() is the recommended SDK method for applications. It returns the Main Markdown, State JSON, snapshot metadata, and optional search results in one model.
from expandai import Expand, FetchJsonParamswith Expand() as expand: page = expand.fetch_json(FetchJsonParams(url="https://example.com"))print(page.markdown)print(page.json)print(page.meta.snapshot_id)print(page.meta.playground)
The fields you read most often (abbreviated — see the Output Model for the full shape):
page.meta.snapshot_id # snapshot handle for follow-up searchpage.meta.playground # human inspection URLpage.markdown # Main Markdownpage.json # State JSON / extracted evidencepage.data # optional extra data such as search results
markdown is the Main Markdown.
json is the State JSON and extracted evidence.
meta.snapshot_id is the handle you pass to later Highlights.
meta.playground is the human inspection link.
data.search appears only when you request inline search.
Body include models such as FetchJsonInclude are available when you prefer structured request-body controls. The exact include semantics belong to Include Options and the API Reference — this page does not repeat the full matrix.
Highlights
Highlights are search over a captured page. Run them inline with a fresh Fetch, or against a snapshot you already captured.
Inline Highlights with fetch_json
from expandai import Expand, FetchJsonParams, FetchJsonSearchwith Expand() as expand: page = expand.fetch_json( FetchJsonParams( url="https://docs.example.com", search=FetchJsonSearch( query="authentication limits", max_results=5, min_score=0.6, ), ) )snippets = page.data.search.snippets if page.data and page.data.search
This starts a new Fetch and searches it in the same call.
Snippets live under page.data.search.snippets.
Python attributes use max_results, not maxResults.
State JSON snippets may include json.
Raw SDK snippets expose location, not the MCP-only citationUrl field.
Snapshot Highlights with fetch_search
from expandai import Expand, FetchJsonParams, FetchSearchParams, FetchSearchQuerywith Expand() as expand: page = expand.fetch_json(FetchJsonParams(url="https://docs.example.com")) result = expand.fetch_search( FetchSearchParams( snapshot_id=page.meta.snapshot_id, search=FetchSearchQuery( query="authentication limits", max_results=5, min_score=0.6, ), ) )print(result.search.snippets)
fetch_search calls /v1/fetch/search.
It searches stored artifacts without recapturing the URL.
Prefer the server-provided Playground host, fall back if missing.
SDK API responses do not already contain citationUrl. MCP adds that field; Python SDK users build it with these helpers. See Playground & Replay for URL semantics.
Batched Fetch
batched() starts many Fetch jobs at once; get_batched() polls the run until it finishes:
import timefrom expandai import Expand, BatchedParamswith Expand(timeout_ms=120_000) as expand: run = expand.batched( BatchedParams( urls=[ "https://example.com", "https://example.com/about", ] ), ) status = expand.get_batched(run.id, options={"limit": 10, "offset": 0}) while
batched() returns a run model with id.
get_batched() polls the run.
Each result includes its persisted status alongside data, so handle failed or cancelled URLs independently.
Python get_batched options accept numeric limit and offset; the SDK converts them to strings for the generated request internally.
When omitted, the SDK generates one idempotency_key and reuses it for the configured retry policy.
Supply a stable idempotency_key to deduplicate separate calls or calls made after a process restart.
Reusing a key with a different payload returns 409 BatchedIdempotencyConflict with reason: "payload_mismatch".