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

# JavaScript / TypeScript SDK

> rankraft-js — the official Node.js client for the RankRaft AI API.

[rankraft-js on npm](https://www.npmjs.com/package/rankraft-js)

## Install

```bash npm theme={"system"}
npm install rankraft-js
```

```bash pnpm theme={"system"}
pnpm add rankraft-js
```

```bash yarn theme={"system"}
yarn add rankraft-js
```

```bash bun theme={"system"}
bun add rankraft-js
```

Requires Node 20+, or any runtime with `fetch` — Cloudflare Workers, Deno and Bun included. Zero runtime dependencies. ESM and CommonJS. Fully typed.

## Quick start

```ts theme={"system"}
import { RankRaft, f } from "rankraft-js";

const rr = new RankRaft(); // reads RANKRAFT_API_KEY

const [site] = await rr.sites.list();

const page = await rr.reports.visibility({
  site_id: site!.id,
  start_date: "2026-09-01",
  end_date: "2026-09-30",
  group_by: ["platform"],
  metrics: ["visibility_score", "mention_rate", "citation_rate"],
  filter: f.and(f.in("platform", ["chatgpt", "ai_mode"]), f.not(f.is("category", "Brand"))),
});

console.log(page.info.total_results, page.data);
```

Parameter and field names are the API's own (`site_id`, `start_date`, `group_by`), never camelCase, so the [API reference](/api-reference/introduction) applies as written.

## Methods

| Method | Endpoint |
| - | - |
| `rr.me()` | [Get key details](/api-reference/endpoints/get-me) — a good connection test |
| `rr.sites.list()` · `rr.sites.get({ site_id })` | [List sites](/api-reference/endpoints/list-sites) · [Get a site](/api-reference/endpoints/get-site) |
| `rr.sites.competitors({ site_id })` | [List competitors](/api-reference/endpoints/list-competitors) |
| `rr.sites.actions({ site_id, status })` | [List actions](/api-reference/endpoints/list-actions) |
| `rr.platforms.list()` | [List answer engines](/api-reference/endpoints/list-platforms) |
| `rr.reports.visibility()` · `.citations()` · `.sentiment()` | [Visibility](/api-reference/endpoints/query-visibility), [citations](/api-reference/endpoints/query-citations), [sentiment](/api-reference/endpoints/query-sentiment) |
| `rr.answers.query()` | [List answers](/api-reference/endpoints/list-answers) |
| `rr.reports.bots()` · `.referrals()` | [AI crawler](/api-reference/endpoints/query-bots) and [AI referral](/api-reference/endpoints/query-referrals) reports |
| `rr.prompts.list()` · `.create()` · `.update()` · `.setStatus()` | [Prompts](/api-reference/endpoints/list-prompts) |
| `rr.openapi()` | [OpenAPI specification](/api-reference/endpoints/get-open-api) |
| `rr.request()` | Any route the client does not model yet |

## Pagination

Every report method is **awaitable for the first page and async-iterable for every row**.

```ts theme={"system"}
// first page
const page = await rr.reports.citations({ site_id, start_date, end_date });

// every row, every page
for await (const row of rr.reports.citations({ site_id, start_date, end_date })) {
  await warehouse.write(row);
}

// or collect everything
const rows = await rr.reports.bots({ site_id, start_date, end_date }).all();
```

The client re-sends your exact query with each cursor, so pages always line up. To change the filter, dates or grouping, start a new call.

## Filters

Pass a plain object, or build one with `f`:

```ts theme={"system"}
import { f } from "rankraft-js";

f.and(
  f.in("platform", ["chatgpt", "ai_mode"]),
  f.or(f.contains("prompt", "pricing"), f.not_contains("prompt", "free")),
);
```

Operators: `is`, `not_is`, `in`, `not_in`, `contains`, `not_contains`, all case-insensitive. Numbers and booleans are converted to strings for you (`f.is("verified", true)`). Which fields each report accepts: [Filtering](/api-reference/filtering).

## Errors

```ts theme={"system"}
import { RateLimitError, ValidationError, PlanRequiredError } from "rankraft-js";

try {
  await rr.reports.visibility({ /* … */ });
} catch (err) {
  if (err instanceof RateLimitError) console.log(err.retryAfterSeconds);
  if (err instanceof ValidationError) console.log(err.issues);
  if (err instanceof PlanRequiredError) console.log("needs an Enterprise plan");
}
```

Every error carries `status`, `code`, `details` and `requestId` — include the request id when you contact support.

| Class | Status · code |
| - | - |
| `BadRequestError` | 400 · `invalid_json`, `api_key_in_url` |
| `InvalidCursorError` | 400 · `invalid_cursor` |
| `AuthenticationError` | 401 · `invalid_api_key` |
| `PlanRequiredError` | 403 · `plan_required` |
| `PermissionDeniedError` | 403 · `insufficient_permission` |
| `NotFoundError` | 404 · `not_found` |
| `ConflictError` | 409 · `site_not_set_up` |
| `PayloadTooLargeError` | 413 · `payload_too_large` |
| `ValidationError` | 422 · `invalid_request`, `invalid_filter`, `invalid_date_range`, `invalid_timezone`, `invalid_sort` |
| `PromptLimitError` | 422 · `prompt_limit_reached` (`err.remaining`, `err.limit`) |
| `RateLimitError` | 429 · `rate_limited` (`err.retryAfterSeconds`) |
| `InternalServerError` | 5xx · `internal_error` |

## Retries

On by default, with two retries after the first attempt. A `429` is retried after exactly the `Retry-After` time. Other errors are retried only for operations that are safe to repeat. `maxRetryWaitMs` caps the total time one call may spend waiting.

```ts theme={"system"}
const rr = new RankRaft({
  maxRetries: 2,
  maxRetryWaitMs: 70_000,
  onRetry: ({ attempt, waitMs, reason }) => console.warn({ attempt, waitMs, reason }),
});
```

## Managing prompts

Needs a key with **Read and manage prompts** access. Prompts can be created, re-categorised and switched on or off, but never deleted, so their history is always kept.

```ts theme={"system"}
const result = await rr.prompts.create({
  site_id,
  prompts: [{ text: "best mixed pickle brand", category: "Discovery" }],
  dry_run: true, // preview, change nothing
});
result.created;  // how many would be new
result.existing; // already tracked (returned, not duplicated)

await rr.prompts.setStatus({ site_id, prompt_ids: ["…"], status: "disabled" });
```

## Options

| Option | Default |
| - | - |
| `apiKey` | `process.env.RANKRAFT_API_KEY` |
| `baseURL` | `process.env.RANKRAFT_BASE_URL` or `https://api.rankraft.ai/v1` |
| `timeoutMs` | `60000` per attempt |
| `maxRetries` | `2` |
| `maxRetryWaitMs` | `70000` total wait per call |
| `fetch` | global `fetch` |
| `defaultHeaders` | `{}` |
| `onRetry` | — |
| `dangerouslyAllowBrowser` | `false` — only for a browser extension or an Electron main process |

## Package

[rankraft-js on npm](https://www.npmjs.com/package/rankraft-js) · MIT licence


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.