Skip to content

Jupyter / Python notebooks

Load a Stackify card directly into a pandas or polars DataFrame with the stackify Python client — no manual CSV export or REST calls required.

Install

pip install stackify

For polars support:

pip install stackify[polars]

Authenticate

The client uses the same API key auth as every other Stackify integration. See API keys to create one.

from stackify import Client

client = Client(api_key="tvk_live_YOUR_KEY")

By default the client talks to https://app.stackify.se/api/v1. Pass base_url= to point at a self-hosted instance.

Find your workspace and card IDs

workspaces = client.list_workspaces()
cards = client.list_cards(workspace_id)

list_workspaces() returns every workspace the API key's owner belongs to. list_cards(workspace_id) returns every card in a workspace (auto-paginated — pass archived=True to include archived cards instead).

Load a card into a DataFrame

df = client.card(workspace_id, card_id).to_pandas()

Or, with polars:

df = client.card(workspace_id, card_id).to_polars()

Under the hood this fetches the card as a typed Parquet export (columnar, so numbers and booleans arrive as real dtypes instead of strings) rather than JSON or CSV. Cards over 100k records are fetched via an async export job automatically — the client polls and downloads the result, no extra code needed.

PII fields are excluded from the DataFrame, same as any other Stackify export.

Save changes back to a card

df = client.card(workspace_id, card_id).to_pandas(editable=True)
df["Score"] = df["Score"] * 1.1          # edit existing rows
df["Risk"] = ["low", "high", "low"]      # a new column — becomes a new field automatically

result = client.card(workspace_id, card_id).save(df)
print(result)  # SaveResult(created=0, updated=3, new_fields=['Risk'])

to_pandas(editable=True) / to_polars(editable=True) load the card via the records API instead of the Parquet export, adding a _record_id column so save() knows which rows already exist. It's slower than the default to_pandas() for large cards, so only opt in when you intend to save back.

save():

  • Updates rows that have a _record_id (a partial merge — only the DataFrame's columns are touched; any other field on that record, including ones excluded from the export, is left alone).
  • Inserts rows without a _record_id as new records (in batches of 500).
  • Never deletes anything — records that exist on the card but aren't in the DataFrame are left untouched.
  • Creates a new field for any DataFrame column that doesn't match an existing field name, inferring the type from the column's dtype (bool → boolean, numeric → number, datetime → date, else text). This requires the API key's user to have Admin or Owner access on the card — save() checks this up front and raises before creating anything if the access isn't there.

Use in a Jupyter notebook

The plain client above works in any notebook cell as-is. For a more "connected" feel, stackify also ships an IPython magic:

%load_ext stackify.ipython
%stackify_connect --api-key tvk_live_YOUR_KEY --workspace-id 01H...

Then, in any cell:

%stackify 01H...card_id --as df

This loads the card into a variable named df in your notebook namespace. Pass --format polars to get a polars DataFrame instead, or --as my_df to use a different variable name.

%stackify_connect reads STACKIFY_API_KEY from the environment if --api-key is omitted — handy for not hard-coding keys into a notebook you might share.

Troubleshooting

  • StackifyError: 401 — the API key is invalid or has been revoked. Create a new one under SettingsIntegrationsAPI keys.
  • StackifyError: 403 — the API key's scope doesn't include read access to the target workspace, or (from save()) the DataFrame has a new column and the API key's user isn't an Admin or Owner on the card.
  • Large card times out — the client waits up to 10 minutes for an async export job to finish before raising TimeoutError. If a card regularly takes longer than that to export, contact support.