> SpecStream is the live web data engine for teams and agents. This is the Markdown copy of https://specstream.ai/docs/integrations/eko. Every page, with what SpecStream does and costs: https://specstream.ai/llms.txt

# Eko

Eko's chat agent searches the web through `ChatService.websearch`, a method your app implements. Implement it with SpecStream's Search API, and the agent's `webSearch` tool gets live results with page text, at $1 per 1,000 searches of up to 10 results.

[SpecStream for EkoAgent frameworks & builders](https://specstream.ai/integrations/eko.md)

## What you need

-   Eko 4 (`@eko-ai/eko`) with a `ChatAgent`, in Node.js, a web page or a browser extension.
-   A SpecStream API key. [Get your key](https://specstream.ai/workspace/search?utm_source=eko&utm_medium=howto) on the Search API screen of your SpecStream workspace. New accounts start with $10 of credit, no card.

1.  1

    ## 1. Write the search function

    Eko calls `websearch(chatId, query, site, language, maxResults)` and expects a list of `WebSearchResult`: `title`, `url`, `snippet` and an optional `content`. This function asks SpecStream and returns that list. `speed=fast` runs the fast search, and `passages=true` adds the text of each page closest to the query, which becomes `content`.

    specstream-search.ts

    ```typescript
    import type { WebSearchResult } from "@eko-ai/eko/types";
    
    const SPECSTREAM_API_KEY = process.env.SPECSTREAM_API_KEY ?? "";
    
    export async function specstreamSearch(
      query: string,
      site?: string,
      language?: string,
      maxResults?: number
    ): Promise<WebSearchResult[]> {
      const params = new URLSearchParams({
        q: site ? `site:${site} ${query}` : query,
        num: String(Math.min(maxResults ?? 10, 100)),
        speed: "fast",
        passages: "true",
      });
      if (language) params.set("language", language.slice(0, 2));
      const res = await fetch(`https://api.specstream.ai/v1/web-search?${params}`, {
        headers: { Authorization: `Bearer ${SPECSTREAM_API_KEY}` },
      });
      if (!res.ok) {
        console.error("SpecStream search failed", res.status, await res.text());
        return [];
      }
      const { data } = await res.json();
      return (data.sources ?? []).map((s: any) => ({
        title: s.title,
        url: s.url,
        snippet: s.snippet ?? "",
        content: s.excerpt,
      }));
    }
    ```

2.  2

    ## 2. Call it from your ChatService

    In the class that implements `ChatService` in your app, have `websearch` return the function's result. In Eko's extension example, that class is `SimpleChatService` in `chat-service.ts`, whose `websearch` returns an empty list.

    chat-service.ts

    ```typescript
    websearch(
      chatId: string,
      query: string,
      site?: string,
      language?: string,
      maxResults?: number
    ): Promise<WebSearchResult[]> {
      return specstreamSearch(query, site, language, maxResults);
    }
    ```

3.  3

    ## 3. Register the service and ask

    Set `global.chatService` before you create the `ChatAgent`, as the example's `initAgentServices` does. `ChatAgent` adds the `webSearch` tool to every chat by itself, so there is nothing else to register.

    index.ts

    ```typescript
    import { global, ChatAgent } from "@eko-ai/eko";
    
    global.chatService = new SimpleChatService();
    const chatAgent = new ChatAgent({ llms, agents });
    ```

## What Eko sends and what comes back

Eko's `webSearch` tool sends the model's `query`, its `language` (a code such as `en` or `zh`) and a `count` of 10 by default, at most 50. It passes no `site`. From each result it keeps `title`, `url` and `content`, or `snippet` when there is no content, cut to 6,000 characters.

SpecStream's reply holds `data.sources` in rank order, each with `url`, `title`, `site` and `snippet`, and `published` when the page states a date. With `passages=true`, each source also carries `excerpt`: up to about 3,500 characters of its page's text closest to the query. On the fast search, code alone picks that text, with no model reading the query. The agent gets page text with each result, where a plain snippet would give it two lines.

## Where the key lives

In Node.js, keep the key in an environment variable as above. In a browser extension, call SpecStream from the background service worker. `GET /v1/web-search` answers cross origin requests, so the same function runs there and in a web page.

A key shipped inside an extension or a page can be read by anyone who has it. Give that key a spending limit under **Agents & API**, or send the search through your own server and keep the key there.

## What each search costs

A fast search is $1 per 1,000 searches of up to 10 results. Each started block of 10 more results adds $0.0012, so a search at Eko's maximum of 50 results costs $0.0058. A search that returns nothing costs nothing.

Leave out `speed=fast` and the call runs the default search at $5 per 1,000 searches, which also returns an answer and rows. Each reply's `price_usd` says what the call cost. Searches come out of your balance with no quote and no approval, and every call is in your account's request log.

## Read whole pages

For the full text of a page, `POST https://api.specstream.ai/v1/extract` with `{"urls": [...], "format": "markdown"}` and the same key returns the main text of up to 20 pages or documents a call, at $0.80 per 1,000 pages. A page with no text is not charged. The [Extract API](https://specstream.ai/extract-api.md) page has the reply fields.

## Errors

-   `401` with `missing_api_key` or `invalid_token`: check `SPECSTREAM_API_KEY`.
-   `402`: the balance is empty, and the message links to where you add balance.
-   `403`: the key has no `finds:spend` scope or is over its spending limit. Create the key on the Search API screen, which gives it both.
-   `503` with `Retry-After`: wait that many seconds and send it again. Nothing is charged.

## More from the Search API

The Search API scores and method are on the [benchmarks page](https://specstream.ai/benchmarks.md), and every parameter is in the [Search API docs](https://specstream.ai/docs/search-api.md). Questions: [support@specstream.ai](mailto:support@specstream.ai).

## More

[SpecStream for Eko](https://specstream.ai/integrations/eko.md) covers what it does in Eko on one page. Every other tool has its guide in [Connect your tools](https://specstream.ai/docs/integrations.md). You see the most a request can cost before it starts, and the matches are paid from your balance. [Billing](https://specstream.ai/docs/billing.md) has the details. For help, email [support@specstream.ai](mailto:support@specstream.ai).
