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

# Convex

> @keenable/convex — a Convex component exposing search and fetch as component actions.

*Convex component*

`@keenable/convex` installs into a Convex app's `convex.config.ts` and exposes `search` and `fetch` as component actions behind a typed client. Both env vars are optional, so registering the component is the whole setup — no key, no env wiring before the first call.

→ [@keenable/convex on npm](https://www.npmjs.com/package/@keenable/convex)

## Install

### Install the package

```bash theme={"dark"}
npm install @keenable/convex
```

### Register the component

```typescript theme={"dark"}
// convex/convex.config.ts
import { defineApp } from "convex/server";
import keenable from "@keenable/convex/convex.config";

const app = defineApp();
app.use(keenable);
export default app;
```

### Call it from your own action

Wrap the component in an app-owned action rather than exposing it directly — that wrapper is where your auth and rate limiting belong.

```typescript theme={"dark"}
// convex/search.ts
import { KeenableClient, toContext } from "@keenable/convex";
import { v } from "convex/values";
import { action } from "./_generated/server";
import { components } from "./_generated/api";

const keenable = new KeenableClient(components.keenable);

export const searchWeb = action({
  args: { query: v.string() },
  handler: async (ctx, args) => {
    const { results } = await keenable.search(ctx, { query: args.query, maxResults: 5 });
    return toContext(results);   // numbered, citable sources for a prompt
  },
});
```

`toContext` renders results as numbered sources and drops whole sources at the budget rather than cutting one mid-page. `keenable.fetch(ctx, { url })` returns the page as markdown with a `truncated` flag, so a caller can tell a whole page from one the budget cut.

### Add a key to lift the rate limit

The component cannot read `process.env` — Convex passes env vars in through the app, so declare the key on the app and bind it:

```typescript theme={"dark"}
const app = defineApp({ env: { KEENABLE_API_KEY: v.string() } });
app.use(keenable, { env: { KEENABLE_API_KEY: app.env.KEENABLE_API_KEY } });
```

```bash theme={"dark"}
npx convex env set KEENABLE_API_KEY keen_...
```

<Note>
  Binding a key removes the hourly request cap. Without one, calls run on the shared public tier at [lower rate limits](/rate-limits).
</Note>

## Parameters

`search` takes `site`, `publishedAfter` / `publishedBefore`, `acquiredAfter` / `acquiredBefore`, `snippetMaxLength` (default 1000) and `maxResults`. `fetch` takes `contentMaxLength` (default 10000).
