> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# Client Reference

`Client` is the entry point to the Python SDK: it connects to Amp's query server, and gives you the other clients.
`client.sql()` returns a `QueryBuilder`, which runs a query and returns its results. [Querying](/sdks/python/querying)
shows them in use.

```python
from amp import Client, DatasetAlias, ExplainResult, PlanNode, QueryBuilder
```

## `Client`

```python
Client(
    url: str | None = None,
    query_url: str | None = None,
    admin_url: str | None = None,
    registry_url: str | None = 'https://api.registry.amp.staging.thegraph.com',
    auth_token: str | None = None,
    auth: bool = False,
)
```

Connects to an Amp's query server, and, with its URL, to its Admin API.

| Parameter      | Type    | Default           | Description                                                                                                                           |
| -------------- | ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `url`          | string  | `None`            | The query server's URL, like `'grpc://localhost:1602'`. The same as `query_url`, which takes precedence.                              |
| `query_url`    | string  | `None`            | The query server's [Arrow Flight](/querying/arrow-flight) URL: `grpc://` or, with TLS, `grpc+tls://`. Either it or `url` is required. |
| `admin_url`    | string  | `None`            | The [Admin API](/managing-amp/admin-api)'s URL, like `'http://localhost:1610'`. Enables `datasets`, `jobs`, and `schema`.             |
| `registry_url` | string  | The SDK's own URL | The [registry client](/sdks/python/registry)'s URL. `None` disables `registry`.                                                       |
| `auth_token`   | string  | `None`            | A token to send with every request. See [Authentication](/sdks/python/authentication).                                                |
| `auth`         | boolean | `False`           | Sends the token you saved by signing in, and refreshes it. See [Authentication](/sdks/python/authentication).                         |

Without `auth_token`, the client sends the `AMP_AUTH_TOKEN` environment variable when it's set, then your saved token
when `auth=True`. Raises `ValueError: Either url or query_url must be provided for Flight SQL connection` without a
query URL. Creating a client doesn't contact Amp: the first query does.

### Properties

| Property        | Type                                                               | Description                                                                              |
| --------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| `datasets`      | [`DatasetsClient`](/sdks/python/reference/admin#datasetsclient)    | Registers, deploys, and explores datasets. Needs `admin_url`.                            |
| `jobs`          | [`JobsClient`](/sdks/python/reference/admin#jobsclient)            | Follows and stops jobs. Needs `admin_url`.                                               |
| `schema`        | [`SchemaClient`](/sdks/python/reference/admin#schemaclient)        | Computes a query's output columns. Needs `admin_url`.                                    |
| `registry`      | [`RegistryClient`](/sdks/python/reference/registry#registryclient) | The [registry client](/sdks/python/registry), which has no service to talk to right now. |
| `aliases`       | [`DatasetAliasManager`](/sdks/python/reference/client#datasetaliasmanager)                      | Your [dataset aliases](/sdks/python/querying#dataset-aliases).                           |
| `label_manager` | `LabelManager`                                                     | Your labels. See [Labels](/sdks/python/labels#clientlabel_manager).                      |

`datasets`, `jobs`, and `schema` raise `ValueError: Admin API not configured. Provide admin_url parameter to Client() …`
without `admin_url`, and `registry` raises `ValueError: Registry API not configured. …` with `registry_url=None`.

### `sql()`

```python
sql(query: str) -> QueryBuilder
```

Returns a [`QueryBuilder`](/sdks/python/reference/client#querybuilder) for a SQL query. It doesn't run the query.

### `configure_connection()`

```python
configure_connection(name: str, loader: str, config: dict) -> None
```

Names a connection for [loaders](/sdks/python/loaders#connections), and saves it, with its configuration, in
`~/.flight_sql_connections.json`, or the file the `FLIGHT_SQL_CONNECTIONS` environment variable names.

| Parameter | Type   | Description                                                                          |
| --------- | ------ | ------------------------------------------------------------------------------------ |
| `name`    | string | The connection's name, which `load()` takes as `connection`.                         |
| `loader`  | string | The loader's name, like `'postgresql'`. See [Loaders](/sdks/python/loaders#loaders). |
| `config`  | dict   | The loader's configuration, which its page documents.                                |

### `list_connections()`

```python
list_connections() -> dict[str, str]
```

Returns your saved connections' names, each with its loader's name, like `{'lake': 'deltalake'}`.

### `get_available_loaders()`

```python
get_available_loaders() -> list[str]
```

Returns the names of the loaders the SDK found, like `['bigquery', 'clickhouse', 'deltalake', 'iceberg', 'postgresql']`.
It doesn't check their packages: some loaders are listed without them, and fail when they connect.

### `configure_label()`

```python
configure_label(name: str, csv_path: str, binary_columns: list[str] | None = None) -> None
```

Loads a CSV file of labels to join with the rows you load. See [Labels](/sdks/python/labels#clientconfigure_label).

### `get_sql()`

```python
get_sql(query: str, read_all: bool = False) -> pyarrow.Table | Iterator[pyarrow.RecordBatch]
```

Runs a query, and returns all its results with `read_all=True`, or an iterator of its record batches. Unlike
`QueryBuilder`'s methods, it doesn't replace [dataset aliases](/sdks/python/reference/client#datasetaliasmanager) in the query.

### `query_and_load()`

```python
query_and_load(
    query: str,
    destination: str,
    connection_name: str,
    config: dict | None = None,
    label_config: LabelJoinConfig | None = None,
    **kwargs,
) -> LoadResult | Iterator[LoadResult]
```

The same as [`QueryBuilder.load()`](/sdks/python/reference/client#querybuilderload) without `stream=True`, for a query string, and without replacing
dataset aliases. `connection_name` is `load()`'s `connection`.

### `query_and_load_streaming()` (Unstable)

```python
query_and_load_streaming(
    query: str,
    destination: str,
    connection_name: str,
    config: dict | None = None,
    label_config: LabelJoinConfig | None = None,
    with_reorg_detection: bool = True,
    resume_watermark: ResumeWatermark | None = None,
    parallel_config: ParallelConfig | None = None,
    **kwargs,
) -> Iterator[LoadResult]
```

The same as [`QueryBuilder.load()`](/sdks/python/reference/client#querybuilderload) with `stream=True`, for a query string, except that it doesn't
add `SETTINGS stream = true` to the query: the query must end with it.

## `QueryBuilder`

`client.sql(query)` returns one. It holds a query, and runs it when you ask for its results. Each method below runs the
query, except `with_dependency()`, `to_manifest()`, and `register_as()`.

Before it sends the query, a `QueryBuilder` replaces your [dataset aliases](/sdks/python/reference/client#datasetaliasmanager) with the datasets
they stand for.

| Attribute | Type   | Description                |
| --------- | ------ | -------------------------- |
| `query`   | string | The SQL, as you passed it. |

### `QueryBuilder.to_arrow()`

```python
to_arrow() -> pyarrow.Table
```

Runs the query, and returns all its results. It keeps them: calling it again returns the same table, without running
the query again.

### `QueryBuilder.to_polars()`

```python
to_polars() -> polars.DataFrame
```

Returns `to_arrow()`'s results as a Polars DataFrame, without copying them.

### `QueryBuilder.stream()` (Unstable)

```python
stream() -> Iterator[pyarrow.RecordBatch]
```

Runs the query, and yields its results' record batches as they arrive, ending with an empty one. For a
[streaming query](/sdks/python/streaming#read-a-streaming-query), it keeps yielding as new blocks arrive, until the
stream ends, 30 seconds after it starts (see
[Streams end after 30 seconds](/sdks/python/streaming#streams-end-after-30-seconds)).

### `QueryBuilder.load()`

```python
load(
    connection: str,
    destination: str,
    config: dict | None = None,
    label_config: LabelJoinConfig | None = None,
    **kwargs,
) -> LoadResult | Iterator[LoadResult]
```

Runs the query, and loads its results with a [loader](/sdks/python/loaders).

| Parameter      | Type                                                                | Default | Description                                                                                               |
| -------------- | ------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `connection`   | string                                                              | none    | A connection's name, from `configure_connection()`. `None` with `config`.                                 |
| `destination`  | string                                                              | none    | What to load into, like a table name.                                                                     |
| `config`       | dict                                                                | `None`  | A loader configuration, with the loader's name under `loader_type`, to use instead of a named connection. |
| `label_config` | [`LabelJoinConfig`](/sdks/python/reference/loaders#labeljoinconfig) | `None`  | Labels to add to each row. See [Labels](/sdks/python/labels).                                             |

The other options, like `read_all`, `mode`, and `stream`, are in [Load options](/sdks/python/loaders#load-options) and
[Streaming options](/sdks/python/streaming#streaming-options); the rest go to the loader, except with `stream=True`,
which drops them.

Returns a [`LoadResult`](/sdks/python/reference/loaders#loadresult) with `read_all=True`, and otherwise an iterator of
them, which loads as you iterate. Raises `ValueError` when the connection doesn't exist
(`Connection 'pg' not found. Available: […]`), when `config` has no `loader_type`, when `mode` isn't a mode, and when
`parallel_config` comes without `stream=True` (`parallel_config requires stream=True`). Loading errors don't raise:
they're in the result.

### `QueryBuilder.explain()`

```python
explain(analyze: bool = False) -> ExplainResult
```

Returns the query's plan, without running the query. With `analyze=True`, runs it, and adds each step's metrics. Returns
an [`ExplainResult`](/sdks/python/reference/client#explainresult). See [See a query's plan](/sdks/python/querying#see-a-querys-plan).

### `QueryBuilder.get_sql()`

```python
get_sql(read_all: bool = False) -> pyarrow.Table | Iterator[pyarrow.RecordBatch]
```

The same as `to_arrow()` with `read_all=True`, and as `stream()` otherwise, without keeping the results.

### `QueryBuilder.with_dependency()`

```python
with_dependency(alias: str, reference: str) -> QueryBuilder
```

Declares a dataset the query reads, for `to_manifest()` and `register_as()`: `alias` is the name the SQL uses, like
`'eth'` in `eth.blocks`, and `reference` the dataset, like `'acme/eth_mainnet@1.0.0'`. Returns the builder, so calls
chain. Dataset aliases the query uses are added for you; `with_dependency()` takes precedence over them, with a
warning when they differ.

### `QueryBuilder.to_manifest()`

```python
to_manifest(table_name: str, network: str = 'mainnet') -> dict
```

Builds a derived dataset's [manifest](/datasets/manifests), with one table, `table_name`, filled by the query. Asks the
Admin API for the query's output columns, so the client needs `admin_url`. Dataset aliases registered for the latest
version are pinned to the version that's latest now. See
[Generate the manifest only](/sdks/python/admin#generate-the-manifest-only).

### `QueryBuilder.register_as()`

```python
register_as(namespace: str, name: str, version: str, table_name: str, network: str = 'mainnet') -> Dataset
```

Builds the manifest with `to_manifest()`, registers it as `namespace/name` at `version`, and returns a
[`Dataset`](/sdks/python/reference/admin#dataset), which can deploy it. See
[Turn a query into a dataset](/sdks/python/admin#turn-a-query-into-a-dataset).

## `ExplainResult`

What `explain()` returns: a query plan, which displays itself as text, or as HTML in a Jupyter or marimo notebook.

| Member           | Type                            | Description                                                                                                                                                                                                                   |
| ---------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `str(result)`    | string                          | The plan as indented text, one step per line, with each step's metrics under it.                                                                                                                                              |
| `plan_text`      | string                          | The plan as Amp sent it.                                                                                                                                                                                                      |
| `nodes`          | list of [`PlanNode`](/sdks/python/reference/client#plannode) | The plan's steps, in order, from the top.                                                                                                                                                                                     |
| `to_html()`      | string                          | The plan as HTML, with metrics color-coded by kind.                                                                                                                                                                           |
| `to_dataframe()` | `polars.DataFrame`              | One row per step: `node`, `depth`, the numeric properties, and the metrics, as numbers. Durations are in seconds, in columns ending with `_secs`; sizes like `8.0 B` and counts like `1.2K` are plain numbers; `N/A` is null. |

## `PlanNode`

A step of a query plan.

| Field           | Type            | Description                                                    |
| --------------- | --------------- | -------------------------------------------------------------- |
| `name`          | string          | The step, like `'AggregateExec'`.                              |
| `depth`         | integer         | How deep it is in the plan, from `0` at the top.               |
| `properties`    | dict of strings | Its settings, like `{'mode': 'Final'}`.                        |
| `metrics`       | dict of strings | What it did, with `analyze=True`, like `{'output_rows': '1'}`. |
| `timing`        | dict of strings | The metrics that are durations.                                |
| `counts`        | dict of strings | The metrics that count rows, bytes, or batches.                |
| `other_metrics` | dict of strings | The other metrics.                                             |

## `DatasetAliasManager`

`client.aliases`: your [dataset aliases](/sdks/python/querying#dataset-aliases), saved in
`~/.amp/cache/amp_dataset_aliases`. Each method reads the file again, so changes from other programs apply.

### `aliases.register()`

```python
register(alias: str, dataset: str, force: bool = False) -> DatasetAlias
```

Saves an alias, and prints `Registered alias 'eth' → acme/eth_mainnet@1.0.0`.

| Parameter | Type    | Default | Description                                                                                  |
| --------- | ------- | ------- | -------------------------------------------------------------------------------------------- |
| `alias`   | string  | none    | The name to use in SQL, like `'eth'`.                                                        |
| `dataset` | string  | none    | The dataset, like `'acme/eth_mainnet@1.0.0'`. Without `@` and a version, its latest version. |
| `force`   | boolean | `False` | Replaces an alias that exists.                                                               |

Raises `InvalidDatasetReferenceError` for a malformed dataset, like one without a namespace;
`DatasetAliasCollisionError` for a SQL keyword or `_`, or, with `admin_url`, a namespace that exists on the Amp; and
`DatasetAliasAlreadyExistsError` when the alias exists and `force` is `False`. When the client can't list the Amp's
namespaces, it saves the alias with a warning.

### `aliases.list()`

```python
list() -> dict[str, DatasetAlias]
```

Returns every alias, by name.

### `aliases.get()`

```python
get(alias: str) -> DatasetAlias
```

Returns an alias. Raises `DatasetAliasNotFoundError` when it doesn't exist.

### `aliases.resolve()`

```python
resolve(alias: str) -> str
```

Returns the dataset an alias stands for, like `'acme/eth_mainnet@1.0.0'`. Raises `DatasetAliasNotFoundError` when it
doesn't exist.

### `aliases.remove()`

```python
remove(alias: str) -> None
```

Deletes an alias, and prints `Removed alias 'eth'`. Raises `DatasetAliasNotFoundError` when it doesn't exist.

### `aliases.pin_dependencies()`

```python
pin_dependencies(used_aliases: Iterable[str]) -> dict[str, str]
```

Returns the datasets the given aliases stand for, with the latest version replaced by the version that's latest now,
like `{'eth': 'acme/eth_mainnet@1.0.0'}`, as `to_manifest()` uses them. Without `admin_url`, or when the Amp can't tell
the latest version, it keeps `latest`, with a warning. Skips names that aren't aliases.

## `DatasetAlias`

A saved alias.

| Field           | Type   | Description                                                  |
| --------------- | ------ | ------------------------------------------------------------ |
| `alias`         | string | Its name, like `'eth'`.                                      |
| `dataset`       | string | The dataset's namespace and name, like `'acme/eth_mainnet'`. |
| `version`       | string | The version, like `'1.0.0'`, or `'latest'`.                  |
| `registered_at` | string | When it was saved, in UTC, like `'2026-10-30T21:13:52Z'`.    |
| `reference`     | string | The dataset and version, like `'acme/eth_mainnet@1.0.0'`.    |

## Alias errors

From `amp.alias`. Each is a `DatasetAliasError`.

| Error                            | Raised when                                                                                                               |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `InvalidDatasetReferenceError`   | A dataset isn't `namespace/name` or `namespace/name@version`, or an alias is empty.                                       |
| `DatasetAliasCollisionError`     | An alias is a SQL keyword or `_`, or a namespace on the Amp.                                                              |
| `DatasetAliasAlreadyExistsError` | An alias exists, and `force` is `False`.                                                                                  |
| `DatasetAliasNotFoundError`      | An alias doesn't exist.                                                                                                   |
| `DatasetAliasResolutionError`    | The alias file isn't valid: not JSON, missing fields, or from a newer SDK. The SDK never overwrites it: fix or delete it. |
