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)