--- title: Technical Landscape Researcher | Tabstack description: Turn a single question into a cited, multi-source report with /research. Shows streaming progress, fast vs balanced mode, and how to read the cited sources off the complete event. --- Ask a question, get back a synthesized report with citations. This example wraps `/research` into a small function that streams progress while the agent works, then prints the finished report and the list of sources it cited. It’s a focused demonstration of the endpoint itself: one question in, multi-source cited synthesis out, with no orchestration code of your own. For a larger agent that combines research with structured extraction, see the [Competitor Briefing Agent](/examples/competitor-briefing-agent/index.md). - [TypeScript](#tab-panel-10) - [Python](#tab-panel-11) ``` import Tabstack from "@tabstack/sdk"; const client = new Tabstack(); type Source = { title: string; url: string }; async function researchTopic(query: string) { const stream = await client.agent.research({ query, mode: "fast", // "balanced" runs a deeper, multi-source pass (requires a paid plan) }); let report: string | null = null; const sources: Source[] = []; for await (const event of stream) { switch (event.event) { case "iteration:start": // Show progress as the agent runs its search loop. process.stdout.write( `\r[researching: iteration ${event.data.iteration}/${event.data.maxIterations}]`, ); break; case "complete": report = event.data.report; for (const page of event.data.metadata.citedPages ?? []) { sources.push({ title: page.title ?? "(untitled)", url: page.url }); } break; case "error": // Task-level failures arrive inside the stream, not as an HTTP error. throw new Error(event.data.error?.message ?? "research failed"); } } console.log("\n\n" + (report ?? "(no report returned)")); console.log(`\nCited ${sources.length} sources:`); for (const source of sources) { console.log(`- ${source.title}: ${source.url}`); } return { report, sources }; } researchTopic( "What are the main approaches to cloud browser automation for AI agents, and how do they differ?", ); ``` ``` import os from tabstack import Tabstack client = Tabstack(api_key=os.environ["TABSTACK_API_KEY"]) def research_topic(query: str): report = None sources = [] for event in client.agent.research( query=query, mode="fast", # "balanced" runs a deeper, multi-source pass (requires a paid plan) ): if event.event == "iteration:start": # Show progress as the agent runs its search loop. print( f"[researching: iteration {event.data.iteration}/{event.data.max_iterations}]" ) elif event.event == "complete": report = event.data.report for page in event.data.metadata.cited_pages or []: sources.append( {"title": page.title or "(untitled)", "url": page.url} ) elif event.event == "error": # Task-level failures arrive inside the stream, not as an HTTP error. message = getattr(event.data.error, "message", None) or "research failed" raise RuntimeError(message) print("\n" + (report or "(no report returned)")) print(f"\nCited {len(sources)} sources:") for source in sources: print(f"- {source['title']}: {source['url']}") return {"report": report, "sources": sources} research_topic( "What are the main approaches to cloud browser automation for AI agents, and how do they differ?" ) ``` ## How it works - **One call, full pipeline.** `client.agent.research(...)` handles source selection, fetching, reading, and synthesis. You pass a question and consume a stream; there is no search, ranking, or citation code to write. - **Stream progress as it runs.** A research run can take from seconds to minutes. Switching on `iteration:start` gives you a live “iteration N of M” indicator. The full set of progress events (`planning:*`, `searching:*`, `writing:*`, and more on balanced mode) is documented in [Autonomous Research](/guides/research/index.md). - **The report and citations arrive on `complete`.** `event.data.report` is the synthesized report as a markdown string, and `event.data.metadata.citedPages` (`cited_pages` in Python) lists every source the report drew on. Treat a missing value as an empty list. - **Always handle `error`.** A failed run delivers an `error` event inside the stream, not an HTTP error. If you only listen for `complete`, a failure produces no output. ## Choosing a mode `fast` is the default and is right for most questions. `balanced` consults more sources and runs a deeper pass, at the cost of time, and it requires a [paid plan](/pricing/index.md). Switch by changing the `mode` argument. For the full tradeoff and the extra progress events balanced emits, see [Autonomous Research](/guides/research/index.md). If you are researching something that changes often (pricing, release notes, live stats), pass `nocache: true` to skip any cached result and force a fresh run. ## Installation - [TypeScript](#tab-panel-12) - [Python](#tab-panel-13) Terminal window ``` npm install @tabstack/sdk ``` Terminal window ``` pip install tabstack ``` Set your API key before running: Terminal window ``` export TABSTACK_API_KEY=your_api_key ```