Connecting
Tagly.build() is a context manager that opens a connection and closes it on exit. Pass your API key explicitly, or leave api_key out and set the API_KEY environment variable instead. base_url defaults to https://api.taglydb.com (or the BASE_URL environment variable), and timeout to 60 seconds.
import os
from taglydb import Tagly
with Tagly.build(api_key=os.environ["TAGLYDB_API_KEY"]) as client:
...The client exposes client.indexes, client.items (items in an index), client.simple_items (standalone items) and client.sign(). Response objects use snake_case attributes — created_at, size_bytes, content_type, next_cursor — with timestamps parsed into datetimes.
Indexes and items
index = client.indexes.create(key="support-tickets", type="json", tags={"env": "prod"})
same = client.indexes.get_by_key("support-tickets")
item = client.items.create_json(
index.id,
key="ticket-1042",
tags={"team": "billing", "priority": "high"},
data={"subject": "Refund request", "amount": 42.5},
)
# Metadata only, one page at a time
page = client.items.list(index.id, tags={"team": "billing"}, limit=50)
while page.next_cursor is not None:
page = client.items.list(
index.id, tags={"team": "billing"}, limit=50, cursor=page.next_cursor
)
# Every match with its data, in one call (refused past 16 MiB of matches)
tickets = client.items.list_with_data(index.id, tags={"team": "billing", "priority": "high"})
for ticket in tickets.items:
print(ticket.key, ticket.data)
value = client.items.get_json(index.id, item.id)
value = client.items.get_json_by_key(index.id, "ticket-1042")
full = client.items.get_full(index.id, item.id) # metadata + dataA tags= filter is a dict, so its entries are ANDed together. For OR filters (the same tag key with several values), call the HTTP API directly.
Blobs
import base64
files = client.indexes.create(key="invoices", type="blob")
with open("invoice.pdf", "rb") as f:
invoice = client.items.create_blob(
files.id,
key="invoice-1042",
data=f.read(),
content_type="application/pdf",
tags={"team": "billing"},
)
pdf: bytes = client.items.get_blob(files.id, invoice.id)
# In get_full / list_with_data, a blob's bytes arrive base64-encoded
full = client.items.get_full(files.id, invoice.id)
pdf = base64.b64decode(full.data["b64"])client.indexes
Methods
create(*, type, key=None, tags=None)→ Index- Create an index. type is "json" or "blob".
get(index_id)→ Index- Get an index by id.
get_by_key(key)→ Index- Get an index by key.
list(*, limit=None, cursor=None, tags=None, key=None)→ IndexPage- One page of your indexes: .items and .next_cursor.
client.items
Methods
create_json(index_id, *, data, tags=None, key=None)→ Item- Store a JSON value in a json index.
create_blob(index_id, *, data, content_type=…, tags=None, key=None)→ Item- Store bytes in a blob index. content_type defaults to application/octet-stream.
list(index_id, *, limit=None, cursor=None, tags=None, key=None)→ ItemPage- One page of item metadata: .items and .next_cursor.
list_with_data(index_id, *, tags=None, key=None)→ ItemWithDataPage- Every matching item with its data. Not paginated; raises PayloadTooLargeError past 16 MiB of matches.
get_json(index_id, item_id)→ JSON value- A JSON item's value.
get_blob(index_id, item_id)→ bytes- A blob item's bytes.
get_json_by_key(index_id, key)→ JSON value- A JSON item's value, by key.
get_blob_by_key(index_id, key)→ bytes- A blob item's bytes, by key.
get_full(index_id, item_id)→ ItemWithData- Metadata plus data for one item.
Standalone items
client.simple_items stores and fetches items that belong to no index — no index to create first, and keys unique across your standalone items. Items created here aren't reachable through client.items, and vice versa.
note = client.simple_items.create_json(
key="latest-report", tags={"kind": "report"}, data={"ok": True}
)
client.simple_items.get_json(note.id)
client.simple_items.get_json_by_key("latest-report")
client.simple_items.get_full_by_key("latest-report")
logo = client.simple_items.create_blob(key="logo", data=png_bytes, content_type="image/png")
client.simple_items.get_blob_by_key("logo")Methods
create_json(*, data, tags=None, key=None)→ Item- Store a standalone JSON value.
create_blob(*, data, content_type=…, tags=None, key=None)→ Item- Store standalone bytes.
get_json(item_id) / get_blob(item_id)→ JSON value / bytes- The payload, by id.
get_json_by_key(key) / get_blob_by_key(key)→ JSON value / bytes- The payload, by key.
get_full(item_id) / get_full_by_key(key)→ ItemWithData- Metadata plus data.
Signed URLs
client.sign(item_id, *, view=None, expires_in=None) mints a time-limited URL for any item you own, indexed or standalone. view is "data" (default) or "full"; expires_in is in seconds, default one hour, at most seven days. See Signed URLs for how the URL behaves.
signed = client.sign(invoice.id, view="data", expires_in=86_400)
print(signed.url, signed.expires_at)Errors
Every error response raises TaglyError or, for the statuses below, one of its subclasses. For the exact cause, read error.detail.type, which ends in one of the error types. Network failures raise HTTPConnectionError or HTTPTimeoutError, also importable from taglydb.
from taglydb import ConflictError, TaglyError
try:
client.indexes.create(key="support-tickets", type="json")
except ConflictError:
index = client.indexes.get_by_key("support-tickets")
except TaglyError as error:
print(error.status, error.detail.type, error.detail.detail, error.detail.request_id)
raiseExceptions
TaglyErrorbase class- Every API error. .status is the HTTP status; .detail is the problem details (type, title, status, detail, request_id).
ValidationError400 / 415 / 422- The request was malformed or had invalid values.
AuthenticationError401- Missing, invalid or revoked API key.
CreditsExhaustedError402- Your plan's credits are used up until Free's monthly reset, or until more credits are added.
NotFoundError404- No such index, item or key in your account.
ConflictError409- That key is already taken.
PayloadTooLargeError413- An item is too large, a list_with_data query matches more than 16 MiB, or a write would pass your plan's storage limit.
Managing API keys and reading usage aren't wrapped by the client yet — use the HTTP API or your dashboard.