Node SDK: Filtering & Pagination

Filtering Time Entries

All list methods accept filter parameters:

const entries = await keito.timeEntries.list({
  source: 'api',
  project_id: 'prj_abc',
  user_id: 'usr_agent_01',
  from: '2026-03-01',
  to: '2026-03-31',
  is_running: false,
  per_page: 100,
});

Available Filters

Parameter Type Description
source string Filter recorded origin; see the REST contract
project_id, client_id string Filter by project or client
task_id, task string Task ID or matching alias
is_billed, invoice_id boolean / string Billed state or linked invoice
approval_status string Approval state
exclude_calendar boolean Exclude calendar-derived records, subject to credential policy
updated_since string ISO timestamp lower bound
external_reference_id, external_reference_service string External object pair; use together
user_id string Filter by user
from string Start date (inclusive, YYYY-MM-DD)
to string End date (inclusive, YYYY-MM-DD)
is_running boolean Filter running timers only
page number Page number, starting at 1
per_page number Results per page

Pagination

The SDK uses API page pagination. Each response includes the current page, total pages, and a nextPage() helper:

const allEntries = [];

let page = await keito.timeEntries.list({
  project_id: 'prj_abc',
  page: 1,
  per_page: 200,
});

while (true) {
  allEntries.push(...page.data);
  if (!page.hasNextPage()) break;
  page = await page.nextPage();
}

console.log(`Total entries: ${allEntries.length}`);

Filtering Expenses

Expense list filters include source, project_id, client_id, user_id, from, to, is_billed, updated_since, page and per_page. It also supports invoice_id, expense_category_id and approval_status. Time-only filters such as is_running do not apply. Follow all pages and keep currencies separate when aggregating costs:

const totalsByCurrency = new Map<string, number>();
for await (const expense of await keito.expenses.list({
  source: 'api',
  project_id: 'YOUR_PROJECT_ID',
  from: '2026-10-01',
  to: '2026-10-31',
})) {
  const currency = expense.currency ?? 'unknown';
  const amount = expense.total_cost;
  if (typeof amount !== 'number') throw new Error('Expense has no amount; reconcile before totaling.');
  totalsByCurrency.set(currency, (totalsByCurrency.get(currency) ?? 0) + amount);
}
console.log([...totalsByCurrency]);

Combining Filters

Filters are combined with AND logic:

// Agent entries for a specific project in March
const entries = await keito.timeEntries.list({
  source: 'api',
  project_id: 'prj_abc',
  from: '2026-03-01',
  to: '2026-03-31',
});