Python SDK: API Reference

Client Classes

Keito

from keito import Keito

client = Keito(
    api_key: str = None,        # defaults to KEITO_API_KEY env var
    account_id: str = None,     # defaults to KEITO_ACCOUNT_ID env var
    base_url: str = "https://app.keito.ai/api/v2",
    max_retries: int = 2,
    timeout: float = 30.0,
)

AsyncKeito

from keito import AsyncKeito

client = AsyncKeito(
    # Same parameters as Keito
)

Time Entries

client.time_entries.create()

entry = client.time_entries.create(
    project_id: str,            # required
    task_id: str,               # required
    spent_date: str,            # required, YYYY-MM-DD
    hours: float = None,        # completed entries only
    notes: str = None,
    is_running: bool = False,
    replace_running: bool = None,
    started_time: str = None,   # HH:mm workspace time
    ended_time: str = None,     # HH:mm workspace time
    billable: bool = None,      # defaults from project
    source: str = "api",
    metadata: dict = None,      # max 4KB
) -> TimeEntry

client.time_entries.start_timer()

timer = client.time_entries.start_timer(
    project_id: str,            # required
    task_id: str,               # required
    spent_date: str,            # required, YYYY-MM-DD
    notes: str = None,
    replace_running: bool = None,
    started_time: str = None,   # optional HH:mm past start
    source: str = "api",
    metadata: dict = None,
) -> TimeEntry

Omit started_time for a current exact start. If another timer is running, the API returns 409 Conflict unless replace_running=True.

client.time_entries.stop_timer()

entry = client.time_entries.stop_timer(
    id: str,
    notes: str = None,
) -> TimeEntry

client.time_entries.restart_timer()

entry = client.time_entries.restart_timer(
    id: str,
    replace_running: bool = None,
) -> TimeEntry

client.time_entries.list()

entries = client.time_entries.list(
    source: str = None,
    project_id: str = None,
    user_id: str = None,
    from_date: str = None,      # YYYY-MM-DD
    to_date: str = None,        # YYYY-MM-DD
    is_running: bool = None,
    page: int = None,
    per_page: int = None,
) -> Iterator[TimeEntry]

client.time_entries.update()

entry = client.time_entries.update(
    id: str,
    hours: float = None,
    notes: str = None,
    billable: bool = None,
    task_id: str = None,
    started_time: str = None,   # HH:mm workspace time
    ended_time: str = None,     # HH:mm workspace time
    metadata: dict = None,
) -> TimeEntry

client.time_entries.delete()

client.time_entries.delete(id: str) -> None

Expenses

client.expenses.create()

expense = client.expenses.create(
    project_id: str,            # required
    expense_category_id: str,   # required
    spent_date: str,            # required, YYYY-MM-DD
    total_cost: float = None,   # auto-calculated if units + unit_price
    units: float = None,
    unit_price: float = None,
    notes: str = None,
    source: str = "api",
    metadata: dict = None,
) -> Expense

client.expenses.list()

expenses = client.expenses.list(
    source: str = None,
    project_id: str = None,
    user_id: str = None,
    from_date: str = None,
    to_date: str = None,
    page: int = None,
    per_page: int = None,
) -> Iterator[Expense]

Projects

client.projects.list()

projects = client.projects.list(
    is_active: bool = None,
    client_id: str = None,
    page: int = None,
    per_page: int = None,
) -> Iterator[Project]

Types

TimeEntry

Field Type Description
id str Unique identifier
project_id str Project ID
task_id str Task ID
user_id str User ID
spent_date str Date (YYYY-MM-DD)
hours float Duration in hours
notes str or None Description
is_running bool Timer active
timer_started_at datetime or None Exact running timer start timestamp
started_time str or None Workspace time-of-day (HH:mm)
ended_time str or None Workspace time-of-day (HH:mm)
billable bool Billable status
source str Origin: web, cli, api, agent, calendar, or desktop
metadata dict or None Agent context
created_at str ISO timestamp
updated_at str ISO timestamp

Expense

Field Type Description
id str Unique identifier
project_id str Project ID
expense_category_id str Category ID
spent_date str Date (YYYY-MM-DD)
units float or None Quantity
unit_price float or None Price per unit
total_cost float Total amount
notes str or None Description
source str Origin
metadata dict or None Agent context
created_at str ISO timestamp

Page Iterators

Field Type Description
items list First fetched page of results after iteration starts
page int or None Current fetched page
per_page int or None Page size returned by the API
total_pages int or None Total pages returned by the API
total_entries int or None Total entries returned by the API

List methods return lazy iterators. Iterate over them directly:

for entry in client.time_entries.list(source=Source.AGENT):
    print(entry.id)