> This page is for Structured Data Models Client.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/sdgm/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/sdgm/_mcp/server.

# Python SDK API

> Public Python interfaces for connecting to a Kumo Relational NIM

Install the SDK and any data-source extras by following [Deploy, Install, and Connect](/sdgm/kumo-relational/get-started/sdk-getting-started). This reference documents the public `sdm_client` interfaces used by the Kumo Relational workflow.

> **Note**
>
> What is public is defined precisely: everything named in `sdm_client.__all__`, `sdm_api.__all__`, `sdm_connectors.__all__`, and `sdm_connectors.databricks.__all__`. Anything beginning with `_`, and every submodule not re-exported at the top level (`sdm_client.core`, `.adapters`, `.materializers`, `.wire`), is an implementation detail that can change in any release. `sdm_relational_engine` is reached only through `sdm_client`; importing it directly is at your own risk. See the [stability contract](https://github.com/NVIDIA/structured-data-models-client/blob/main/docs/reference/stability.md) for the full rules and the deprecation policy.

Import the supported public interfaces from `sdm_client`:

```python
from sdm_client import (
    SDMClient, Schema, Request, PredictiveQuery, KumoRelational, PredictionResult,
)
```

## `SDMClient`

```text
SDMClient(
    url: str,
    api_key: str | None = None,
    *,
    verify_ssl: bool = True,
    timeout: float = 60.0,
    max_retries: int = 3,
)
```

A connection to one NIM, serving every model it can reach. Use it as a context manager for a scoped sequence of requests. For an application or notebook that makes multiple requests, create one client, reuse it, and call `close()` when finished. Calling `close()` retires the client permanently; construct a new client instead of attempting to reuse a closed one.

| Method                                               | Description                                                                                                                                                                                        |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `health_ready()`                                     | Checks whether the NIM is ready to serve requests.                                                                                                                                                 |
| `models()`                                           | Model ids **the endpoint** advertises, read from `/v1/models`.                                                                                                                                     |
| `capabilities(model)`                                | What `model` supports, as far as this client knows. Client-side, tagged `source='sdk'`; needs no live connection.                                                                                  |
| `predict(request, *, model, outputs=None)`           | Answer `request` with `model`. Returns a `PredictionResult`.                                                                                                                                       |
| `fit(request, *, model, outputs=None, holdout=None)` | Pin `request`'s context on the endpoint and return a `FittedModel`. Raises `SESSIONS_UNSUPPORTED` if the endpoint does not serve sessions, or if the request materialized into more than one call. |
| `close()`                                            | Releases pooled connections and permanently retires the client.                                                                                                                                    |

Several clients can target different endpoints or tenants concurrently, including from multiple threads. Each prediction remains bound to the endpoint and credential of the client that started it.

### Connecting through Databricks Model Serving

```text
SDMClient.for_databricks_serving(
    endpoint: str,
    *,
    workspace_client: Any | None = None,
    timeout: float | None = None,
) -> SDMClient
```

```python
with SDMClient.for_databricks_serving("kumo-relational") as client:
    result = client.predict(request, model=KumoRelational())
```

`endpoint` is the model's name on the serving endpoint, not a URL — there is no key, since the call authenticates through the Databricks SDK with `workspace_client`'s identity (the notebook's own, when omitted). Requires `pip install "structured-data-models-client[relational,databricks-serving]"` for the Kumo Relational workflow. `fit()` is refused there: a serving endpoint answers one request at a time and has no session to pin a context in. There is no `for_snowflake_serving`.

## `KumoRelational`

```python
from sdm_client import KumoRelational

client.predict(request, model=KumoRelational())
```

A thin, stateless handle naming the relational model. Requires the `structured-data-models-client[relational]` extra, which pulls in the native `structured-data-models-relational-engine` — CPython 3.10 through 3.13 only.

|                                           |                                                                                                                                                                                                                                                                                            |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Task kinds                                | `classification`, `binary_classification`, `multiclass_classification`, `regression`, `forecasting`, `temporal_link_prediction`                                                                                                                                                            |
| Output fields declared                    | `prediction`, `probabilities`, `scores`, `rankings`, `embeddings`, `quantiles`, `explanation`                                                                                                                                                                                              |
| Output fields a typical deployment serves | `prediction`, `probabilities`, `quantiles` natively; `rankings` via client-side decomposition. `scores`, `embeddings`, and `explanation` are commonly refused — see [Prediction Types](/sdgm/kumo-relational/user-guide/make-predictions/prediction-types#what-your-deployment-actually-serves) |
| Request source                            | Must come from `PredictiveQuery(...).materialize(...)`; a directly-built `Request` is refused, even with `related_tables=`                                                                                                                                                                 |
| Extra required                            | `structured-data-models-client[relational]`                                                                                                                                                                                                                                                |

## `Schema`

```text
Schema.from_data(
    tables: Mapping[str, pandas.DataFrame],
    *,
    primary_keys: Mapping[str, str | Sequence[str]] | None = None,
    time_columns: Mapping[str, str | None] | None = None,
    relationships: Sequence[Relationship] | None = None,
) -> Schema

Schema.from_source(
    source: Source,
    tables: Mapping[str, str] | None = None,
    *,
    primary_keys: Mapping[str, str | Sequence[str]] | None = None,
    time_columns: Mapping[str, str | None] | None = None,
    relationships: Sequence[Relationship] | None = None,
    check_keys: bool = True,
) -> Schema
```

`from_data()` builds a schema from DataFrames already in memory, inferring dtype, semantic type, primary key, time column, and relationships for every table. `from_source()` describes tables that stay where they are — a warehouse or database — without reading any table in full; building the schema still queries the source for column metadata, a row preview per table, and (unless `check_keys=False`) a key-uniqueness scan. Only `materialize()` samples the rows a prediction actually needs, with SQL. See [Build a Schema](/sdgm/kumo-relational/user-guide/build-a-relational-graph/graph-creation).

```python
schema = Schema.from_data({"customers": customers, "orders": orders})
schema["customers"].primary_key = "customer_id"
schema["orders"].time_column = "order_time"
```

Index it by table name, and index a table by column name. There is no `visualize()`, `print_metadata()`, or `print_links()` — inspect a schema with `print(schema)`, `schema.tables`, and `schema.relationships`. Call `schema.check_consistent()` to validate. `schema.conversion_notes` records what a semantic-model conversion resolved, for reading — its wording is not part of the contract.

## `Relationship`

```text
Relationship(source_table: str, source_column: str | Sequence[str], target_table: str, target_column: str | Sequence[str])
```

A foreign key pointing at a table's primary key, directed from the many side to the one side. See [Define Relationships](/sdgm/kumo-relational/user-guide/build-a-relational-graph/graph-definitions).

## `PredictiveQuery`

```text
PredictiveQuery(query: str, data: Schema | Mapping[str, pandas.DataFrame])

.materialize(
    *,
    indices: Sequence[Any] | None = None,
    predict: pandas.DataFrame | None = None,
    num_context_samples: int | None = None,
    model: str | Model | None = None,
    candidates: Sequence[Any] | None = None,
    task: str | None = None,
    num_neighbors: Sequence[int] | None = ...,
    num_hops: int | None = ...,
    anchor_time: Any = ...,
    context_anchor_time: Any = ...,
    use_prediction_time: bool = ...,
    lag_timesteps: int = ...,
    random_seed: int | None = ...,
    max_pq_iterations: int = ...,
    exclude_cols_dict: Mapping[str, Sequence[str]] | None = ...,
    return_embeddings: bool = ...,
    top_k: int | None = ...,
    prediction_statistic: str = ...,
    rows_per_call: int | None = ...,
    inference_config: Mapping[str, Any] | None = ...,
) -> Request
```

Resolves and validates the query when `materialize()` builds a request. For a relational schema, `indices` selects existing entities by key; the direct-row form (`predict=`) is tabular-only. `candidates=` is required for a `RANK TOP K` query. See [Configuration](/sdgm/kumo-relational/user-guide/configure-and-scale/configuration) for every option and its default, and [Limits](/sdgm/kumo-relational/user-guide/configure-and-scale/configuration#limits) for size caps.

## `Request`

The wire envelope minus the two things that are `predict()` arguments: which model answers it, and which output fields you want back. For Kumo Relational, build it only through `PredictiveQuery(...).materialize(...)`.

Read-only properties: `is_relational`, `context_rows`, `predict_rows`, `nothing_to_score`. `request.task` is a `Task(kind, target, entity_table, options)`; `request.task.target` is a `Target(column_name, dtype, table_name)`; `request.context`/`request.predict` are each a `Split(instance_table, related_tables)`. These are read-only descriptions of a request, not something you construct by hand.

## `SourceTable`

What `schema["table"]` returns for a table built with `Schema.from_source()`, in place of a `Table`. Holds column metadata and keys but no rows — sampling reads them from the source when a query is materialized. Keys, time columns, and semantic types are corrected the same way as on a `Table`.

## `ModelCapabilities`

What `client.capabilities(model)` returns: `model`, `tasks`, `outputs`, `single_table`, `multiple_tables`, `relationships`, `explanation_formats`, and `sessions`. Describes this client, not the endpoint — see [Prediction Types](/sdgm/kumo-relational/user-guide/make-predictions/prediction-types#what-your-deployment-actually-serves).

## `PredictionResult`

| Attribute / Method    | Description                                                                                                                                                                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`               | The model id that answered.                                                                                                                                                                                                                                               |
| `request_id`          | The endpoint's id for the call.                                                                                                                                                                                                                                           |
| `items`               | The response items, in the order the endpoint returned them.                                                                                                                                                                                                              |
| `metadata`            | Whatever the endpoint reported alongside the predictions.                                                                                                                                                                                                                 |
| `explanations`        | `.gradients` maps prediction identities to gradient records; `.columns` holds labelled-context cohort statistics or `None`. Requires a compatible client and deployment; see [Explainability](/sdgm/kumo-relational/user-guide/explain-predictions/prediction-explainability). |
| `unscored_candidates` | For a ranking: the candidates left out because no entity's sampled context linked to them, in the order passed. Empty when every candidate was scored.                                                                                                                    |
| `to_pandas()`         | The predictions as a DataFrame. See [Prediction Results](/sdgm/kumo-relational/user-guide/work-with-prediction-results/prediction-results).                                                                                                                                    |
| `absent(requested)`   | Which of `requested` the answer does not carry.                                                                                                                                                                                                                           |

## `FittedModel`

Returned by `SDMClient.fit()`. `predict(request)` scores rows against the pinned context; `close()` releases it. Use as a context manager or call `close()` explicitly. The constructor is not public — call `client.fit()`.

## Errors

```python
from sdm_client import (
    StructuredDataError, UnknownModelError, UnsupportedRequestError,
    MissingExtraError, NimRequestError,
)
```

Every call on `SDMClient` raises `StructuredDataError` or a subclass, so `except StructuredDataError` catches anything the client reports. Every error carries a stable `code`; branch on it and on the exception type, never on the message text — message wording is not part of the public contract.

| Class                     | `code`                     | Raised when                                                                                                                                                                                       |
| ------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UnknownModelError`       | `UNKNOWN_MODEL`            | `model` is not one the contract carries.                                                                                                                                                          |
| `UnsupportedRequestError` | `UNSUPPORTED_REQUEST`      | The model cannot answer this request — an unmaterialized request, more entities than a limit allows, an output field it does not produce, or a size over a limit. Raised before anything is sent. |
| `MissingExtraError`       | `MISSING_EXTRA`            | An optional dependency the call needs is not installed.                                                                                                                                           |
| `NimRequestError`         | varies, plus `status_code` | The NIM answered, and the answer was an error.                                                                                                                                                    |

A call that fails partway through a batched request carries what it already answered on the error's `details`: `answered`/`rows_answered` for entities, `parts_answered` for a forecast's completed steps.

Typed relational connection and graph-construction failures are translated to `StructuredDataError` when they cross the client boundary; `sdm_relational_engine`'s own exception types are not public API.

## `sdm_connectors`

```python
from sdm_connectors import (
    read, read_table,
    DatabricksSource, SnowflakeSource, PostgresSource, DuckDBSource, SQLiteSource,
)
from sdm_connectors.databricks import warehouse, lakebase
```

`read()`/`read_table()` load a table into a DataFrame for `Schema.from_data()`. Each `*Source` class describes tables that stay in their backend for `Schema.from_source()`, built from that backend's own driver connection. `DatabricksSource` and `SnowflakeSource` additionally offer `.semantic_model(name)` for a metric view or semantic view. See the connector pages under **Connect Data Sources**.