Metadata-Version: 2.4
Name: analog-sdk
Version: 0.8.2
Summary: Python SDK for Analog — the perception layer for LLMs. Understand any website, in a format built for AIs.
Project-URL: Homepage, https://getanalog.io
Author-email: Marcus Campbell <marcus@getanalog.io>
License: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: keyring>=24.0
Requires-Dist: lxml>=5.2
Requires-Dist: markdownify>=0.11
Requires-Dist: playwright>=1.49
Requires-Dist: protego>=0.4
Requires-Dist: pydantic>=2.6
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13
Requires-Dist: typing-extensions>=4.4
Description-Content-Type: text/markdown

# analog-sdk

Python SDK for [Analog](https://getanalog.io) — the perception layer for
LLMs. Understand any website, in a format built for AIs.

## Installation

```bash
uv tool install analog-sdk
```

This puts the `analog` command on your PATH in its own isolated
environment. [`uv`](https://docs.astral.sh/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:

```bash
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:

```bash
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:

```bash
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

```python
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.

## 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:

```python
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 with `analog.open(handle)`. 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).

### 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:

```bash
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:

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

Subsequent `describe` / `export` / `distinct` use the new names.

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

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

`--where` supports `field=value`, `field!=value`, and `field~value` (contains);
repeat `--where` to AND conditions. To read a single field's distinct values:

```bash
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:

```bash
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):

```bash
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:

```bash
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](./LICENSE).
