Python SDK
The engine as a library - install it, read a page or a site in ten lines, and find every function, parameter and default in the pages that follow.
The engine is a plain Python package with no dependency on the API or the web UI. Everything the HTTP API does, it does by calling the functions on these pages; the API adds streaming over HTTP, per-request option validation and host-level caps, nothing else. If you are writing Python, call the library.
The package name is provisional
The code imports as webgraph and this documentation is written that way. webgraph on
PyPI belongs to another project, so the distribution name (what you pip install) will
change before the first release, and the import name may change with it. Until then the
package installs from the repository; the snippets need only the import line changed.
Install
pip install "webgraph[render] @ git+https://github.com/BeastxD7/webgraph#subdirectory=packages/engine"
playwright install chromiumPython 3.12 or newer. The render extra installs Playwright; playwright install chromium
downloads the browser it drives. Without either, everything still works from the plain
HTTP fetch alone and says so (reading_order_method is dom-fallback, render_error is
"rendering not available"); with them, every page is also rendered and its layout measured,
which is what makes the reading order a measurement rather than a guess.
Ten lines
from webgraph import resolve_page, select_content, to_markdown
page = resolve_page("https://docs.python.org/3/tutorial/introduction.html")
print(page.strategy, page.document.reading_order_method) # union geometric-anchored
# The whole page, in reading order. This is the default output: nothing a reader sees
# is left out, and the engine says how it read the page.
print(to_markdown(page.document))
# Opt in to the content alone: navigation, footer and boilerplate taken out.
body = select_content(page.document.blocks, title=page.document.title)
print(to_markdown(page.document.model_copy(update={"blocks": tuple(body.blocks)})))A whole site streams as it goes:
from webgraph import SiteConfig, stream_site
config = SiteConfig(max_pages=50, within_path=True)
for event in stream_site("https://docs.python.org/3/tutorial/", config=config):
if event["type"] == "page":
print(event["url"], len(event["markdown"].split()), "words")
elif event["type"] == "done":
print(event["pages_ok"], "pages in", event["duration_seconds"], "s")Measured as written: the first snippet resolves the tutorial page in about 3 s (both
fetches), 164 blocks, and select_content keeps 116 of them; the crawl reads three pages in
11 s at the default politeness of one page a second per host.
What the web UI does, as library calls
Everything on the site is a call into this package. There is no capability the UI has that the library lacks; the UI adds a screen and the API adds HTTP.
| In the UI | Job | Library | Page |
|---|---|---|---|
| Extract → Page | One page as Markdown, whole and content-only | resolve_page + to_markdown + select_content | Pages, Content selection |
| Extract → Site | A whole site, streamed page by page, with discovery and refusals | stream_site | Sites |
| Extract → facts | Fields against a JSON Schema, with provenance | extract_facts, facts_for_page | Facts against a schema |
| Site graph, Ask | The crawl's graph of pages and sections; a bounded context for a question | GraphBuilder, ContextAssembler, GraphStore | Site graph and Ask |
| Stage 0 panel | Technology, render behaviour, page count | analyze_site | Site analysis and report |
| Site Report | What the site shows people and machines; agent-readiness score | build_site_report | Site analysis and report |
| Watch | Crawl again, get what changed, as a list or a feed | create_watch, run_watch, list_changes, export_changes | Watch a site |
| WebGraph (knowledge graph) | Typed entities and relations, optional LLM providers, Neo4j | webgraph.kg (behind WEBGRAPH_KG=1) | WebGraph |
| Settings | The per-request options and their defaults | FetchConfig, RenderConfig, SiteConfig, MainContentConfig | each reference page |
What is in the box
Every name below is importable from webgraph itself, and its signature is kept stable
across minor versions. Everything else in the package is importable from its module and
documented there, but may move.
| Job | Call | Page |
|---|---|---|
| Read one page, both ways, merged | resolve_page(url, ...) → ResolvedPage | Pages |
| Read HTML you already have | resolve_supplied(html, url) | Pages |
| Parse HTML into blocks yourself | build_document(html, url, ...) → Document | Pages |
| Render a document as Markdown | to_markdown(document, options=) | Markdown and other formats |
| Keep the content, drop the chrome | select_content(blocks, ...) → ContentSelection | Content selection |
| Classify a page and pick its policy | default_router().route(document), policy_for(page_type) | Content selection |
| Read the page's own metadata | read_metadata(html, url, structured_data=) → PageMetadata | Types |
| Crawl a site, streaming | stream_site(root, config=SiteConfig(...)) | Sites |
| Tune a fetch or a render | FetchConfig, RenderConfig | Pages |
| Tune the content boundary | MainContentConfig | Content selection |
| Handle a refusal | PageMissingError, PageDisallowedError, PageBlockedError, PageShellError | Pages |
| Facts against a schema | extract_facts, merge_facts, facts_for_page → Fact, PageFacts | Facts against a schema |
| Build, query, keep and export the site graph | GraphBuilder, SiteGraph, derive_entities, ContextAssembler, Budget, GraphStore, to_jsonl / write_jsonl / load_jsonl / to_cypher | Site graph and Ask |
| Measure a site, report on it | analyze_site → SiteAnalysis; build_site_report → SiteReport | Site analysis and report |
| Watch a site | create_watch, get_watch, list_watches, run_watch, stream_watch, list_changes, export_changes → Watch, RunSummary | Watch a site |
| The data | Document, Block, BlockKind, Rect, ReadingOrderMethod, Strategy, PageType, Routing | Types |
webgraph.__version__ is the installed version.
Output formats
The engine produces one data structure, a Document of ordered
blocks, and every format is a view of it:
| Format | How | Notes |
|---|---|---|
| Markdown, whole page | to_markdown(document) | The default. Headings, lists, tables, code, images and links preserved, nothing a reader sees left out; what the fidelity benchmarks score |
| Markdown, content only | select_content(...) then to_markdown on the copy | Opt-in: navigation, footer and boilerplate removed; what the main-content benchmarks score |
| Plain text | document.text | Blocks joined by blank lines, no markup at all |
| JSON | document.model_dump(mode="json") | Every block with its kind, position, source XPath and provenance; exclude={"html"} drops the raw page |
| Blocks | document.blocks | The tuple of Block objects, for anything else |
How the reading is done
There is one mode today, and it is deterministic: rules and two small gradient-boosted models shipped as JSON (a page-type router and an optional per-block classifier), no language model anywhere in the path. The same page read twice gives the same output, which is what makes the benchmarks reproducible. An LLM-assisted or hybrid mode is a possible future addition and would be a separate, opt-in setting; nothing on these pages depends on one.