> ## 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.

# SDKs

> Official Python, TypeScript and Go SDKs for Keenable: install, a keyless first search, configuration, methods and error classes.

The official SDKs give you web search and page fetch in Python, TypeScript and Go, with results already shaped for a prompt.

## What you get

* `search` returns ranked pages with their text extracted. `fetch` returns one page as markdown.
* `to_context()` renders results as numbered `[n] Title (url)` sources a model can cite.
* `TOOLS` holds OpenAI-format definitions for `keenable_search` and `keenable_fetch`. A helper runs whichever tool the model calls.
* In Python, `AsyncKeenable` has the same methods as the sync client.
* Every call works with no account and no key. An API key raises your rate limit.

## Install

Install the package for your language:

```bash Python theme={"system"}
pip install keenable
```

```bash TypeScript theme={"system"}
npm install keenable
```

```bash Go theme={"system"}
go mod init example.com/keenable-demo
go get github.com/keenableai/keenable-sdk/go@v0.1.1
```

The TypeScript SDK needs Node 18 or newer. The Go SDK needs Go 1.22 or newer. Neither has runtime dependencies.

On macOS and many Linux distributions, system Python refuses a global `pip install`. Install the Python package into a virtual environment: run `python3 -m venv .venv`, then `source .venv/bin/activate`. If your Go project already has a `go.mod`, skip `go mod init`.

## Run a keyless search

This program runs one search without a key and prints the top 3 results as a citable block:

```python Python theme={"system"}
from keenable import Keenable

client = Keenable()  # keyless: no API key needed to try it
results = client.search("fastest inference providers 2026", max_results=3)
print(results.to_context())
```

```typescript TypeScript theme={"system"}
import { Keenable } from "keenable";

async function main() {
  const client = new Keenable(); // keyless: no API key needed to try it
  const results = await client.search("fastest inference providers 2026", { maxResults: 3 });
  console.log(results.toContext());
}

main();
```

```go Go theme={"system"}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/keenableai/keenable-sdk/go"
)

func main() {
	client, err := keenable.New(nil) // keyless: no API key needed to try it
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	results, err := client.Search(context.Background(), "fastest inference providers 2026",
		&keenable.SearchOptions{MaxResults: 3})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(results.ToContext(nil))
}
```

```bash curl theme={"system"}
curl -X POST https://api.keenable.ai/v1/search/public \
  -H "Content-Type: application/json" \
  -H "X-Keenable-Title: My App" \
  -d '{"query": "fastest inference providers 2026", "max_results": 3}'
```

Save it in the directory where you installed the SDK and run it:

| Language | Save as | Run |
| - | - | - |
| Python | `main.py` | `python main.py` |
| TypeScript | `index.ts` | `npx tsx index.ts` |
| Go | `main.go` | `go run .` |
| curl | | Paste the command into a terminal. |

You see one `[n] Title (url)` header per result, followed by the page text:

```text Example output · results vary theme={"system"}
[1] Hugging Face Inference Providers · Supported Models (https://huggingface.co/inference)
…

[2] Fastest LLM Inference Provider 2026: TTFT, tok/s, p99 (https://openbenchmarks.com/inference/fastest-inference-provider) - published 2026-09-03T00:00:00Z
Baseten recorded the lowest end-to-end p99 latency at 3.23 seconds on this run, with a median of 331 ms…

[3] Best LLM Inference Providers Compared for 2026 - SiliconFlow (https://siliconflow.com/articles/the-best-inference-provider-for-llms)
…
```

To raise your limit, [create an API key](https://docs.keenable.ai/api-keys.md) and set `KEENABLE_API_KEY`. The code stays the same.

## Configuration

Every SDK reads two environment variables:

| Variable | Default | Purpose |
| - | - | - |
| `KEENABLE_API_KEY` | unset | Your API key. Unset means keyless. |
| `KEENABLE_API_URL` | `https://api.keenable.ai` | The API base URL. |

You can also pass settings to the constructor:

| Setting | Python | TypeScript | Go (`keenable.Options`) |
| - | - | - | - |
| API key | `api_key` | `apiKey` | `APIKey` (pointer) |
| Base URL | `base_url` | `baseUrl` | `BaseURL` (pointer) |
| Timeout | `timeout`, in seconds (default 30) | `timeoutMs` (default 30000) | `Timeout` (default 30s) |
| Client source | `client_source` | `clientSource` | `ClientSource` |

The SDK sends the client source as the `X-Keenable-Title` header. It defaults to `Keenable SDK`. If you build an integration on top of an SDK, set it to your product's name so Keenable attributes your traffic to your product.

## Methods

| What it does | Python | TypeScript | Go |
| - | - | - | - |
| Search the web | `search(query, **filters)` | `search(query, options)` | `Search(ctx, query, *SearchOptions)` |
| Fetch one page | `fetch(url)` | `fetch(url)` | `Fetch(ctx, url)` |
| Render a citable block | `to_context()` | `toContext()` | `ToContext(nil)` |
| List the results a block cites | `cited()` | `cited()` | `Cited(nil)` |
| Tool definitions | `TOOLS` | `TOOLS` | `Tools` |
| Run a tool call | `run_tool_call` (`arun_tool_call` for async) | `runToolCall` | `RunToolCall` |

Search filters follow the [Search parameters](https://docs.keenable.ai/products/search.md#key-parameters), in each language's naming style: `published_after` in Python, `publishedAfter` in TypeScript, `PublishedAfter` in Go. SDK 0.1.x has three gaps:

* In Python and TypeScript, `mode` accepts only `pro`.
* No SDK sends `disable_cards`.
* `fetch` takes only a URL. For `live`, `prompt` and `max_chars`, call the HTTP API as shown on [Fetch](https://docs.keenable.ai/products/fetch.md#key-parameters).

## Errors

Every SDK raises typed errors that share one base class. See [Errors](https://docs.keenable.ai/errors.md) for what each status code means and how to fix it.

| When | Python | TypeScript | Go |
| - | - | - | - |
| Base class | `KeenableError` | `KeenableError` | `keenable.Error` interface |
| HTTP 429, rate limit exceeded | `KeenableRateLimitError` | `KeenableRateLimitError` | `RateLimitError` |
| HTTP 401 or 403, key rejected | `KeenableAuthError` | `KeenableAuthError` | `AuthError` |
| Any other non-2xx response | `KeenableAPIError` (`status_code`) | `KeenableAPIError` (`statusCode`) | `APIError` (`StatusCode`) |
| API could not be reached | `KeenableConnectionError` | `KeenableConnectionError` | `ConnectionError` |
| Bad arguments, no request sent | `KeenableInvalidRequestError` | `KeenableInvalidRequestError` | `InvalidRequestError` |

In 0.1.x, an HTTP 402 (out of credits) raises the generic API error. To catch it, check `status_code == 402` in Python, `statusCode === 402` in TypeScript, or `StatusCode == 402` on an `APIError` in Go.

## Source, packages and changelogs

| Language | README | Package | Changelog |
| - | - | - | - |
| Python | [python/README.md](https://github.com/keenableai/keenable-sdk/blob/main/python/README.md) | [PyPI](https://pypi.org/project/keenable/) | [python/CHANGELOG.md](https://github.com/keenableai/keenable-sdk/blob/main/python/CHANGELOG.md) |
| TypeScript | [typescript/README.md](https://github.com/keenableai/keenable-sdk/blob/main/typescript/README.md) | [npm](https://www.npmjs.com/package/keenable) | [typescript/CHANGELOG.md](https://github.com/keenableai/keenable-sdk/blob/main/typescript/CHANGELOG.md) |
| Go | [go/README.md](https://github.com/keenableai/keenable-sdk/blob/main/go/README.md) | [pkg.go.dev](https://pkg.go.dev/github.com/keenableai/keenable-sdk/go) | [go/CHANGELOG.md](https://github.com/keenableai/keenable-sdk/blob/main/go/CHANGELOG.md) |


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