> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polyvia.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript / TypeScript SDK

> Official JS/TS SDK for the Polyvia API

The `polyvia` npm package wraps the entire REST API in a fully-typed, ESM/CJS-compatible client.

```bash theme={null}
npm install polyvia
```

Requires Node.js 18+. Works in TypeScript and plain JavaScript.

***

## Quick Start

```ts theme={null}
import { Polyvia } from "polyvia";

const client = new Polyvia({ apiKey: "poly_<key>" });

// Ingest → wait → query
const result = await client.ingest.file("report.pdf");
await client.ingest.wait(result.task_id);
const answer = await client.query("What are the key findings?");
console.log(answer.answer);
```

<Tip>
  Prefer not to hard-code the key? The SDK also reads it from the
  `POLYVIA_API_KEY` environment variable — set `export POLYVIA_API_KEY=poly_<key>`
  and you can drop the `apiKey` option.
</Tip>

***

## Usage

### Ingest

The SDK uploads file bytes directly to Polyvia's storage backend (the API
server is not in the upload path), so there is no practical file-size cap
from the SDK side and large batches don't fail on a request-body limit.
Each file in a batch is uploaded and finalized independently — a failure
on one file is captured in `BatchIngestItem.error` and does not affect
the others.

```ts theme={null}
// Single file — accepts a file path, Buffer, or Blob.
// `group` takes the group name; it's created if it doesn't exist yet.
const result = await client.ingest.file("report.pdf", {
  name: "Q4 Report",
  group: "Finance",
});

// Multiple files (the group is resolved once for the batch)
const items = await client.ingest.batch(["q3.pdf", "q4.pdf"], {
  names: ["Q3 Report", "Q4 Report"],
  group: "Finance",
});

// Check status
const status = await client.ingest.status(result.task_id);

// Block until done — throws IngestionError on failure, IngestionTimeout on timeout
await client.ingest.wait(result.task_id, { pollInterval: 5, timeout: 300 });
```

### Query

```ts theme={null}
// All completed documents
const answer = await client.query("What risks are mentioned across all reports?");

// Single document (fastest)
const answer = await client.query("Summarise section 3.", { documentId: "doc_<id>" });

// Scoped to a group — by name (must already exist)
const answer = await client.query("Key findings?", { group: "Finance" });

// Multiple groups — by id
const answer = await client.query("Compare results.", { groupIds: ["g_<id>", "g_<id>"] });

console.log(answer.answer);
```

### Groups

Groups have a human **name** and an opaque backend **id**. Pass the name to
`ingest` / `query` and the SDK resolves it — you rarely touch the id. Use
`getOrCreate` when you want the group object itself.

```ts theme={null}
// Idempotent: returns the existing "Finance" group or creates it (matched by
// exact name, so never a duplicate).
const group = await client.groups.getOrCreate("Finance");
group.id;     // backend id, if you ever need it
group.name;   // "Finance"

// Look one up without creating it (undefined if there isn't one)
const existing = await client.groups.find("Finance");

// List
const groups = await client.groups.list();

// Delete all documents in a group, then the group itself
await client.groups.delete(group.id, { deleteDocuments: true });
```

### Documents

```ts theme={null}
// List — filter by status and/or group
const docs = await client.documents.list({ status: "completed", groupId: "g_<id>" });
const docs = await client.documents.list({ groupIds: ["g_<id>", "g_<id>"] });

// Get one
const doc = await client.documents.get("doc_<id>");

// Move to a different group / remove from group
await client.documents.update("doc_<id>", { groupId: "g_other" });
await client.documents.update("doc_<id>", { groupId: null });

// Delete
await client.documents.delete("doc_<id>");
```

### Usage & Rate Limits

```ts theme={null}
const usage = await client.usage();
console.log(usage.usage.requests.period);    // requests this calendar month
console.log(usage.usage.documents_stored);   // live document count

const limits = await client.rateLimits();
console.log(limits.limits["requests_per_minute"]);
console.log(limits.current["remaining_this_minute"]);
```

***

## Error Handling

```ts theme={null}
import {
  AuthenticationError,  // 401 — bad or missing API key
  ForbiddenError,        // 403 — document belongs to another user
  NotFoundError,         // 404 — document, group, or task not found
  RateLimitError,        // 429 — too many requests
  IngestionError,        // task finished with status "failed"
  IngestionTimeout,      // ingest.wait() exceeded its timeout
} from "polyvia";

try {
  await client.ingest.wait(taskId, { timeout: 60 });
} catch (e) {
  if (e instanceof IngestionError) console.error("Parsing failed:", e.error);
  else if (e instanceof IngestionTimeout) console.error("Timed out");
  else if (e instanceof RateLimitError) console.error("Rate limited");
  else if (e instanceof NotFoundError) console.error("Not found");
  else if (e instanceof AuthenticationError) console.error("Invalid API key");
  else throw e;
}
```

***

## MCP & Agent Tools

<Card title="MCP Server" icon="plug" href="/products/mcp">
  Connect Polyvia to Claude, OpenAI, and other AI clients via MCP — including SDK helpers, programmatic agent tools, and Claude Desktop config.
</Card>

***

## Links

<CardGroup cols={2}>
  <Card title="npm" icon="box" href="https://www.npmjs.com/package/polyvia">
    npm install polyvia
  </Card>

  <Card title="GitHub" icon="github" href="https://github.com/polyvia-ai/polyvia-sdk-typescript">
    Source code and examples
  </Card>
</CardGroup>
