---
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
```
