Skip to main content

Python SDK

The taruvi package is the Python client for TaruviBase. Use it in backends, scheduled jobs, scripts, notebooks, and TaruviBase functions. It has a synchronous client and an asynchronous client with the same modules: client.database, client.storage, client.functions, and more.

This guide covers taruvi 0.2.3. It takes you from install to a first query, then explains responses, errors, timeouts, and retries.

Before you begin#

You need:

  • Python 3.10 or newer.
  • A TaruviBase app, with TARUVI_SITE_URL and TARUVI_APP_SLUG from Settings → Connect in TaruviBase Console.
  • An API key for server code. On the same page, select Generate API Key. The key is shown once; see Issue API tokens.

Install the SDK#

python -m pip install taruvi

Pin the version in your requirements file. taruvi.__version__ reports the installed version.

Create a client#

Keep credentials in environment variables or a secret store, never in source:

export TARUVI_SITE_URL="https://YOUR_SITE_HOST"
export TARUVI_APP_SLUG="APP_SLUG"
printf 'TaruviBase API key: ' && read -rs TARUVI_API_KEY && export TARUVI_API_KEY && printf '\n'
taruvi_client.py
import os

from taruvi import Client

client = Client(
api_url=os.environ["TARUVI_SITE_URL"],
app_slug=os.environ["TARUVI_APP_SLUG"],
api_key=os.environ["TARUVI_API_KEY"],
mode="sync",
)

The sync client uses a blocking httpx.Client. It works in scripts, Django views, Celery tasks, and notebooks. Call client.close() when you're done, or use it as a context manager: with Client(...) as client:.

Client() takes api_url and app_slug, then keyword options:

  • mode — "sync" or "async". Without it, the client is async inside a running event loop and sync otherwise. Set it explicitly in shared code.
  • api_key, jwt, session_token — The credential to send. Defaults to none; see Authenticate with the Python SDK.
  • timeout — Seconds to wait for each request: a whole number from 1 to 300. Defaults to 120. Other values raise pydantic's ValidationError.
  • max_retries — Retries for a request that fails to connect or times out. Defaults to 3. See Timeouts and retries.
Credentials can come from the environment

When you pass no credential, the client reads TARUVI_API_KEY, TARUVI_JWT, or TARUVI_SESSION_TOKEN from the environment or from a .env file in the working directory. A client you meant to leave unauthenticated then acts as that credential's user. A credential you pass always replaces the environment's. Check client.is_authenticated when it matters.

Make your first request#

This query reads the first 20 open tasks, sorted by title. The quickstart creates the tasks table.

list_tasks.py
from taruvi_client import client

result = (
client.database.from_("tasks")
.filter("done", "eq", False)
.sort("title", "asc")
.page_size(20)
.execute()
)

for task in result["data"]:
print(task["id"], task["title"])
print("Matching tasks:", result["total"])

execute() sends GET /api/apps/APP_SLUG/datatables/tasks/data/?ordering=title&page_size=20&done=false.

How requests work#

from_() starts a query. The builder is mutable: each method changes it and returns it for chaining. Start each request with a fresh from_() call rather than reusing a builder.

Operations are staged, then sent. get(id), create(), update(), upsert(), delete(), bulk_delete(), and delete_filtered() choose the operation. execute() sends it:

created = client.database.from_("tasks").create({"title": "Ship docs", "done": False}).execute()
task_id = created["data"][0]["id"]

client.database.from_("tasks").get(task_id).update({"done": True}).execute()

Modules share the client. client.database, client.storage, client.functions, client.secrets, client.policy, client.users, client.app, client.settings, and client.analytics all use the client's address and credential. The method reference lists them.

Read the response#

Methods return plain dictionaries and lists.

CallReturns
List read: execute() without an operation{"data": [...], "total": N}
get(id).execute(){"data": {...}, "total": None}; data is the record
create(), update(), or upsert(), then execute()The full response envelope: status, message, and data
first()The first record, or None. It requests a single row
count()An integer: the number of matching rows. It requests a single row

create() returns data as a list, even for one record. delete(id) returns an empty dict, and bulk_delete() and delete_filtered() return {"deleted_count": N, "message": ...}. When nothing matches, delete_filtered() returns the envelope instead, with the count in result["data"]["deleted_count"].

Set a page size. Without page_size(), a list read returns every matching row, and page() has no effect. The largest page size is 1,000 by default; a larger page_size() raises ValidationError rather than being reduced for you. To read everything in pages:

page = 1
while True:
result = client.database.from_("tasks").page(page).page_size(500).execute()
for task in result["data"]:
print(task["id"])
if page * 500 >= result["total"]:
break
page += 1

Handle errors#

API failures raise a TaruviError subclass. Each carries status_code, the platform's code and detail, and field-level details:

create_task.py
from taruvi import (
AuthenticationError,
AuthorizationError,
BillingError,
ConnectionError,
TaruviError,
TimeoutError,
ValidationError,
)

from taruvi_client import client

try:
client.database.from_("tasks").create({"title": ""}).execute()
except ValidationError as error:
print("Fix these fields:", error.details)
except AuthenticationError:
# Also catches NotAuthenticatedError, raised when the client has no credential.
print("The credential is missing, invalid, or expired")
except AuthorizationError as error:
print("Authenticated, but not allowed:", error.detail)
except BillingError as error:
print("Blocked by the organization's billing:", error.message)
except (TimeoutError, ConnectionError) as error:
print("TaruviBase could not be reached:", error)
except TaruviError as error:
print(error.status_code, error.code, error.message)

TimeoutError and ConnectionError are the SDK's classes, imported from taruvi. They shadow Python's built-ins of the same name in that module. The error reference lists every class.

Timeouts and retries#

  • Timeouts. Each request waits up to timeout seconds. A single function call can wait longer with client.functions.execute(..., timeout=300).
  • Retries. Up to max_retries more attempts, after 1, 2, and 4 seconds. GET, PUT, and DELETE are retried after a timeout or a dropped connection. POST and PATCH are retried only when the connection never opened, so a create or a function run is never sent twice.
  • HTTP errors are not retried. A 4xx or 5xx response raises at once.
  • Uploads and downloads aren't retried; failures still raise the SDK's TimeoutError or ConnectionError.

Next steps#