Skip to the page

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

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 on the Search API screen of your SpecStream workspace. New accounts start with $10 of credit, no card.
  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
    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. 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
    websearch(
    chatId: string,
    query: string,
    site?: string,
    language?: string,
    maxResults?: number
    ): Promise<WebSearchResult[]> {
    return specstreamSearch(query, site, language, maxResults);
    }
  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
    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 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, and every parameter is in the Search API docs. Questions: [email protected].

More

SpecStream for Eko covers what it does in Eko on one page. Every other tool has its guide in Connect your tools. You see the most a request can cost before it starts, and the matches are paid from your balance. Billing has the details. For help, email [email protected].