Python SDK: Expenses

Use keito 0.3.0 or later and an initialized client. Discover the project’s active category before creating an expense; see category lookup.

Create and read

from keito.core.request_options import RequestOptions

expense = client.expenses.create(
    project_id=project_id,
    expense_category_id=category_id,
    spent_date="2026-10-30",
    total_cost=24.50,
    notes="Receipt imported from my application",
    source="api",
    request_options=RequestOptions(
        additional_headers={"Idempotency-Key": "receipt-import-unique-001"}
    ),
)
saved = client.expenses.get(expense.id)
print(saved.total_cost, saved.currency)

Use total_cost for amount-based categories or units for a configured per-unit category. The category supplies the unit price; unit_price is not a create argument. Persist the returned ID before uploading a file.

Receipt files

with open("/path/to/receipt.pdf", "rb") as receipt:
    client.expenses.upload_receipt(
        expense.id,
        file=receipt,
        filename="receipt.pdf",
        content_type="application/pdf",
    )
contents = client.expenses.download_receipt(expense.id)
print(len(contents))
# Only when the user explicitly wants to remove the receipt:
# client.expenses.delete_receipt(expense.id)

The file must be nonempty and at most 9 MiB. PDF, JPEG, PNG, GIF, WebP, HEIC, HEIF and TIFF are supported. Uploading replaces the current receipt. Multipart uploads are not automatically replayed; keep the expense ID and retry deliberately. Invoice attachment selection is a separate choice.

Iterate and update

for row in client.expenses.list(project_id=project_id, source="api"):
    print(row.id, row.total_cost)

client.expenses.update(expense.id, notes="Reviewed receipt")
# Explicit deletion only:
# client.expenses.delete(expense.id)

List returns a lazy iterator with no .data attribute. With AsyncKeito, use async for row in client.expenses.list(...) and await item reads/mutations. Approved, billed or locked expenses and submitted/approved timesheet weeks may prevent changes. See the REST contract for the complete policy and refund/classification endpoints.