# Answers in the Vercel AI SDK

`@hyperresearch/ai-sdk` adds [Answers](/answers) to the
[Vercel AI SDK](https://ai-sdk.dev) as two tools:

- **`hyperresearchAnswer`** asks one factual question and returns one short
  answer with the quotes it rests on, each checked word for word against its
  page. When no source supports an answer, it returns `not_found` instead of a
  guess. The research runs on our side, so the model makes one tool call and no
  pages land in its context.
- **`hyperresearchSearch`** returns ranked raw sources from the open web and
  about 45 structured sources (company filings, statistics agencies, scholarly
  indexes, books, government records), each with the passages that matched,
  copied word for word. No summary.

Package: [@hyperresearch/ai-sdk on npm](https://www.npmjs.com/package/@hyperresearch/ai-sdk).
Source: [github.com/jordan-gibbs/hyperresearch-answers](https://github.com/jordan-gibbs/hyperresearch-answers)
(`packages/`). MIT licensed.

## Install

```bash
npm install @hyperresearch/ai-sdk
# pnpm add @hyperresearch/ai-sdk · yarn add @hyperresearch/ai-sdk · bun add @hyperresearch/ai-sdk
```

Peer dependencies: `ai` 6 or 7, and `zod` 3.25 or later (4 works).

## API key

Create a key with the `answers:ask` scope on the
[API keys page](https://console.hyperresearch.ai/#/keys) and put it in the
environment:

```bash
export HYPERRESEARCH_API_KEY=hr_live_...
```

Use an `hr_live_` key. Answers has no fixture mode, so `hr_test_` keys are
refused. Every account gets free credit to start; prices per call are on
[Answers pricing](/answers#pricing). Keep the key out of anything you commit.

## Example

```ts

const { text } = await generateText({
  model: 'openai/gpt-5-mini',
  prompt: 'How old must a US senator be?',
  tools: {
    answer: hyperresearchAnswer(),
  },
  stopWhen: isStepCount(3),
});

console.log(text);
```

The examples use `isStepCount`, the `ai` 7 name. On `ai` 6 the same helper is
`stepCountIs`.

`hyperresearchTools()` returns both tools sharing one client:

```ts

const { text } = await generateText({
  model: 'anthropic/claude-sonnet-4.6',
  prompt: 'What was Nvidia revenue in fiscal 2026, and where is it reported?',
  tools: hyperresearchTools(),
  stopWhen: isStepCount(5),
});
```

## Exports and options

| Export | What it is |
| --- | --- |
| `hyperresearchAnswer(options?)` | The `ask` tool |
| `hyperresearchSearch(options?)` | The `search` tool |
| `hyperresearchTools(options?)` | Both, sharing one client |
| `HyperresearchClient` | The REST client the tools use, for direct calls |
| `HyperresearchError` | Thrown for any non-2xx response |

```ts
hyperresearchAnswer({
  apiKey: process.env.HYPERRESEARCH_API_KEY, // the default
  mode: 'fast',          // fix the mode instead of letting the model choose
  maxCostCents: 10,      // cap every call; a standard ask capped below its escalated price never escalates
  askTimeoutMs: 360_000, // end to end, including waiting for a long ask
});

hyperresearchSearch({
  mode: 'max',           // every provider, plus structured data rows and PDF tables
  maxResults: 5,         // 1 to 25, default 10
});
```

Without `mode`, the model picks it per call (`fast`, `standard` or `max` for
`ask`; `standard` or `max` for `search`), and `standard` is the default. Every
factory also takes `baseUrl`, `fetch`, `headers`, `maxRetries` (default 2) and
a shared `client`.

The client works on its own:

```ts

const hr = new HyperresearchClient();
const r = await hr.ask({ question: 'When was the Peace of Westphalia signed?', mode: 'fast' });
console.log(r.status, r.answer, r.evidence[0]?.quote, r.price_cents);
```

## What the model gets back

`hyperresearchAnswer`:

```json
{
  "id": "ans_01J...",
  "status": "answered",
  "answer": "At least 30 years old (US Constitution, Article I, Section 3).",
  "evidence": [
    {
      "url": "https://www.archives.gov/founding-docs/constitution-transcript",
      "title": "The Constitution of the United States: A Transcription",
      "quote": "No Person shall be a Senator who shall not have attained to the Age of thirty Years",
      "check": "verified"
    }
  ],
  "price_cents": 5
}
```

`status` is one of `answered`, `false_premise`, `unconfirmed`, `not_found` or
`error`. `caveat` appears when the check could not settle something, and
`escalated: true` when a standard ask dug further.

`hyperresearchSearch` returns `results`, each with `rank`, `url`, `title`,
`source` and `passages` (`text`, `verified`), plus `price_cents`.

Answers and passages are text from web pages. Treat them as data, not as
instructions.

## Long asks

A `max` ask can take a few minutes. The client sends
`Prefer: respond-async; wait=50`; if the answer is not ready by then, the API
answers `202` with the call's `id` and `status: "pending"`, and the client
collects it from `GET /v1/answers/{id}?wait=45` until it finishes. The call is
charged once, when it finishes. `askTimeoutMs` (default six minutes) bounds the
whole wait.

## Errors

- **Our failures** come back as `200` with `status: "error"` and
  `error.code: "engine_unavailable"`. They cost nothing.
- **Non-2xx responses** are thrown as `HyperresearchError`, carrying `status`,
  `type`, `code`, `message`, `param`, `requestId` and `retryAfter` from the
  [error envelope](/docs/errors).
- `402 insufficient_balance`: add credit in the console. `402 wallet_frozen`:
  write to support.
- `409 idempotency_key_reuse` or `idempotency_in_flight`: the client sends a
  fresh `Idempotency-Key` per call and reuses it on its own retries, so these
  mean two callers shared one key.
- `429` (rate limit or too many calls running) and `503 answers_capacity` are
  retried up to `maxRetries` times, waiting `Retry-After`. Neither is charged.

`ask` and `search` together are limited to 60 calls a minute per API key and
120 a minute per workspace. See [rate limits](/docs/errors#rate-limits).

## Without the SDK

The tools call the Answers REST API: `POST /v1/answers`,
`POST /v1/answers/search` and `GET /v1/answers/{id}` on
`https://api.hyperresearch.ai`. Every field is in the
[API reference](/docs/api/operations/tags/answers). The same two tools are also
an MCP server at `https://answers.hyperresearch.ai/mcp`; see [Answers](/answers).
