Full examples
Complete, copy-pasteable programs for the common tasks. Each one is a full file —
set FINN_URL and FINN_API_KEY in your environment and run it.
Run with node file.mjs. Requires Node ≥ 18.
Run with python file.py. Requires Python ≥ 3.9.
Stream an answer
Section titled “Stream an answer”Open a connection, stream the answer token by token, then print the final result.
import { FinnClient, FinnError } from '@riseanalytics/finn-sdk';
const finn = new FinnClient({ url: process.env.FINN_URL, apiKey: process.env.FINN_API_KEY, timeoutMs: 15000,});
await finn.connect();try { const turn = finn.sendMessage('How many active members do I have?'); for await (const chunk of turn) { process.stdout.write(chunk.text); } const result = await turn.completed; console.log('\n---'); console.log('conversationId:', result.conversationId); console.log('tokenUsage:', result.tokenUsage);} catch (err) { if (err instanceof FinnError) console.error(`\nFinn failed (${err.code}): ${err.message}`); else throw err;} finally { await finn.disconnect();}import asyncio, osfrom finn_sdk import FinnClient, FinnError
async def main(): finn = FinnClient( url=os.environ["FINN_URL"], api_key=os.environ["FINN_API_KEY"], timeout_ms=15000, ) await finn.connect() try: turn = await finn.send_message("How many active members do I have?") async for chunk in turn: print(chunk.text, end="", flush=True) result = await turn.completed print("\n---") print("conversationId:", result.conversation_id) print("tokenUsage:", result.token_usage) except FinnError as err: print(f"\nFinn failed ({err.code}): {err.message}") finally: await finn.disconnect()
asyncio.run(main())One-shot answer with ask()
Section titled “One-shot answer with ask()”When you only want the final text, skip the loop.
import { FinnClient } from '@riseanalytics/finn-sdk';
const finn = new FinnClient({ url: process.env.FINN_URL, apiKey: process.env.FINN_API_KEY });await finn.connect();
const { text, tokenUsage } = await finn.ask('Summarize member growth this quarter.');console.log(text);console.log('tokens:', tokenUsage);
await finn.disconnect();import asyncio, osfrom finn_sdk import FinnClient
async def main(): async with FinnClient(url=os.environ["FINN_URL"], api_key=os.environ["FINN_API_KEY"]) as finn: result = await finn.ask("Summarize member growth this quarter.") print(result.text) print("tokens:", result.token_usage)
asyncio.run(main())Structured JSON with askJson()
Section titled “Structured JSON with askJson()”Ask for JSON and get a parsed object back. Tell Finn “JSON only” in the prompt.
import { FinnClient, FinnError } from '@riseanalytics/finn-sdk';
const finn = new FinnClient({ url: process.env.FINN_URL, apiKey: process.env.FINN_API_KEY });await finn.connect();
try { const rows = await finn.askJson(` How many active members did I have over the last 3 months? Return ONLY a JSON array like [{ "month": "2026-06", "active_members": 0 }]. No prose, no markdown. `); for (const row of rows) console.log(row.month, '->', row.active_members);} catch (err) { if (err instanceof FinnError && err.code === 'invalid_json') { console.error('Not JSON. Raw answer:\n', err.details.raw); } else throw err;} finally { await finn.disconnect();}import asyncio, osfrom finn_sdk import FinnClient, FinnError
async def main(): async with FinnClient(url=os.environ["FINN_URL"], api_key=os.environ["FINN_API_KEY"]) as finn: try: rows = await finn.ask_json( "How many active members did I have over the last 3 months? " 'Return ONLY a JSON array like [{"month": "2026-06", "active_members": 0}]. ' "No prose, no markdown." ) for row in rows: print(row["month"], "->", row["active_members"]) except FinnError as err: if err.code == "invalid_json": print("Not JSON. Raw answer:\n", err.details["raw"]) else: raise
asyncio.run(main())Multi-turn conversation
Section titled “Multi-turn conversation”Thread a conversation id across turns to keep context.
import { FinnClient } from '@riseanalytics/finn-sdk';
const finn = new FinnClient({ url: process.env.FINN_URL, apiKey: process.env.FINN_API_KEY });await finn.connect();
const first = await finn.ask('How many active members do I have?');console.log('Q1:', first.text);
const second = await finn.ask('How many of them joined in the last 90 days?', { conversationId: first.conversationId,});console.log('Q2:', second.text);
await finn.disconnect();import asyncio, osfrom finn_sdk import FinnClient
async def main(): async with FinnClient(url=os.environ["FINN_URL"], api_key=os.environ["FINN_API_KEY"]) as finn: first = await finn.ask("How many active members do I have?") print("Q1:", first.text)
second = await finn.ask( "How many of them joined in the last 90 days?", conversation_id=first.conversation_id, ) print("Q2:", second.text)
asyncio.run(main())Interrupt a long turn
Section titled “Interrupt a long turn”Stop generation partway through; the turn settles with interrupted set.
import { FinnClient } from '@riseanalytics/finn-sdk';
const finn = new FinnClient({ url: process.env.FINN_URL, apiKey: process.env.FINN_API_KEY });await finn.connect();
const turn = finn.sendMessage('Summarize every transaction this year in detail…');setTimeout(() => turn.interrupt(), 2000);
for await (const chunk of turn) process.stdout.write(chunk.text);const result = await turn.completed;console.log('\ninterrupted?', result.interrupted);
await finn.disconnect();import asyncio, osfrom finn_sdk import FinnClient
async def main(): async with FinnClient(url=os.environ["FINN_URL"], api_key=os.environ["FINN_API_KEY"]) as finn: turn = await finn.send_message("Summarize every transaction this year in detail…") asyncio.get_event_loop().call_later(2, turn.interrupt)
async for chunk in turn: print(chunk.text, end="", flush=True) result = await turn.completed print("\ninterrupted?", result.interrupted)
asyncio.run(main())Robust error handling
Section titled “Robust error handling”Catch a FinnError and branch on its code.
import { FinnClient, FinnError } from '@riseanalytics/finn-sdk';
async function askWithRetry(finn, question, tries = 3) { for (let attempt = 1; attempt <= tries; attempt++) { try { return await finn.ask(question); } catch (err) { if (!(err instanceof FinnError)) throw err; if ((err.code === 'rate_limited' || err.code === 'timeout') && attempt < tries) { await new Promise((r) => setTimeout(r, 500 * attempt)); continue; } throw err; } }}
const finn = new FinnClient({ url: process.env.FINN_URL, apiKey: process.env.FINN_API_KEY });await finn.connect();const { text } = await askWithRetry(finn, 'How many active members do I have?');console.log(text);await finn.disconnect();import asyncio, osfrom finn_sdk import FinnClient, FinnError
async def ask_with_retry(finn, question, tries=3): for attempt in range(1, tries + 1): try: return await finn.ask(question) except FinnError as err: if err.code in ("rate_limited", "timeout") and attempt < tries: await asyncio.sleep(0.5 * attempt) continue raise
async def main(): async with FinnClient(url=os.environ["FINN_URL"], api_key=os.environ["FINN_API_KEY"]) as finn: result = await ask_with_retry(finn, "How many active members do I have?") print(result.text)
asyncio.run(main())