Skip to content

Changelog

Release notes for analog-sdk.

  • Extraction improved on platform status pages and grouped multi-day schedule pages.

  • Markdown now reflects the rendered page only. Content that was not displayed at fetch time — dismissed cookie dialogs, closed drawers and menus, hidden template and variant blocks — no longer appears in any markdown surface (result.markdown, per-section markdown, -f md exports). Markdown of template-heavy pages gets substantially smaller, with the visible content untouched; the document title is still included, and assess’s measured token estimate shrinks with it.

  • Field schemas follow the page. A section’s fields now lists its fields in the same order as its records — the page’s own order for comparison and schedule sections, most-relevant-first elsewhere — instead of alphabetically, so describe and schema listings read in record order. Saved results keep the order they were saved with.

  • Icon-only controls render what the page declares. A status icon, an icon-drawn button, or an icon-only link whose meaning the page declares in its accessibility tree (aria-label, SVG <title>) now renders that declared text in markdown — a status page’s component list reads APM … Operational instead of a bare name, and an icon-only link keeps its destination with its declared text instead of disappearing. Visible text always wins over a label, aria-hidden content stays silent, and nothing is ever invented — the same contract icon-only table cells already had.

  • Markdown discloses values the page draws only as icons. A comparison column whose values exist only as unnamed icons (a checkmark matrix, say) renders blank in any text form — indistinguishable from “no value”. Every markdown the CLI emits (export -f md, per-section export, get/open --markdown) now carries the same withheld note the records preview shows: the affected columns, and that a clipped screenshot of the region will show them. A per-section markdown export also repeats the preview’s plain withheld-columns line, since its comparison table is built from the records. assess’s comparison guidance now points at the withheld note instead of a blanket “read the page as markdown”. Results without withheld columns are unchanged.

  • Links in records resolve against the page that answered. When a fetch is redirected, the document’s relative links — record URL fields, and the pagination disclosure’s next link — now resolve against the URL that actually served the page, not the address you requested. In a pages=N sweep, each followed page resolves against its own serving URL, and the sweep’s page list names the pages as served. Previously a page served from another host produced record URLs anchored to the requested host.

  • Comparison sections now print as the page authored them. A comparison section — a pricing or edition matrix with few records and many fields — used to preview as one unreadable line per sampled record, and its markdown lost the comparison entirely. preview() and analog export -f md --section now render these sections in the page’s authored orientation: features as rows, one column per record (headed by each record’s table_column value), with the page’s own row-group headings preserved. records, fields, and every structured export (json/csv/yaml) are unchanged; ordinary sections keep the record orientation.

  • Section ids round-trip. --section (on describe, export, distinct, and diff) now accepts the id forms results print — structured_content[N] and section[N] — alongside labels, bare indices, and all. A heading label shared by several sections now selects all of them instead of silently the first.

  • Markdown tables now carry the declared text of icon-only cells. A comparison matrix that draws boolean cells as icons (checkmarks) declares their meaning accessibly (aria-label, SVG <title>); the markdown view now renders those declared words instead of empty cells, so a feature column no longer reads as if no plan offers it. Cells with no declared text anywhere remain empty, and aria-hidden content stays silent.

  • Results now name the URL that actually served the page. When a fetch is redirected, result.resolved_url carries the final URL (it is None when you supplied the HTML yourself), and it persists with the saved result, so an agent can cite the source it actually read. When the serving host differs from the requested one — the case that changes a citation — the fetch log says so and analog history names the serving URL on that result’s row. The requested URL remains the result’s identity everywhere else.

  • Section.locate_in(html) — find a section in a different rendering of its page. A result’s xpaths address the document the extraction read; a live DOM after scripts ran, or a re-fetch, can shift positional paths. locate_in finds the section there by its own record values and returns matches with xpaths valid in the document you passed. One match is a confident location; several mean the page holds several full copies of the content (a duplicate widget, a mobile/desktop double render) — pick with what only you can see, such as which copy is visible. An empty list means the section couldn’t be pinned — never a silent guess at a wrong element. The preview’s visual-withheld hint now points at it for screenshot clips.

  • Results now carry the page as Analog read it. document_view ships the exact document the response’s xpaths address, as a compact verified patch against the HTML you submitted; attach_html reconstructs it automatically. Section, outline, and navigation xpaths now resolve reliably on pages where a plain re-parse used to come up empty or silently land on the wrong element — so per-section markdown stops coming back empty on those pages, and markdown no longer includes markup that plays no part in what a reader sees. When the view is absent (an older service) or fails verification, everything degrades to parsing the submitted HTML as-is. Wire schema 24.

  • page_sweep.divergent_sections — a multi-page sweep now discloses when a later page or batch contributed a section whose fields matched no section captured earlier: the count arrives in page_sweep, the affected section carries a merge_note saying why it stands alone, and the preview adds a sentence warning that no single field spans every merged record. A sweep that fragments is never presented as one uniformly queryable collection.

  • Pages that fold detail behind in-page disclosure links now report the available content under browse_actions; expand_all=True reveals it before capture. Ordinary navigation links remain untouched.
  • Feedback submissions now return a durable receipt. Client.submit_feedback(...) exposes the opaque report_id, and both analog feedback commands print it as Receipt: so a submission can be cited during later triage. The ID is not a report-reading endpoint.

  • Built-in browser recipes are now replayable. Browser.recipe records every serializable setting that shaped a fetch, and Browser.from_recipe(...) reconstructs it. New browser-backed saved results retain that recipe in fetch_settings.browser_recipe; older saves remain readable and state that the exact recipe is unknown.

  • Compatible page areas can now arrive as a Collection. result.collections preserves the physical Section indices and identity field while providing one canonical local records view. Repeated editorial placements remain available through the member Sections; Collection records select one whole authored placement and never fuse values across placements. A complete Collection powers result.records, CSV, YAML, and DataFrame export without duplicating repeated placements. Wire schema 23.

  • page_sweep.cross_section_repeats — a multi-page sweep now discloses when the same records appear in more than one section of the merged result (two layouts of one collection that stayed separate sections), matched by a shared per-record URL. The preview adds a sentence when it’s nonzero, so a “duplicates dropped” count is never read as “all duplicates handled.”

  • The serializer family is now methods, matching to_dataframe(): result.to_json(), result.to_yaml(), result.to_csv() replace the result.json / result.yaml / result.csv properties. A serialization renders output (to_json() includes the full markdown rendering), so it reads as the call it is — and .json no longer collides with Pydantic’s inherited json() method, which broke generic Pydantic tooling’s expectations. No compatibility aliases; update call sites by adding to_ and parentheses. .markdown stays a property: it is the page’s content, cached after first render, not an export.
  • The built-in browser now preserves visible page content rendered inside open web-component boundaries instead of returning an empty host element.

  • Finite virtualized lists no longer lose rows that appear only between the first and final rendered windows during scrolling.

  • load_more guidance now refers only to content controls. Generic “More” controls in site navigation, headers, footers, menus, and dialogs are no longer offered or clicked.

  • Complete static pages no longer report incomplete feed coverage. A windowed_feed disclosure now means the browser restored content that the page removed during scrolling.

  • Pagination guidance now requires credible evidence that the current collection continues. Unrelated page=1 links and carousel slide counts no longer produce whole-page pagination claims.

  • The built-in browser now captures complete collections when a page initially shows only a preview and reveals the rest through an in-page control.

  • Extraction quality improved on complex multi-section pages, with more complete record coverage and more accurate Section boundaries.

  • Comparison results now keep their row and column identities together. Authored blank values are returned as null, visual-only values are named in withheld_columns, and comparisons that cannot be preserved as records point to markdown instead. Fit guidance recommends structured comparison only when both axes are named and no comparison fields are withheld. Wire schema 22.

  • Transient gateway failures no longer spend daily quota more than once for one extraction. Retries share the logical extraction’s quota accounting without retaining the URL, page content, or result server-side.

  • Pages with a continuously-updating DOM (live clocks, ad slots, client-side hydration) now capture much faster in the built-in browser. Such a page never sits perfectly still, so the pre-extraction scroll pass used to run to its internal cap before giving up; it now recognizes a settled page promptly and captures the same content in a fraction of the time.

  • The first help screen (analog --help) now shows the recommended one-call Python usage — from analog import analog; analog("https://example.com") — instead of the lower-level hosted client.

  • pages=N now captures script-driven pagination. In-page batches are merged under the same result contract as link-followed pages. page_sweep.mechanism is "in_page", batches_captured reports how many batches arrived, and pagination.in_page_controls discloses the available controls. If a control navigates away, captured batches are kept and the stop reason is control_navigated. See the fetching guide.

  • pagination.stated_total reports the page’s own collection count. It is the site’s claim, not Analog’s record count, so both remain visible when they differ.

  • Sticky field orders. analog reorder-fields <ref> FIELD... and analog.reorder_fields() put named fields first on a saved result and remember the order for later fetches of the same URL. --once or sticky=False applies it only to the current result; replayed rules appear in applied_orders. See work with results.

  • Comparison tables arrive as one joined section. Compared plans or models are records keyed by table_column; grouped table fields expose their heading through FieldInfo.table_group. Wire schema 17.

  • assess reports navigation_probe. The fit check can show top-level destinations from the already-fetched page before an extraction is spent. See the fit-check guide.

  • section.withheld_visual identifies glyph-only columns. The disclosure names the columns and directs the reader to a clipped screenshot rather than guessing what a glyph means. Wire schema 16.

  • Results now carry the page’s navigation and outline. result.navigation contains labeled link trees; result.outline distinguishes extracted, non-extracted, and structural regions and replaces page_sections. Both appear in preview() and are searchable with find().

  • Sections expose Section.label_url. When a section heading has one unambiguous destination, the absolute URL points to the fuller page; otherwise it remains empty.

  • Greenhouse fit checks disclose the board’s public jobs feed. The feed’s own current total is reported as result.fit.item_count, the provider-neutral replacement for product_count.

  • Uniform multi-page sweeps use less service compute while preserving the same results. Wire schema 19.

  • Outline outcomes are more specific. Empty repeated regions use no_fields_detected or no_emittable_fields; overlapping regions use overlapping_area_extracted and may point to the shipped section through superseded_by. The catch-all not_selected is retired. Wire schemas 18, 20, and 21.

  • The feedback vocabulary gains garbled_markdown for text that returned malformed or out of order.

  • Extraction quality improved on script-driven listings, comparison tables, navigation-heavy pages, and table-heavy pages.

  • --load-all reaches a wider range of clearly labeled load-more controls.

  • Bot-protection challenges no longer show pagination or fit advice derived from the challenge page.

  • assess preserves the page’s most prominent navigation destinations when a menu contains many links.

  • Automatic browser interactions no longer replace the requested page with a navigated destination. If the captured address still changes, the result discloses both addresses.

  • analog find searches navigation and outline entries as well as records. --field continues to restrict the search to records.

  • export --fields now rejects unknown fields with the available field names and guidance for heterogeneous sections.

  • analog docs, analog contact. Pointer commands: docs prints the documentation URL (pure output — analog get $(analog docs) --mode local reads the docs in the terminal); contact prints the team email and points at analog feedback for reports that go straight into triage.
  • Analog fetches only public web addresses by default. get and assess now decline local and private-network hosts and non-web schemes like file:, so a page can’t steer a fetch at localhost, an internal address, or the local disk. Pass --allow-private (allow_private=True) to reach a dev server or intranet host on purpose. Paginated sweeps follow only links on the page’s own host.
  • Signed-out analog get says what it’s doing. Without a sign-in, get returns the page’s markdown (mode=local) — it now says so on stderr and names the fix: run analog login for structured records. Previously the downgrade was silent, and a first run before login could read as “Analog returns prose.”
  • Numeric columns survive renames and narrowed exports. Renaming a numeric field (including swaps) keeps its normalized numbers under the new name, and export --envelope re-aligns per-record numbers and structural-outlier positions after --where/--sort/--limit/ --fields.
  • Detail toggles inside a card’s own header now expand. With --expand-all (expand_all=True), per-item disclosure toggles that sit inside a section’s header are recognized and opened, so their detail is captured — previously only toggles outside a header were.
  • A deactivated account ends with a clear terminal error — the commands name the state and stop, instead of a generic sign-in failure.
  • Hardened the fetch and sign-in paths for launch. The site-feed probe streams and size-caps what it downloads, so a hostile server can’t balloon memory; and sign-in, credential handling, and result parsing got a robustness pass.
  • analog help — the subcommand form of --help. analog help prints the top-level help; analog help get prints exactly what analog get --help prints; nested paths work (analog help feedback quality). -h now works everywhere as the short form.
  • The feedback label vocabulary is now version 3 — every label names what you observed in the result (no_records_found, misfielded_values, page_chrome_in_records, wrong_content_extracted, …). At open launch the vocabulary freezes to additive-only.
  • Wire schema is now 13. Section.items_detected counts the repeating items the page shows in the section’s area. Results saved by earlier versions reopen unchanged.

Paginated collections can be swept — analog(url, pages=N) (CLI: --pages N) follows the site’s own next links, politely, capped at 50, and discloses its coverage on result.page_sweep. Per-item detail toggles can be expanded (expand_all=True / --expand-all), and long feeds no longer lose records to scrolling. Also: saved results remember how they were fetched; virtualized tables disclose the page’s own declared size; result.rate_limit gains the daily quota; the package ships py.typed; analog assess mentions a site’s sitemap when robots.txt lists one; back-to-back extractions got dramatically faster (one warm browser per process); legacy-encoded pages decode correctly on the plain-GET path; and a long list of contract, disclosure, and robustness fixes landed alongside. Extraction quality improved on discussion threads (records gained a depth field), menu-styled pages, and multi-paragraph content.

analog feedback — quality reports with standardized labels, and feature requests. Extraction URLs are never logged; a quality report includes the URL you explicitly submit and never page content. result.to_dataframe(); filtered history deletion (analog rm --older-than 30d); store disk-usage reporting; partial-coverage disclosures for paginated pages; the credential moved from the OS keychain to a local owner-only file (run analog login once after upgrading); a lighter install; and the fit check reworked around measured facts and task guidance. Extraction quality improved on store and listing pages, and field-naming contracts firmed up — price is always the pay-now price, with price_excl_vat and review_count beside it where pages show them.

result.records; analog find; section-addressable markdown; field renaming as a library operation; guessed-name and page-truncation disclosures; withheld-column warnings; documented exit codes on every command. Extraction quality improved on complex HTML tables.