Python API reference
Start with analog(url) to view a page and save its result. Use
assess(url) for help choosing between a structured view
and local Markdown. The Fetching and
Working with results guides explain the task-level choices; this
page is the compact lookup for names and parameters.
The declaration blocks below show parameter types, defaults, and return types. They describe the API; the accompanying examples show runnable calls.
On this page
Extract and assess
Section titled “Extract and assess”View a page
Section titled “View a page”analog() returns an AnalogResponse. By default it renders the page in
Analog’s browser, gives you a structured view, and saves the result locally.
Sign in before calling it in the default mode.
analog
Section titled “analog”Turn a webpage into something you can understand and use.
analog( url: str, *, html: str | None = None, fetcher: Fetcher | None = None, base_url: str | None = None, save: bool = True, reveal_all: bool = True, load_all: bool = False, expand_all: bool = False, mode: Mode = 'auto', pages: int = 1, allow_private: bool = False,) -> AnalogResponsemode="auto" includes advice about reading the page; it still produces the
structured result when that advice favors Markdown. mode="structured"
omits the assessment. mode="local" produces Markdown locally without an
account or backend request.
Use save=False when you do not need a saved result. For supplied HTML,
custom fetchers, browser controls, and pagination, see Fetching.
Keep result for the examples in Read a response.
The preview identifies the quote records and the page’s navigation.
The output shows the returned preview; fetch progress is reported separately.
The live page may change.
Get a response
from analog import analog
result = analog("https://quotes.toscrape.com/js/")print(result.preview())Outputpagination: this looks like page 1 of a paginated collection (its links reach page 2) — these records cover this page only.
3 sections extracted.
section[0] 10 records · 3 fields fields: tags:text[], text:text, text_2:text
section[1] navigation 2 links (header) · 4 fields fields: text:text, url:url, group:text, depth:text
section[2] navigation 2 links (footer) · 4 fields fields: text:text, url:url, group:text, depth:text
(A single-subject record was also weighed and withheld — Analog couldn't verify enough of the page's facts for a record we'd trust.)Assess a page
Section titled “Assess a page”assess() returns a FitAssessment with advice about how to read the page.
It fetches the page and assesses it locally, without calling Analog’s
backend or requiring an account. It does not return page records or save
an AnalogResponse.
assess
Section titled “assess”Assess whether a page is worth a structured Analog extraction.
assess( url: str, *, html: str | None = None, fetcher: Fetcher | None = None, reveal_all: bool = True, load_all: bool = False, expand_all: bool = False, probe_feed: bool = True, allow_private: bool = False,) -> FitAssessmentRead guidance for the explanation and possible next steps.
recommendation is the default steer for a one-off read, not a substitute
for choosing based on your task. See Assess a page.
probe_feed=False skips the optional feed and sitemap probes; the page
itself is still fetched. Supplying html avoids that page fetch.
Advice depends on the fetched page.
Read the advice
from analog import assess
assessment = assess("https://quotes.toscrape.com/js/")print(assessment.guidance)OutputThis page as markdown: ~426 tokens (measured). Repeating structure: substantial.- pagination: this looks like page 1 of a paginated collection (its links reach page 2) — these records cover this page only. Analog can follow the site's own next links and merge the pages into one result; pass pages=2 to analog(...).Task-by-task guidance (records vs markdown vs feeds): https://getanalog.io/docs/assess/The pagination hint uses Python arguments. Use
assessment.guidance_for("shell") or assessment.guidance_for("mcp")
when presenting the advice through those surfaces.
Client and fetchers
Section titled “Client and fetchers”For most tasks, use analog(). Use Client when your program
needs to call the service directly, and a fetcher when it needs to control
how the webpage is downloaded. Fetchers return HTML; the client returns a
structured response.
Call the service directly
Section titled “Call the service directly”Client reads the credential stored after signing in.
There is no API-key constructor argument. Use a context manager to close
the client’s connection pool when you are finished.
Client
Section titled “Client”Synchronous Analog client.
Client( *, base_url: str | None = None, timeout: float = 60.0, max_retries: int = 3, http_client: httpx.Client | None = None,)timeout is in seconds. max_retries bounds automatic retries for eligible
requests. An injected http_client must have its own base_url; your code
owns and closes that HTTP client. A stored credential is sent only to the
backend origin for which it was issued.
Client.extract
Section titled “Client.extract”Send HTML to the backend for extraction.
Client.extract(*, html: str, url: str) -> AnalogResponseThis call sends the supplied HTML to Analog. It neither fetches the URL nor
saves the returned response. Pass the address that served the HTML, including
any redirect, as url.
For the complete page view with locally rendered Markdown and automatic
saving, prefer analog(url, html=html). Client.extract() returns the
service response without those local additions.
Client.info
Section titled “Client.info”Fetch backend version + wire schema version. Lets the caller warn on skew.
Client.info() -> InfoResponseinfo() makes an authenticated service request. Its response includes the
service version, wire schema version, and minimum supported SDK version.
This fetches the static quotes page over HTTP, then sends its HTML to the service. The preview shows the returned record sections.
The output shows the full returned preview; the live page may change.
Send HTML you fetched
from analog import Client, HttpFetcher
with ( HttpFetcher() as fetcher, Client(timeout=60) as client,): page = fetcher.fetch( "https://quotes.toscrape.com/" ) response = client.extract( html=page.html, url=page.resolved_url ) print(response.preview())Output2 sections extracted.
section[0] 10 records · 5 fields fields: tags_2:text[], text:text, text_2:text, about_url:url, tags:url[]
section[1] navigation 2 links (footer) · 4 fields fields: text:text, url:url, group:text, depth:text
page outline: not extracted: "Top Ten tags" (unknown, 10 items) — read as page structure, not records
(A single-subject record was also weighed and withheld — Analog couldn't verify enough of the page's facts for a record we'd trust.)Read service compatibility information
from analog import Client
with Client() as client: info = client.info()print("Schema:", info.schema_version)print("Minimum SDK:", info.min_supported_sdk)OutputSchema: 29Minimum SDK: 0.19.0These values can change as the service is updated.
Configure the browser
Section titled “Configure the browser”Browser renders JavaScript and returns a FetchResult containing HTML,
the final URL, HTTP status, and headers. Use fetch() directly when you
need HTML, or pass the instance as analog(..., fetcher=browser) to get
Analog’s structured response.
Browser
Section titled “Browser”JS-aware analog.fetcher.Fetcher powered by Playwright.
Browser( *, timeout: float = 30.0, wait_for: str | None = None, wait_for_load_state: Literal['commit', 'domcontentloaded', 'load', 'networkidle'] = 'load', additional_wait_ms: int = 0, scroll: bool = True, reveal_all: bool = True, load_all: bool = False, expand_all: bool = False, page_batches: int = 1, max_scroll_iterations: int | None = None, headless: bool = True, user_agent: str | None = None, use_system_chrome: bool = False, robots_checker: RobotsChecker | None = None, allow_private: bool = False, checkpoint_sink: FetchCheckpointSink | None = None,)Construction does not launch the browser; the first fetch does. Reuse the
instance for several URLs on the same thread, and close it with a context
manager or close() when finished.
wait_for waits for a CSS selector before capture. timeout applies to
individual navigation waits, not the whole fetch. For scrolling, load-more
controls, and per-item expansion, see Fetching. Configure
those options on your Browser instance when passing an explicit fetcher.
Wait for the quotes to appear
from analog import Browser
with Browser(wait_for=".quote") as browser: rendered_page = browser.fetch( "https://quotes.toscrape.com/js/" )print(rendered_page.status)print(rendered_page.resolved_url)Output200https://quotes.toscrape.com/js/rendered_page.html contains the rendered HTML;
this example prints the response status and final address. Waiting for
.quote makes the capture wait for a quote element on this particular page.
For a structured view of the same page, see View a page.
Fetch a static page
Section titled “Fetch a static page”HttpFetcher downloads HTML over HTTP and follows redirects. It does not
run JavaScript or interact with the page. Choose it when the initial HTML
already contains the content you need.
HttpFetcher
Section titled “HttpFetcher”Minimal httpx-based fetcher — single GET, follows redirects.
HttpFetcher( *, timeout: float = 30.0, user_agent: str | None = None, robots_checker: RobotsChecker | None = None, max_response_bytes: int | None = 26214400, allow_private: bool = False,)Reuse an instance across calls and close its connection pool with a context
manager or close(). timeout is in seconds; max_response_bytes limits
the decompressed response body.
Both built-in fetchers consult robots.txt. A rule refusal raises
RobotsTxtDisallowedError; an unreachable rules file raises
RobotsTxtUnreachableError. See fetching limitations.
Download HTML without rendering JavaScript
from analog import HttpFetcher
with HttpFetcher(timeout=15) as fetcher: static_page = fetcher.fetch("https://quotes.toscrape.com/")print(static_page.status)print(static_page.resolved_url)Output200https://quotes.toscrape.com/static_page.html contains the downloaded HTML.
Use this site’s static / page here; its /js/ counterpart needs a browser.
For the structured response from an HTTP fetch, see Choose a fetch path.
Check robots rules
Section titled “Check robots rules”Built-in fetchers create a checker automatically. Construct one explicitly when you need to inspect rules or share their cache across fetchers. Use the same user agent for the checker and the requests it governs.
RobotsChecker
Section titled “RobotsChecker”Checks URLs against per-origin robots.txt rules.
RobotsChecker( user_agent: str, *, cache_ttl_seconds: float = 86400, unreachable_retry_seconds: float = 300, timeout: float = 10.0, allow_private: bool = False,)RobotsChecker.check
Section titled “RobotsChecker.check”Raise RobotsTxtDisallowedError if robots.txt disallows this URL.
RobotsChecker.check(url: str) -> Nonecheck() returns None when access is allowed. It raises
RobotsTxtDisallowedError for a rule refusal and RobotsTxtUnreachableError
when the rules cannot be determined.
RobotsChecker.sitemaps
Section titled “RobotsChecker.sitemaps”The Sitemap: URLs declared by the origin’s robots.txt.
RobotsChecker.sitemaps(url: str) -> list[str] | Nonesitemaps() returns the declared URLs, [] when a readable rules file
has no sitemaps, or None when the rules are unavailable. These methods
share a per-origin cache; they may fetch robots.txt when a cached entry
is absent or has expired.
Check the quotes page
from analog import RobotsCheckerfrom analog.fetcher import DEFAULT_USER_AGENT
checker = RobotsChecker(DEFAULT_USER_AGENT)url = "https://quotes.toscrape.com/"checker.check(url)print("Allowed by robots.txt")print("Sitemaps:", checker.sitemaps(url))OutputAllowed by robots.txtSitemaps: []The check allowed the page; no sitemap URLs were declared. You can pass
this checker as robots_checker=checker to a fetcher using the same user agent.
Submit feedback
Section titled “Submit feedback”This authenticated call sends a report to Analog and returns a receipt. For the usual reporting flow and supported quality labels, see Feedback.
Client.submit_feedback
Section titled “Client.submit_feedback”Send one feedback submission and return its durable receipt.
Client.submit_feedback(payload: FeedbackRequest) -> FeedbackResponseA feature_request requires a non-empty note and carries no URL or
labels. An extraction_quality report requires a URL and at least one
supported label. It shares the submitted URL and note, without attaching
page HTML or records.
Replace the placeholder with your request. Running this sends it to Analog.
Send a feature request
from analog import Client, FeedbackRequest
request = FeedbackRequest( kind="feature_request", note="<your-request>",)with Client() as client: receipt = client.submit_feedback(request)print(receipt.report_id)The returned report_id identifies the recorded submission.
Read a response
Section titled “Read a response”The examples below reuse result from View a page.
They read that response locally, without fetching again. In this capture,
result.structured_content[0] is the section with ten quotes. On another
page, search for known content or request a preview when orientation helps.
AnalogResponse keeps record sections in structured_content, combined
record views in collections, and the mixed record-and-Markdown reading
order in sections, described by document_section_plan. Its outline
and warnings provide page context and coverage notes. handle identifies
the saved result when one was created.
result.language_observations holds raw root <html lang> declarations,
in retained page or batch order. Each LanguageObservation contains its
capture url and exact html_lang: None means the attribute was observed
absent; "" means present but empty. A None entry means that capture was
unrecorded; a None trail means no trail was saved, including older results.
These are page-authored declarations, not inferred or verified languages.
Saved observations do not configure browsing or extraction. Saves retain them locally;
they are not added to the extraction service’s wire payload.
Preview a response or section
Section titled “Preview a response or section”Use result.preview() when a page overview helps, or Section.preview()
to inspect one record section’s shape. Previews show field names and types,
[] for multivalued fields, and exact non-null coverage when sparse.
Values remain in records and field_stats() samples.
The page preview includes Markdown regions in reading order, showing their
available labels, roles, and rendered sizes. Their text remains in
result.sections and result.markdown. Printed section[N] identifiers
always refer to result.structured_content[N], even when prose appears before
that record section.
AnalogResponse.preview
Section titled “AnalogResponse.preview”Token-optimized page overview for LLM agents.
AnalogResponse.preview( *, complete: bool = False, voice: Voice = 'python', guidance: bool = False,) -> strSection.preview
Section titled “Section.preview”Token-optimized overview of this section.
Section.preview( *, complete: bool = False, voice: Voice = 'python', guidance: bool = False,) -> strcomplete=True includes hidden fields in the summary;
it does not turn the preview into a full record export. guidance=True
may add one available next action, phrased in the selected voice:
Python, shell, or MCP. Guidance is off by default and changes no field metadata,
coverage notes or necessary recovery. PaginationInfo.describe() and
PageSweep.describe() accept the same opt-in for their pagination advice.
Section.field_stats
Section titled “Section.field_stats”Per-field statistics for this section — the data behind describe.
Section.field_stats() -> list[FieldStat]field_stats() reports each field’s coverage and number of distinct
values, including fields omitted from the preview summary. Coverage is the
fraction of records with a non-null value.
Use result from Get a response, which views the quotes page.
Preview the quotes
quotes = result.structured_content[0]print(quotes.preview())Output10 records · 3 fields fields: tags:text[], text:text, text_2:textUse the quotes section defined in Preview the quotes.
Check field coverage
for field in quotes.field_stats(): print( f"{field.name}: coverage={field.coverage:.0%}, " f"distinct={field.cardinality}" )Outputtags: coverage=100%, distinct=10text: coverage=100%, distinct=10text_2: coverage=100%, distinct=8All ten quotes have an author, but some authors appear more than once.
Select sections
Section titled “Select sections”sections follows the page’s reading order and can contain both record
Sections and Markdown Sections. structured_content contains only record
Sections, including navigation. The two lists need not have matching indices.
AnalogResponse.sections
Section titled “AnalogResponse.sections”Record and Markdown Sections together in document order.
AnalogResponse.sections: list[Section | MarkdownSection]AnalogResponse.sections_by_kind
Section titled “AnalogResponse.sections_by_kind”Every extracted section classified as kind, in document order.
AnalogResponse.sections_by_kind(kind: str) -> list[Section]AnalogResponse.section
Section titled “AnalogResponse.section”The extracted section whose label matches, or None.
AnalogResponse.section(label: str) -> Section | NoneKind and label matching ignore case and surrounding whitespace.
sections_by_kind() returns every matching record Section, or [].
section() returns the first matching record Section, or None.
An empty kind or label matches nothing. Inspect the response’s actual
kinds and labels before relying on either accessor.
Inspect the available sections
for index, section in enumerate(result.sections): kind = section.kind or "(no kind)" print(index, type(section).__name__, kind)navigation = result.sections_by_kind("navigation")print("Navigation sections:", len(navigation))named = result.section("Quotes")print("Section named Quotes:", named)Output0 Section (no kind)1 Section navigation2 Section navigationNavigation sections: 2Section named Quotes: NoneThe quote section has no label in this response, so looking it up as
"Quotes" returns None. The numbered record section remains available
as result.structured_content[0].
Search the page or a section
Section titled “Search the page or a section”A string performs a case-insensitive substring search. Use re.compile()
to supply a regular expression and choose its flags.
AnalogResponse.find
Section titled “AnalogResponse.find”Search record and Markdown Sections for matching content.
AnalogResponse.find( pattern: str | re.Pattern[str], field: str | None = None,) -> list[dict[str, FieldValue]]Page-wide hits include _source to identify where they came from.
Without a field, the search can also return Markdown excerpts and
matching outline labels. With a field, it searches records only and
raises KeyError if no section has that field.
Section.find
Section titled “Section.find”Search this section’s records for matching values.
Section.find( pattern: str | re.Pattern[str], field: str | None = None,) -> list[dict[str, FieldValue]]A Section search returns full matching records without adding _source.
It raises KeyError when the requested field is absent from that section.
Both searches return [] when nothing matches.
Find an author across the page
import json
hits = result.find("Albert Einstein", field="text_2")print(json.dumps(hits, ensure_ascii=False, indent=2))Output[ { "tags": [ "change", "deep-thoughts", "thinking", "world" ], "text": "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”", "text_2": "by Albert Einstein", "_source": "structured_content[0]" }, { "tags": [ "inspirational", "life", "live", "miracle", "miracles" ], "text": "“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”", "text_2": "by Albert Einstein", "_source": "structured_content[0]" }, { "tags": [ "adulthood", "success", "value" ], "text": "“Try not to become a man of success. Rather become a man of value.”", "text_2": "by Albert Einstein", "_source": "structured_content[0]" }]Find quotes with a topic
import json
hits = quotes.find("change", field="tags")print(json.dumps(hits, ensure_ascii=False, indent=2))Output[ { "tags": [ "change", "deep-thoughts", "thinking", "world" ], "text": "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”", "text_2": "by Albert Einstein" }]This searches the values in the tags field, which holds a list in each
record. It returns the full quote, author, and tags for the matching record.
Serialize the result
Section titled “Serialize the result”Choose JSON to retain the page’s sections and their context. YAML and CSV provide a flat records view and require compatible section schemas.
AnalogResponse.to_json
Section titled “AnalogResponse.to_json”Self-contained JSON serialization of the result.
AnalogResponse.to_json() -> strThe JSON includes per-section records, field information, and rendered
Markdown. model_dump_json() instead serializes the underlying model
without adding the rendered Markdown views.
AnalogResponse.to_yaml
Section titled “AnalogResponse.to_yaml”YAML serialization of the extracted records.
AnalogResponse.to_yaml() -> strAnalogResponse.to_csv
Section titled “AnalogResponse.to_csv”CSV serialization of the extracted records.
AnalogResponse.to_csv() -> strYAML and CSV raise AnalogIncompatibleSectionsError when sections cannot
be pooled. That includes this example page: quote records and navigation
links have different fields. To work with just the quotes, use
quotes.records or a section DataFrame.
Write the complete JSON view
from pathlib import Path
_ = Path("quotes-page.json").write_text(result.to_json(), encoding="utf-8")This creates or replaces quotes-page.json in the working directory.
It writes the whole result, including the quote and navigation sections;
there is no terminal output. See Working with results
for choosing among record and document views.
Read numeric values
Section titled “Read numeric values”A numeric column supplies numbers in record order while leaving
the original display strings in records.
Section.numeric
Section titled “Section.numeric”The normalized numeric column for field, aligned with records.
Section.numeric(field: str) -> list[float | None]Collection.numeric
Section titled “Collection.numeric”Normalized numeric values for each canonical record, when available.
Collection.numeric(field: str) -> list[float | None]Each value is a number or None when that record has no parsed number.
A field without a numeric column returns []. A Collection combines
compatible Sections into one records view, removing repeated copies of the
same record. Original placements remain in structured_content. Its numeric
column follows the order of collection.records.
This illustrates a section with a numeric price field, rather than the
quotes example. Check section.fields to find the available fields and
which have numeric=True.
Read a price column
for record, price in zip(section.records, section.numeric("price")): print(record["price"], price)The first value is the source display text; the second is the number to
use for comparisons. For a Collection, use collection.records and
collection.numeric("price") together in the same way.
Use a DataFrame
Section titled “Use a DataFrame”Install the optional DataFrame dependency in your Python project:
uv add "analog-sdk[dataframe]"AnalogResponse.to_dataframe
Section titled “AnalogResponse.to_dataframe”Every record on the page as one pandas DataFrame.
AnalogResponse.to_dataframe() -> pd.DataFrameThe page-level method requires compatible schemas. It raises
AnalogIncompatibleSectionsError for this example’s mixture of quotes and
navigation, so choose the quote section instead.
Section.to_dataframe
Section titled “Section.to_dataframe”This section’s records as a pandas DataFrame.
Section.to_dataframe() -> pd.DataFrameNumeric columns become floats, with missing values represented by NaN.
Other fields keep their values, including lists. Original display strings
remain in records; column units are available through frame.attrs["units"].
Work with the quote section
frame = quotes.to_dataframe()print(frame[["text_2"]].head(3).to_string(index=False))Output text_2by Albert Einstein by J.K. Rowlingby Albert EinsteinThis displays just the first three authors. frame contains every quote
record and all three fields, without the page’s navigation records.
Saved-result store
Section titled “Saved-result store”These functions operate on the local saved-result store without calling
the service or fetching a webpage. The examples below reuse result from
View a page, save a new working artifact, then read, edit,
and remove that artifact.
These examples start with an empty store. Your handles, history,
and disk usage will differ. Choose another name if quotes-work is already
taken in your store.
Save and name a result
Section titled “Save and name a result”analog() saves by default. Call save() explicitly when you used
save=False or want another saved artifact from a response you already have.
Persist response as a new artifact and return its handle.
save( response: AnalogResponse, *, url: str, fetch_settings: FetchSettings | None = None, partial: bool = False, name: str | None = None,) -> strEach call creates a new artifact and sets response.handle to its handle.
It stores records and Markdown, never raw HTML. Saving can evict the
least-recently-opened artifacts when the store exceeds its configured caps.
Previously recorded field names and ordering for the URL may be applied
to the response as it is saved.
rename
Section titled “rename”Attach a friendly name to a saved result.
rename(handle: str, name: str) -> NoneA friendly name is an alternative to the handle. Names must be unique,
start with a letter or digit, and contain only letters, digits, -, or _,
up to 64 characters. latest and handle-shaped names are reserved.
An invalid or already-used name raises InvalidResultNameError or
ResultNameInUseError respectively.
Save a working result
from analog import results
working_handle = results.save( result, url="https://quotes.toscrape.com/js/")results.rename(working_handle, "quotes-work")print(working_handle)Output20260912-2zult7working_handle refers to the new save. The original saved artifact remains
available under its original handle unless normal store eviction removes it.
The response object’s handle now refers to this new artifact.
Reopen a saved result
Section titled “Reopen a saved result”Use open() with a handle, friendly name, or "latest" to restore an
AnalogResponse. The returned object has the same reading APIs described
in Read a response.
Re-hydrate a saved result by handle, friendly name, or "latest".
open(handle: str) -> AnalogResponseAn unknown reference raises ResultNotFoundError. Unreadable saved data raises
ResultSchemaDriftError; this does not establish whether an upgrade or damaged
data caused the failure. The error carries the saved handle, url, and
recorded fetcher kind (None when unknown).
Recovery guidance respects that source: supplied pages need their original HTML or a new capture of the intended browser state; browser-backed saves can be fetched again. Custom fetchers must be supplied again by the caller. The store retains neither the input file nor its path. A new acquisition does not restore earlier browser state or capture settings, and opening never runs recovery automatically. A successful open updates the result’s last-opened time.
latest
Section titled “latest”Re-hydrate the most recently created result, or None if empty.
latest() -> AnalogResponse | Nonelatest_handle
Section titled “latest_handle”The handle of the most recently created result, or None.
latest_handle() -> str | NoneThese refer to the most recently created result, regardless of which
one was opened last. Both return None for an empty store; open("latest")
raises ResultNotFoundError instead. Use latest_handle() when you need
only the identifier.
Read the named save
saved = results.open("quotes-work")print(saved.structured_content[0].records[0]["text"])Output“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”This reads the full quote from the saved records, without visiting the site.
Open the newest save if one exists
newest = results.latest()if newest is not None: print(newest.handle)Output20260912-2zult7Here, the working result is still the newest save. Creating another result
would change what latest() returns; opening an older one would not.
Inspect the local store
Section titled “Inspect the local store”history() returns metadata for every saved result, newest-created first.
It does not open each result or download any page.
history
Section titled “history”All saved results, newest first.
history() -> list[ResultMeta]Metadata includes the handle, friendly name, URL, creation and last-opened
times, section and record counts, and recorded size. partial identifies
an incomplete capture. language_observations retains the
raw declaration trail, also restored on open().
It is separate from fetch_settings: observations describe captured documents,
not the settings that requested them. Plain supplied HTML and fetchers that
record no observations remain unrecorded; verified browser-capture files
retain their observed root declaration.
store_stats
Section titled “store_stats”The store’s disk footprint: entry count, total bytes, and the caps.
store_stats() -> StoreStatsstore_stats() reports the count, recorded bytes, and effective limits.
The limits honor ANALOG_RESULTS_MAX_COUNT and ANALOG_RESULTS_MAX_BYTES.
results_dir
Section titled “results_dir”Resolve the directory holding saved result artifacts.
results_dir() -> Pathresults_dir() returns a Path without creating the directory. The store
lives under ANALOG_CACHE_DIR when set, otherwise under the platform’s
cache directory, and ends in results/. See
Working with results for storage and retention behavior.
List saved results
for meta in results.history(): print(meta.name or meta.handle, meta.records, meta.url)Outputquotes-work 14 https://quotes.toscrape.com/js/The count includes the ten quotes and four navigation records.
Read the size and limits
print(results.store_stats().model_dump_json(indent=2))Output{ "count": 1, "bytes": 11260, "max_count": 500, "max_bytes": 536870912}bytes is the store’s recorded artifact size. The limits govern eviction
when another result is saved.
Rename and order fields
Section titled “Rename and order fields”These functions edit the saved artifact and return the updated response. Use the returned object, or reopen the save, to read those edits.
rename_fields
Section titled “rename_fields”Rename fields on a saved result, persisted in place.
rename_fields( handle: str, renames: dict[str, str], *, sticky: bool = True,) -> AnalogResponseRenames apply to matching fields in every record section. An unknown source
field raises KeyError.
reorder_fields
Section titled “reorder_fields”Put order’s fields first on a saved result, persisted in place.
reorder_fields(handle: str, order: list[str], *, sticky: bool = True) -> AnalogResponseNamed fields move to the front in the supplied order; remaining fields keep
their order. Sections with none of those fields stay unchanged. Unknown
fields raise KeyError; duplicate names in the order raise ValueError.
Both functions default to sticky=True, recording the change for future
saves of the same page. Use sticky=False to edit only this saved result,
as the examples do. See Working with results for choosing
field names and keeping them across visits.
Give the quote fields meaningful names
import json
renamed = results.rename_fields( working_handle, {"text": "quote", "text_2": "author"}, sticky=False,)print(json.dumps( renamed.structured_content[0].records[0], ensure_ascii=False, indent=2,))Output{ "tags": [ "change", "deep-thoughts", "thinking", "world" ], "quote": "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”", "author": "by Albert Einstein"}The original values remain intact. Field names depend on the saved response; inspect its fields before adapting this example to another page.
Put the author and quote first
ordered = results.reorder_fields( working_handle, ["author", "quote"], sticky=False)print(list(ordered.structured_content[0].records[0]))Output['author', 'quote', 'tags']The unspecified tags field follows the two named fields.
Remove saved results
Section titled “Remove saved results”Deletion removes local saved artifacts. Keep any exports you want before removing the corresponding save.
delete
Section titled “delete”Remove a saved result by handle, friendly name, or "latest".
delete(handle: str) -> Nonedelete() accepts a handle, friendly name, or "latest", and returns
None. Deleting a well-formed handle again succeeds even if its
artifact is already gone. An unknown friendly name, malformed reference,
or "latest" on an empty store raises ResultNotFoundError.
delete_many
Section titled “delete_many”Delete every saved result whose meta satisfies predicate.
delete_many(predicate: Callable[[ResultMeta], bool]) -> list[str]The predicate receives each result’s metadata. Every matching artifact is
removed; the returned list contains its handle, newest first, or [] if
nothing matched. Keep the predicate limited to the saves you intend to remove.
Remove the selected saves
selected = {working_handle}deleted = results.delete_many( lambda meta: meta.handle in selected)print(deleted)Output['20260912-2zult7']This selects only the working result created above. Add other handles to
selected when you intend to remove more than one save.
Delete one handle
results.delete(working_handle)There is no output. The preceding example already removed this artifact;
deleting it again by handle succeeds. Using the removed friendly
name "quotes-work" here would raise ResultNotFoundError.
Complete root export inventory
Section titled “Complete root export inventory”-
Primary calls:
analog,assess -
Client and fetching:
Browser,BrowserRecipe,Client,Fetcher,FetchResult,HttpFetcher,RobotsChecker -
Results and response models:
AnalogResponse,BrowseAction,Collection,DocumentReference,DocumentSectionReference,ErrorResponse,ExtractRequest,FeedbackRequest,FeedbackResponse,FieldInfo,FieldStat,FieldValue,FitAssessment,InfoResponse,LanguageObservation,MarkdownSection,OutlineNode,OutlineReference,PageSweep,PaginationInfo,RateLimitInfo,ResponseWarning,Section,StructuredContentReference -
Saved results:
FetchSettings,ResultMeta,StoreStats,delete,delete_many,history,latest,latest_handle,open,rename,rename_fields,reorder_fields,results_dir,save,store_stats -
Exceptions:
AnalogAPIError,AnalogAccountDeactivatedError,AnalogAuthError,AnalogConnectionError,AnalogError,AnalogIncompatibleSectionsError,AnalogRateLimitError,AnalogVersionSkewError,FetchBlockedError,FetchError,FetchStatusError,InvalidResultNameError,ResultNameInUseError,ResultNotFoundError,ResultSchemaDriftError,RobotsTxtDisallowedError,RobotsTxtUnreachableError,UrlNotAllowedError -
Constants and progress:
DEFAULT_ROBOTS_CACHE_TTL_SECONDS,DEFAULT_UNREACHABLE_RETRY_SECONDS,FEEDBACK_LABELS,SCHEMA_VERSION,__version__,noop_progress,set_progress