---
title: Query Unity Catalog tables from Python DataFrame libraries
summary: Find, read, and append to external Delta tables in Unity Catalog from
  Polars, Daft, and pandas, with storage credentials vended by the server.
diataxis: how-to
project: unitycatalog
canonical: https://docs.unitycatalog.io/how-to/python-dataframes
companions:
  - url: "https://docs.unitycatalog.io/how-to/python-dataframes/snippets/daft_tables.py"
    kind: python
    purpose: "List and read a Unity Catalog table on local storage from Daft."
    run: "AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run daft_tables.py"
    helpers: ["https://docs.unitycatalog.io/how-to/python-dataframes/snippets/_seed.py"]
    requires-python: ">=3.11"
    services: ["unitycatalog"]
  - url: "https://docs.unitycatalog.io/how-to/python-dataframes/snippets/managed_tables_fail.py"
    kind: python
    purpose: "Pin that Polars, Daft, and pandas (deltalake) can't read or append to a catalog-managed table."
    run: "AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run https://docs.unitycatalog.io/how-to/python-dataframes/snippets/managed_tables_fail.py"
    requires-python: ">=3.11"
    services: ["unitycatalog"]
  - url: "https://docs.unitycatalog.io/how-to/python-dataframes/snippets/pandas_tables.py"
    kind: python
    purpose: "Read and append to a Unity Catalog table on S3 from pandas, through deltalake."
    run: "AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run pandas_tables.py"
    helpers: ["https://docs.unitycatalog.io/how-to/python-dataframes/snippets/_seed.py"]
    requires-python: ">=3.11"
    services: ["unitycatalog"]
  - url: "https://docs.unitycatalog.io/how-to/python-dataframes/snippets/polars_tables.py"
    kind: python
    purpose: "List, read, and append to a Unity Catalog table on S3 from Polars."
    run: "AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run polars_tables.py"
    helpers: ["https://docs.unitycatalog.io/how-to/python-dataframes/snippets/_seed.py"]
    requires-python: ">=3.11"
    services: ["unitycatalog"]
environment:
  title: "Unity Catalog 0.7.0 with simulated S3 and PostgreSQL"
  dir: "uc-docs-env/uc-aws"
  start: ["curl -fsSL https://docs.unitycatalog.io/env/uc-aws.tar.gz | tar -xz", "cd uc-docs-env/uc-aws && docker compose up -d --wait", "export AWS_ALLOW_HTTP=true", "export AWS_ENDPOINT_URL=http://localhost:9000", "export AWS_ENDPOINT_URL_S3=http://localhost:9000", "export AWS_REGION=us-east-1", "export UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog"]
  stop: "docker compose down"
  elements: ["envoy", "postgresql", "rustfs", "unityCatalogOSS"]
  index: "https://docs.unitycatalog.io/env/environments.json"
---

## Companion files

CI-tested scripts that run this page's examples end to end. A script that exits 0 has passed its checks.

They need Unity Catalog 0.7.0 with simulated S3 and PostgreSQL running. To start it (also `environment` in this file's frontmatter):

```bash
curl -fsSL https://docs.unitycatalog.io/env/uc-aws.tar.gz | tar -xz
cd uc-docs-env/uc-aws && docker compose up -d --wait
export AWS_ALLOW_HTTP=true
export AWS_ENDPOINT_URL=http://localhost:9000
export AWS_ENDPOINT_URL_S3=http://localhost:9000
export AWS_REGION=us-east-1
export UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog
```

- [`daft_tables.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/daft_tables.py) (Python): List and read a Unity Catalog table on local storage from Daft.
  - run: `AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run daft_tables.py`
  - save [`_seed.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/_seed.py) beside it first; the script imports it
- [`managed_tables_fail.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/managed_tables_fail.py) (Python): Pin that Polars, Daft, and pandas (deltalake) can't read or append to a catalog-managed table.
  - run: `AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run https://docs.unitycatalog.io/how-to/python-dataframes/snippets/managed_tables_fail.py`
- [`pandas_tables.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/pandas_tables.py) (Python): Read and append to a Unity Catalog table on S3 from pandas, through deltalake.
  - run: `AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run pandas_tables.py`
  - save [`_seed.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/_seed.py) beside it first; the script imports it
- [`polars_tables.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/polars_tables.py) (Python): List, read, and append to a Unity Catalog table on S3 from Polars.
  - run: `AWS_ALLOW_HTTP=true AWS_ENDPOINT_URL=http://localhost:9000 AWS_ENDPOINT_URL_S3=http://localhost:9000 AWS_REGION=us-east-1 UNITY_CATALOG_URL=http://localhost:8080/api/2.1/unity-catalog uv run polars_tables.py`
  - save [`_seed.py`](https://docs.unitycatalog.io/how-to/python-dataframes/snippets/_seed.py) beside it first; the script imports it

Polars, Daft, and pandas can find [Unity Catalog](https://docs.unitycatalog.io/explanation/what-is-unity-catalog "The open-source reference server implementing the Unity Catalog specification.") tables by name and read and append to them. Each library looks up the table, asks the server for temporary storage credentials, and then reads or writes the [Delta](https://delta.io "Open table-format specification: ACID transactions, schema evolution, and time travel over object storage.") files directly in storage.

All three work with external Delta tables, through [delta-rs](https://delta-io.github.io/delta-rs/ "Native Rust implementation of the Delta protocol with Python bindings."). For catalog-managed tables, use [Spark](https://docs.unitycatalog.io/how-to/configure-spark) or [DuckDB](https://docs.unitycatalog.io/how-to/duckdb), which commit through the server.

> **Prerequisites**
>
> - [Docker](https://docs.docker.com/get-started/get-docker/) with Compose v2, for the local environment.
> - Unity Catalog 0.7.0 with simulated S3 and PostgreSQL, running: [Run a local Unity Catalog server](https://docs.unitycatalog.io/how-to/run-local-server#start-the-server-with-simulated-s3).
> - An external Delta table in Unity Catalog, such as `retail.sales.orders`: [Register an existing external table](https://docs.unitycatalog.io/how-to/register-external-table).
> - For tables on S3, an [external location](https://docs.unitycatalog.io/how-to/configure-aws-storage) that covers the table's path.
> - [uv](https://docs.astral.sh/uv/getting-started/installation/).

The Polars and pandas examples use a table on S3, and the Daft examples a table on local storage, which is what each library is tested with here. The local environment above simulates S3. On real AWS, leave its `AWS_ENDPOINT_URL` and `AWS_ALLOW_HTTP` variables unset.

## Connect to the server

**Polars**

Open a Python session with Polars:

```bash
uv run --with polars==1.44.2 --with deltalake==1.6.6 python
```

`polars_tables.py`

```python
import polars as pl

catalog = pl.Catalog("http://localhost:8080", require_https=False)
```

`pl.Catalog` takes the server's base URL, without `/api/2.1/unity-catalog`. `require_https=False` allows the local `http://` address. On a secured server, pass a Unity Catalog access token as `bearer_token`. Polars marks `pl.Catalog` as unstable, so its API may change in any release.

**Daft**

Open a Python session with Daft:

```bash
uv run --with "daft[unity]==0.7.25" --with tenacity python
```

`daft_tables.py`

```python
import daft
from daft.catalog import Catalog
from daft.catalog.__unity import UnityCatalogClient

client = UnityCatalogClient("http://localhost:8080", token="not-used")
catalog = Catalog.from_unity(client)
```

Pass a `token` even though the local server has authorization disabled. Without one, Daft 0.7.25 sends an empty `Authorization` header and every request fails. On a secured server, use a Unity Catalog access token. Daft 0.7.25 exposes the client only from `daft.catalog.__unity`.

**pandas**

Open a Python session with pandas and `deltalake`:

```bash
uv run --with pandas==2.3.3 --with deltalake==1.6.6 python
```

`pandas_tables.py`

```python
import pandas as pd
from deltalake import DeltaTable, write_deltalake

UNITY = {
    "unity_workspace_url": "http://localhost:8080",
    "unity_access_token": "not-used",
    "unity_allow_http_url": "true",
}
```

pandas has no catalog support of its own. The `deltalake` package resolves `uc://catalog.schema.table` names through these `unity_*` options and returns pandas DataFrames.

## List tables

**Polars**

`polars_tables.py`

```python
for table in catalog.list_tables("retail", "sales"):
    print(table.name, table.table_type, table.storage_location)
```

```text
orders EXTERNAL s3://uc-docs/retail/orders
```

`list_catalogs()` and `list_namespaces(catalog)` list the levels above.

**Daft**

`daft_tables.py`

```python
print(catalog.list_tables("retail.sales"))
```

```text
[Identifier('retail.sales.orders')]
```

`deltalake` can't list tables. With pandas, use the [Python client](https://docs.unitycatalog.io/tutorials/python-client) to find them.

## Read a table

**Polars**

`polars_tables.py`

```python
orders = catalog.scan_table("retail", "sales", "orders")
print(orders.filter(pl.col("amount") > 5).collect())
```

`scan_table` returns a `LazyFrame`, so filters run as Polars reads the files.

**Daft**

`daft_tables.py`

```python
orders = catalog.get_table("retail.sales.orders").read()
orders.where(daft.col("amount") > 5).show()
```

**pandas**

`pandas_tables.py`

```python
orders = DeltaTable("uc://retail.sales.orders", storage_options=UNITY).to_pandas()
print(orders[orders["amount"] > 5])
```

Each library requests read credentials for the table's location from the server, and then reads the Delta files with them. For how vending works, see [Credential vending](https://docs.unitycatalog.io/explanation/credential-vending).

## Append rows

**Polars**

`polars_tables.py`

```python
new_orders = pl.DataFrame(
    {"order_id": [4], "customer": ["margaret"], "amount": [12.0]}
)
catalog.write_table(new_orders, "retail", "sales", "orders", delta_mode="append")
```

**pandas**

`pandas_tables.py`

```python
new_orders = pd.DataFrame(
    {"order_id": [4], "customer": ["margaret"], "amount": [12.0]}
)
write_deltalake(
    "uc://retail.sales.orders", new_orders, mode="append", storage_options=UNITY
)
```

The library requests read-write credentials, writes new Parquet files, and commits a new version to the table's Delta log. The server isn't involved in the commit, so it doesn't coordinate writers on an external table.

## Other table operations

Create and drop tables with the [Python client](https://docs.unitycatalog.io/tutorials/python-client) or Spark.

## Required privileges

With authorization enabled, reading needs `USE CATALOG` on the catalog, `USE SCHEMA` on the schema, and `SELECT` on the table. Appending also needs `MODIFY` on the table. Owners have all of these. See [Core concepts](https://docs.unitycatalog.io/explanation/uc-basics#ownership-and-privileges).

## Next steps

- [External tables and catalog-managed Delta tables](https://docs.unitycatalog.io/explanation/external-and-managed-tables) explains why path-based libraries can't use managed tables.
- The [Polars](https://docs.pola.rs/api/python/stable/reference/catalog/index.html), [Daft](https://docs.daft.ai/en/stable/connectors/unity_catalog/), and [delta-rs](https://delta-io.github.io/delta-rs/) documentation cover other storage options and authentication methods.
