Skip to main content

Python client for the Aesops dataset API — browse the catalog and load datasets into pandas, polars, or DuckDB.

Project description

aesops

Python client for the Aesops dataset API.

Browse the full catalog without any credentials. Load datasets directly into pandas, polars, or a DuckDB connection (with range-request pushdown, so only the row-groups you query are fetched).

Install

pip install aesops                  # core: httpx + duckdb
pip install 'aesops[pandas]'        # + pandas
pip install 'aesops[polars]'        # + polars
pip install 'aesops[pandas,polars]' # both

Requires Python 3.9+.

Quickstart

from aesops import Client

# No key needed to browse — the full catalog is public
client = Client()

datasets = client.list(query="housing", license="MIT")
for ds in datasets:
    print(ds.slug, ds.row_count, ds.keyless)

# Fetch metadata + schema (works for any dataset, keyless or not)
ds = client.load_dataset("kenya-housing-prices")
print(ds.name, ds.row_count, ds.column_count)
for col in ds.detail.columns:
    print(col.name, col.dtype)

# Summary stats per column — no network call, no API key needed; built from
# the metadata already fetched by load_dataset()
print(ds.describe())
# ┌────────┬────────┬───────┬────────────┬────────┬────────┬─────────┬──────┬────────┬────────┬────────┐
# │ column │  dtype │ count │ null_count │ null_% │ unique │    mean │  std │    min │ median │    max │
# ├────────┼────────┼───────┼────────────┼────────┼────────┼─────────┼──────┼────────┼────────┼────────┤
# │   year │ number │   178 │          0 │    0.0 │     16 │ 2018.50 │ 4.30 │ 2011.0 │ 2018.50 │ 2026.0 │
# │  month │ string │   178 │          0 │    0.0 │     12 │     NaN │  NaN │    NaN │    NaN │    NaN │
# └────────┴────────┴───────┴────────────┴────────┴────────┴─────────┴──────┴────────┴────────┴────────┘

# In a Jupyter/IPython notebook (or Zed's REPL), the same call renders as a
# bordered HTML table instead when it's the last expression in a cell.

# Want a real pandas.DataFrame for further chaining (.loc, filtering, etc.)?
ds.describe().to_frame()

# A compact overview — name, slug, description, AI insights, link to the
# dataset's Aesops page, and any linked community discussions. Also no
# network call, no API key needed.
print(ds.summary())

Full catalog

list() always returns the full catalog and never sends the API key even if the Client has one configured:

client = Client()
datasets = client.list(category="finance")
for ds in datasets:
    print(ds.slug, ds.keyless)  # keyless tells you which need a key to load

This is deliberate: the catalog is public for discovery/marketing purposes. What's gated is loading actual data (see below).

Loading data

Loading a dataset's actual rows (.to_pandas(), .to_polars(), .to_duckdb(), .sql(), .to_csv()) requires a read-scoped API keyunless the dataset is currently keyless (ds.keyless), in which case no key is needed. Create a key at aesops.co.ke/profile/api-keys.

client = Client(api_key="Aes_...")

ds = client.load_dataset("kenya-housing-prices")

# pandas
df = ds.to_pandas()

# polars
lf = ds.to_polars()

# DuckDB (predicate + projection pushdown — only fetches the row-groups you touch)
con = ds.to_duckdb()
con.sql("SELECT county, avg(price) FROM data GROUP BY county").df()

# or use ds.sql() directly
ds.sql("SELECT county, avg(price) FROM data GROUP BY 1").df()

# CSV export (client-side, no extra server compute)
ds.to_csv("/tmp/housing.csv")

# Just want a peek? `limit` is pushed down through read_parquet, so it also
# cuts what crosses the network, not just what lands in the DataFrame.
sample = ds.to_pandas(limit=100)
ds.to_polars(limit=100)
ds.to_csv("/tmp/sample.csv", limit=100)

# for anything more specific (offset, filters, ordering) use ds.sql() directly
ds.sql("SELECT * FROM data ORDER BY price DESC LIMIT 20").df()

New to DuckDB?

ds.sql() and ds.to_duckdb().sql() return a duckdb.DuckDBPyRelation — a lazy query result, not a DataFrame. Call .df() for pandas, .pl() for polars, .arrow() for an Arrow table, or .fetchall() for plain Python tuples. If SQL or DuckDB itself is new to you: the DuckDB docs are a good starting point, and the Python client guide covers the API this SDK builds on.

Brand colors

The Aesops chart palette is available as plain hex strings, so a chart built from Aesops data can match the platform's own look — and printing a palette renders actual color swatches, not just hex text:

from aesops import colors

colors.primary          # '#155f6b' — brand teal
colors.aeschart         # 6-slot categorical series for chart colors, teal-led
colors.dark.primary     # '#D4956A' — dark mode swaps primary to terracotta
colors.dark.aeschart    # dark mode's chart series

# See the actual chart colors, not just hex codes: in a terminal, prints
# each of the 6 aeschart slots as a colored block (ANSI truecolor) next to
# its name and hex value — separately for light and dark.
print(colors.light)
print(colors.dark)

# In a Jupyter/IPython notebook, colors.light / colors.dark render as HTML
# swatches automatically when they're the last expression in a cell.

# matplotlib
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_prop_cycle(color=list(colors.aeschart))

No network call, no API key required — colors mirrors the same design tokens the Aesops web app itself uses (see DESIGN.md). Full field list: primary, accent, background, foreground, card, muted, border, success, destructive (each with a _foreground counterpart where relevant), and aeschart — on both the top-level (light) module and colors.light/colors.dark explicitly.

Context manager

with Client(api_key="Aes_...") as client:
    df = client.load_dataset("kenya-housing-prices").to_pandas()

Error handling

from aesops import AuthError, NotFoundError, ApiError

try:
    ds = client.load_dataset("does-not-exist")
except NotFoundError:
    print("dataset not found")
except AuthError:
    print("invalid or missing API key")
except ApiError as e:
    print(f"API error {e.status_code}: {e}")

Notes

  • Signed URLs are fetched lazily at query time (not at load_dataset) and automatically re-fetched on expiry or a 403 from storage, so long-running sessions never stall.
  • All format conversion (CSV, Arrow, etc.) happens locally — zero extra server compute.
  • list() and load_dataset() always work without an api_key — the full catalog and every dataset's metadata are public. Loading actual data (.to_pandas(), .to_polars(), .to_duckdb(), .sql(), .to_csv()) requires a key unless the dataset is currently keyless (ds.keyless).
  • .describe() and .summary() never touch the network or require a key — they summarize the metadata load_dataset() already fetched. .describe() returns a DescribeTable (bordered text/HTML display, no pandas required); call .to_frame() on it for a real pandas.DataFrame. .summary() returns a Summary (same bordered text/HTML display) with name, slug, description, AI insights, a link to the dataset's Aesops page, and any linked community discussions.
  • Datasets are always served at their latest active version — there's no version pinning.

Project details


Download files

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

Source Distribution

aesops-0.1.0.tar.gz (204.1 kB view details)

Uploaded Source

Built Distribution

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

aesops-0.1.0-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

Details for the file aesops-0.1.0.tar.gz.

File metadata

  • Download URL: aesops-0.1.0.tar.gz
  • Upload date:
  • Size: 204.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for aesops-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1c6c2a77784d825c8fda211b64317a3dd1d3f6519ff893334a935f56a9f6f93e
MD5 f2b7ad91e1aebe57c9c5c4fc821a7438
BLAKE2b-256 c9ea73f7f1b512fd0a4600979b78e737d0f4c9bbed013d44cb9003b362d0fd2b

See more details on using hashes here.

Provenance

The following attestation bundles were made for aesops-0.1.0.tar.gz:

Publisher: sdk-python-publish.yml on aesopske/aesops

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

File details

Details for the file aesops-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: aesops-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for aesops-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 79617de719977e656ba0940495e34792c19821a9c114bda65fcc8629a9417124
MD5 183c1ea26fc4309cca045cd64f73755a
BLAKE2b-256 e0e3eaab6e2fdcf6609d10853c279ae5c32c3e1b6b25d76310c62f2122ecec84

See more details on using hashes here.

Provenance

The following attestation bundles were made for aesops-0.1.0-py3-none-any.whl:

Publisher: sdk-python-publish.yml on aesopske/aesops

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