Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

keenable

Official Python SDK for Keenable, a web search API built for AI agents: ranked results that come back with the page text already extracted, plus a fetch endpoint that returns any URL as clean markdown.

Keyless by default. Every call works with no account and no key. An optional KEENABLE_API_KEY only lifts the hourly rate limit.

pip install keenable

Search

from keenable import Keenable

keenable = Keenable()  # or Keenable("keen_...") / set KEENABLE_API_KEY

results = keenable.search("how wafer scale chips avoid the memory bottleneck")

for result in results:
    print(result.title, result.url)
    print(result.snippet)

Results carry the page text in snippet. (description is the page's meta description and is empty for most pages, so build prompts from snippet.)

Filters are optional and combine freely:

results = keenable.search(
    "post-training quantization results",
    site="arxiv.org",
    published_after="2026-01-01",
    snippet_max_length=500,
)
Argument What it does
site Restrict to one domain, e.g. "arxiv.org"
published_after / published_before Filter by publication date (YYYY-MM-DD)
acquired_after / acquired_before Filter by when Keenable indexed the page
snippet_max_length Cap the page text returned per result
mode Search mode; "pro" (default) does deeper retrieval

Ground a model in one line

to_context() renders the result set as a numbered, citable block you can drop straight into a prompt:

context = keenable.search("cerebras inference benchmarks").to_context()

prompt = f"Answer using only these sources, citing them by number.\n\n{context}\n\nQuestion: ..."

It emits [1] Title (url) headers followed by the page text, adds results whole until the character budget is reached (max_chars=12000 by default), and never truncates a source mid-sentence.

To print a source list that matches the citations in the answer, ask which results were actually rendered rather than listing them all:

results = keenable.search("cerebras inference benchmarks")

for index, result in enumerate(results.cited(), start=1):
    print(f"[{index}] {result.title} - {result.url}")

Read a full page

page = keenable.fetch("https://cerebras.ai/chip")
print(page.title)
print(page.content)  # markdown, boilerplate stripped
print(page.to_context())  # same citable block shape as search results

Tool calling

The SDK ships OpenAI-compatible tool definitions, so any inference API that speaks that schema can call Keenable directly:

from keenable import Keenable, TOOLS, run_tool_call

keenable = Keenable()
response = client.chat.completions.create(model=..., messages=messages, tools=TOOLS)

for call in response.choices[0].message.tool_calls or []:
    messages.append(
        {
            "role": "tool",
            "tool_call_id": call.id,
            "content": run_tool_call(
                keenable, call.function.name, call.function.arguments
            ),
        }
    )

TOOLS exposes keenable_search and keenable_fetch; run_tool_call executes whichever the model picked and returns text ready for the tool message. Both tools render the same numbered, citable block, so the model can cite a fetched page the way it cites a search result. arun_tool_call is the async counterpart, for use with AsyncKeenable.

Async

AsyncKeenable mirrors the sync client:

import asyncio
from keenable import AsyncKeenable


async def main():
    async with AsyncKeenable() as keenable:
        results = await keenable.search("wafer scale engine")
        print(results.to_context())


asyncio.run(main())

Errors

All errors subclass KeenableError:

Exception When
KeenableRateLimitError HTTP 429. Set KEENABLE_API_KEY to lift the keyless cap
KeenableAuthError HTTP 401/403, the key was rejected
KeenableAPIError Any other non-2xx response; carries status_code
KeenableConnectionError The API could not be reached
KeenableInvalidRequestError Bad arguments; no request was sent

Configuration

Variable Default Purpose
KEENABLE_API_KEY unset Lifts the hourly rate limit. Create one at keenable.ai/console
KEENABLE_API_URL https://api.keenable.ai Override the API base URL

Both can also be passed to the constructor as api_key and base_url, alongside timeout and client_source. Building an integration on top of this SDK? Set client_source="Your Integration" so your traffic is attributed to you rather than to the bare SDK.

License

MIT