# Tabstack for agents

Tabstack is a managed web API for models that don't come with the web. One call returns finished output: a cited answer from live sources, clean Markdown or JSON matching a schema you define, or a completed task on a public website. Never trained on. Private by default. Built by Mozilla.

- Version: 2.0
- Updated: 2026-10-01
- Canonical: https://tabstack.ai/agents.md
- API spec: https://tabstack.ai/openapi.json (OpenAPI 3.0.3; this document is a quickstart and does not restate it)
- Documentation: https://docs.tabstack.ai

## Base URL and auth

```
https://api.tabstack.ai/v1
```

Authenticate with an API key. The SDKs read `TABSTACK_API_KEY` from the
environment, so no key appears in any example below. Get one at https://console.tabstack.ai/signup;
signing in with a browser creates the account and provisions the key in one
step.

## Install

```bash
pip install tabstack    # Python
```

```bash
# CLI (macOS / Linux)
curl -fsSL https://tabstack.ai/install.sh | sh
tabstack auth login
```

```bash
# Hermes Agent plugin (Python 3.11+)
pip install tabstack-hermes
hermes plugins enable tabstack
```

```bash
npm i @tabstack/sdk     # TypeScript
```

```ts
import Tabstack from '@tabstack/sdk'

// Reads TABSTACK_API_KEY from the environment by default.
const client = new Tabstack()
```

## Endpoints

| Endpoint | SDK method | Returns | Purpose |
| --- | --- | --- | --- |
| `/research` | `client.agent.research()` | 200 OK · text/event-stream (SSE) | Answer a question with cited multi-source synthesis from the live web. Streams (SSE). |
| `/extract/markdown` | `client.extract.markdown()` | 200 OK · application/json | Fetch a URL and return its content as clean Markdown. |
| `/extract/json` | `client.extract.json()` | 200 OK · application/json | Fetch a URL and return JSON matching a schema you define. |
| `/generate/json` | `client.generate.json()` | 200 OK · application/json | Fetch a URL, transform its content per instructions, and return JSON matching a schema. |
| `/automate` | `client.agent.automate()` | 200 OK · text/event-stream (SSE) | Run a multi-step browser task on a public website from a plain-language description. Streams (SSE). |

### /research

Answer a question with cited multi-source synthesis from the live web. Streams (SSE).

```ts
import Tabstack from '@tabstack/sdk'

const client = new Tabstack()

// /research always streams Server-Sent Events.
const stream = await client.agent.research({
  query: 'Compare pricing and free tiers across the top 3 analytics platforms',
})

for await (const event of stream) {
  // The final cited report arrives on the complete event.
  if (event.event === 'complete') {
    console.log(event.data.report)
    break
  }
  if (event.event === 'error') {
    console.error(event.data.error.message)
    break
  }
}
```

```bash
tabstack agent research \
  "Compare pricing and free tiers across the top 3 analytics platforms" \
  --mode fast
```

### /extract/markdown

Fetch a URL and return its content as clean Markdown.

```ts
import Tabstack from '@tabstack/sdk'

const client = new Tabstack()

const page = await client.extract.markdown({
  url: 'https://example.com/docs/getting-started',
})

console.log(page.content)
```

```bash
tabstack extract markdown https://example.com/docs/getting-started
```

### /extract/json

Fetch a URL and return JSON matching a schema you define.

```ts
import Tabstack from '@tabstack/sdk'

const client = new Tabstack()

const product = await client.extract.json({
  url: 'https://store.example.com/products/aurora-trail-jacket',
  json_schema: {
    type: 'object',
    properties: {
      name: { type: 'string', description: 'Product name' },
      price: { type: 'number', description: 'Current price' },
      in_stock: { type: 'boolean', description: 'Whether the product is available' },
    },
  },
})

console.log(product.name, product.price)
```

```bash
tabstack extract json \
  https://store.example.com/products/aurora-trail-jacket \
  --schema '{"type":"object","properties":{"name":{"type":"string"},"price":{"type":"number"}}}'
```

### /generate/json

Fetch a URL, transform its content per instructions, and return JSON matching a schema.

```ts
import Tabstack from '@tabstack/sdk'

const client = new Tabstack()

// Use generate, not extract, when the shape you want is not on the page.
const summary = await client.generate.json({
  url: 'https://example.com/pricing',
  instructions: 'Summarize each plan and who it suits.',
  json_schema: {
    type: 'object',
    properties: {
      plans: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            name: { type: 'string' },
            suits: { type: 'string' },
          },
        },
      },
    },
  },
})

console.log(summary.plans)
```

```bash
tabstack generate json https://example.com/pricing \
  --instructions "Summarize each plan and who it suits" \
  --schema '{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object"}}}}'
```

### /automate

Run a multi-step browser task on a public website from a plain-language description. Streams (SSE).

```ts
import Tabstack from '@tabstack/sdk'

const client = new Tabstack()

// /automate always streams Server-Sent Events.
const stream = await client.agent.automate({
  task: 'Find the published delivery estimate for the standard shipping option',
  url: 'https://example.com/shipping',
})

for await (const event of stream) {
  if (event.event === 'complete') {
    console.log(event.data)
    break
  }
  if (event.event === 'error') {
    console.error(event.data.error.message)
    break
  }
}
```

```bash
tabstack agent automate \
  "Find the current status of order tracking for carrier X" \
  --url https://example.com
```

## Choosing an endpoint

**When should an agent use /research?**

Use /research when you need an answer rather than sources: it reads across live pages, checks what is missing, and returns a cited synthesis. This is the right default when the question spans more than one page.

**When should an agent use the extract endpoints?**

Use /extract/markdown to read one known page as clean text, and /extract/json when that page has a structure you can describe as a schema.

**When should an agent use /generate/json instead of /extract/json?**

Use /generate/json when the shape you want is not on the page: it transforms and summarizes content into a new structure, rather than lifting what is already there.

**When should an agent use /automate?**

Use /automate for an interactive multi-step task on a public website: navigation, clicks and forms. It operates on public pages and does not sign in on your behalf.

## Effort, caching and geotargeting

- `effort`: `min`, `standard` or `max` on the extract and generate
  endpoints. Higher effort spends more time on difficult pages.
- `mode`: `fast` or `balanced` on `/research`.
- `nocache`: set true to bypass the cache when you need a fresh read.
- `geo_target`: request the page from a given country when results differ by
  region. Supported on the extract, generate and automate endpoints, not on
  `/research`.

The defaults suit most requests. Reach for these only when a result tells you
to.

## Streaming

`/research` and `/automate` always stream Server-Sent Events. The SDK
returns an async iterable, so iterate with `for await`: read events until
`complete`, and handle `error`. The other endpoints return a single
JSON response.

## Errors and retries

| Status | SDK error class |
| --- | --- |
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `UnprocessableEntityError` |
| 429 | `RateLimitError` |
| 500+ | `InternalServerError` |

All of these extend `APIError`. Connection failures raise
`APIConnectionError` and timeouts `APIConnectionTimeoutError`. The SDK
retries `408`, `409`, `429` and `500+` twice with exponential
backoff before raising. It does not retry a `400`, `401`, `403`,
`404` or `422`, which describe the request rather than a transient
fault.

## Framework adapters

Pre-built tool definitions for the frameworks below. Each page has a markdown
twin at `https://tabstack.ai/integrations/<slug>.md`.

| Framework | Package | Language | Guide |
| --- | --- | --- | --- |
| LangChain | `@tabstack/langchain` | TypeScript, Python | https://tabstack.ai/integrations/langchain.md |
| Vercel AI SDK | `@tabstack/ai` | TypeScript | https://tabstack.ai/integrations/vercel-ai-sdk.md |
| OpenAI Agents | `@tabstack/openai-agents` | TypeScript | https://tabstack.ai/integrations/openai-agents.md |
| Claude Agent | `@tabstack/claude-agent` | TypeScript | https://tabstack.ai/integrations/claude-agent.md |
| Mastra | `@tabstack/mastra` | TypeScript | https://tabstack.ai/integrations/mastra.md |
| LlamaIndex | `@tabstack/llamaindex` | TypeScript | https://tabstack.ai/integrations/llamaindex.md |
| eve | `@tabstack/eve` | TypeScript | https://tabstack.ai/integrations/eve.md |
| Hermes Agent | `tabstack-hermes` | Python | https://tabstack.ai/integrations/hermes-agent.md |

## More

- Schema library: https://tabstack.ai/schemas (47 pre-built extraction schemas)
- Pricing and limits: https://tabstack.ai/pricing
- Data handling: https://tabstack.ai/trust
- Console: https://console.tabstack.ai
- Pilo: https://github.com/mozilla/pilo, the open-source browser automation engine behind
  `/automate`. For automation that must run on your own infrastructure,
  for example behind a login or air-gapped, run Pilo yourself.
- Site index for models: https://tabstack.ai/llms.txt