Skip to main content

Python SDK for Analog — the perception layer for LLMs. Understand any website, in a format built for AIs.

Project description

analog-sdk

Python SDK for Analog — the perception layer for LLMs. Understand any website, in a format built for AIs.

Installation

uv tool install analog-sdk

This puts the analog command on your PATH in its own isolated environment. uv manages its own Python, so you don't need to set up a particular system Python first — Analog needs 3.10+. pipx install analog-sdk works identically; or, if you manage your own environment, pip install analog-sdk into a Python 3.10+ virtualenv.

Analog renders every page in a real headless browser (running its JS) before extraction, so client-side-built content — single-page apps, infinite scroll, content that hydrates client-side — is captured by default. The browser is built in (analog.Browser); its binaries download automatically on first use (~150 MB, one-time). To fetch them ahead of time — in CI or an agent harness, so the first call doesn't pause — run:

analog browser install

Prefer a plain HTTP GET (no JS)? Pass fetcher=HttpFetcher().

Authenticate

New to Analog? analog signup opens the account-creation page (invite code required during the private alpha). Then:

analog login

This opens your browser to sign in. The credential is minted straight into a local store and never shown on screen, so the model driving your session never sees it. Subsequent SDK and CLI usage picks it up automatically; sign out any time with analog logout.

Signing in requires a browser on the machine running analog login.

Try it straight from the shell — point it at a JS-rendered page that's tedious to scrape by hand:

analog get https://greylock.com/portfolio/

One call turns the rendered portfolio into structured records — every company, with sectors, founders, and links — ready to query and export.

Run analog --help to see all commands (signup, login, logout, whoami, status, get).

Usage

from analog import analog

result = analog("https://example.com")

Same one-liner whether or not the JS-aware fetcher is installed. When it is, JS-heavy pages just work.

See https://getanalog.io for full documentation.

Data flow

Extraction runs on Analog's servers. Here is what happens to a page's content when you call analog(url) (or analog get <url>):

  • Sent to Analog. The SDK fetches the page — by default rendering it in a local headless browser so client-side JavaScript runs — and sends that page content, plus the URL, to Analog's hosted extraction API, which returns the structured records.
  • Stored on your machine. The structured records and the full-page markdown, saved under a handle in your local cache (pass save=False to skip; see Saved results below).
  • Never written to disk. The raw HTML — it is used only for the extraction request and to render markdown locally, then dropped.
  • mode="local" sends nothing. The page is fetched and converted to markdown entirely on your machine — no extraction, no backend call, no account needed.

By default Analog fetches as an unauthenticated visitor: it sees only what any visitor to that URL sees, so public pages are straightforward. If you hand it authenticated or otherwise sensitive content — by passing your own html=, or a logged-in custom fetcher — that content is sent to the extraction API just the same. Point Analog at content that is acceptable to send, or use mode="local" to keep a page on your machine.

Saved results

Every analog(url) call saves its result locally, so you can re-open or re-export it later — without re-fetching or re-rendering the page:

from analog import analog, history, latest

result = analog("https://example.com")
print(result.handle)        # "20260618-k7m2p9"

# Later — rehydrated from disk, no network:
again = latest()            # the most recent result
print(again.markdown)       # full-page markdown, stored alongside the data
for meta in history():      # everything saved, newest first
    print(meta.handle, meta.url)

Re-open a specific result by handle through the results module — a separate import, because from analog import analog above rebinds the name analog to the function:

from analog import results

same = results.open(result.handle)   # re-open by handle (results.latest() / .history() too)

Artifacts live under your per-user cache directory (~/.cache/analog/results; ~/Library/Caches/analog/results on macOS; ANALOG_CACHE_DIR overrides) and store only safe derivatives — the structured result and full-page markdown, never raw HTML. Pass analog(url, save=False) to skip saving; the store is size-bounded and drops least-recently-opened results.

From the shell, analog get <url> saves and prints the handle, and analog history / analog open <handle> / analog export <handle> -f csv / analog rm <handle> manage saved results (latest works anywhere a handle does).

Exit codes are uniform across commands, so scripts can branch on $? by category instead of grepping stderr: 0 success, 1 command error, 2 usage error, 3 auth, 4 backend unreachable, 5 page fetch refused or failed (including robots.txt refusals), 6 extraction failed. The same table is printed in analog --help and on each command that can exit non-zero with a semantic code.

Inspect a result's fields

analog describe <handle> prints field-level statistics for a saved result — per field: coverage (how many records have the field), cardinality (how many distinct values), where it was extracted from, semantic traits, and a sample value. It's the quick way to tell real data from decoration before pulling records:

analog get https://greylock.com/portfolio/   # prints a handle
analog describe <handle>                       # field-stats table
analog describe <handle> -f markdown           # format: rich (default) | markdown | plain

Field names are best-effort deterministic heuristics — a starting point, not ground truth. A semantically rich page like this one names cleanly; a thin, div-soup page falls back to positional names like text_2. A positional name means the page gave the field no usable label — not that extraction failed — so read it as "what's here", then rename to taste in one batch, persisted to the saved result:

analog rename-fields <handle> text_2=title text_4=site

Subsequent describe / export / distinct use the new names.

Field names are heuristic; field values are byte-faithful. Analog never truncates extracted text — a value ending in is the page's own visible truncation (or the author's ellipsis), preserved exactly as rendered. describe flags such fields with a visually_truncated trait; the full string often sits in a title attribute on the page. The only ellipsis Analog itself introduces is in display surfaces (preview's per-value cap), and that one is explicitly marked as a display cap with the elided character count.

Narrow an export without writing any Python — pick columns with --fields, filter/sort/limit rows with --where / --sort / --limit:

analog export <handle> -f csv --fields name,sector --where sector=Fintech --sort name --limit 20

--where supports field=value, field!=value, field~value (contains), and the numeric comparisons field<value, field<=value, field>value, field>=value; repeat --where to AND conditions.

--sort and the numeric --where are value-aware. A price field keeps its honest display value ("from $5.41", "$1,299.00") yet sorts and compares by the real number — so the ten cheapest is just a sort and a limit, and "under $20" is a filter, no parsing:

analog export <handle> -f csv --fields name,price --sort price --limit 10
analog export <handle> -f csv --fields name,price --where "price < 20" --sort price

Values that carry no number ("Call for price") sort last and never match a numeric comparison. In Python, section.numeric("price") gives the parallel numbers aligned with the records.

To read a single field's distinct values:

analog distinct <handle> sector      # value-counts, most frequent first

Compare two saved results to see what changed — added/removed fields, record counts, and per-field coverage/cardinality shifts:

analog diff <handleA> <handleB>

Pages with several areas

A page often has more than one repeating area — a storefront splits its catalog across several product grids plus a reviews carousel; a reference page carries several tables. Analog extracts each as its own section, in page order, and describe lists them all (one stats table per section, with its heading).

Scope any tool to the sections you mean — --section (a heading label or a 0-based index) or --kind (every section of a kind):

analog describe <handle> --kind product             # just the product areas
analog export  <handle> -f csv --section "Solids"   # one grid, by its heading

This matters because pooling unlike sections conflates them — a product grid's name isn't a reviews carousel's reviewer name. distinct and export refuse to silently pool across sections of different shapes; scope them, or project shared columns with --fields.

"How many distinct products?" is the question multiple areas exist to answer. The catalog is spread across grids, and some products reappear in the reviews carousel — so the honest answer is the deduped distinct count of an identity field (a product URL) across the product sections:

analog distinct <handle> products_url --count --kind product
# 21 distinct products_url across 3 sections (24 total values)

--count treats the field as an identity key and counts it across sections, deduping a value that appears in more than one — the count you'd otherwise assemble by hand. analog diff is section-aware too: it matches sections across the two results by heading / kind and diffs them like-for-like.

License

MIT — see LICENSE.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

analog_sdk-0.11.0.tar.gz (187.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

analog_sdk-0.11.0-py3-none-any.whl (138.6 kB view details)

Uploaded Python 3

File details

Details for the file analog_sdk-0.11.0.tar.gz.

File metadata

  • Download URL: analog_sdk-0.11.0.tar.gz
  • Upload date:
  • Size: 187.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for analog_sdk-0.11.0.tar.gz
Algorithm Hash digest
SHA256 dd11d393d8c8e165ce48a58b0bc74d93e762ae2be770cada780a595947cfa09b
MD5 f6fede2c0574db287687f8dced8c4365
BLAKE2b-256 9b4691913d31a26255ba9bac53895c8cb02d74ffef34147e670d53805263bcf1

See more details on using hashes here.

Provenance

The following attestation bundles were made for analog_sdk-0.11.0.tar.gz:

Publisher: release-sdk-python.yml on getanalog/monorepo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file analog_sdk-0.11.0-py3-none-any.whl.

File metadata

  • Download URL: analog_sdk-0.11.0-py3-none-any.whl
  • Upload date:
  • Size: 138.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for analog_sdk-0.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 66b24ab554bcfa2677640bd6a7130615a7c62f994b0bc1c61a6ee4b5dce97066
MD5 97e438c406ef4e7c2eedfe7d739685b6
BLAKE2b-256 ac872987c1e40071abaae0e8b5d5b9482d1a585d24eeb43dff11e35c9e86e35b

See more details on using hashes here.

Provenance

The following attestation bundles were made for analog_sdk-0.11.0-py3-none-any.whl:

Publisher: release-sdk-python.yml on getanalog/monorepo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page