Skip to the page

MetaGPT

Give MetaGPT's Researcher and Searcher live web search from SpecStream. MetaGPT's custom search engine takes one async function. This guide gives you that function, the two lines that wire it in, and what each search costs.

SpecStream for MetaGPTAgent frameworks & builders

What you need

  • MetaGPT, with an LLM set in ~/.metagpt/config2.yaml. The Researcher reads the pages it finds with a browser engine, so install one too: pip install "metagpt[playwright]".
  • A SpecStream account. New accounts get $10 of credit, no card. Create one.
  1. 1. Create an API key

    In your SpecStream workspace, open Agents & API, choose API keys, then Create API key, and set a total limit. Copy the key from the next screen. SpecStream shows it once.

    Keep it in an environment variable where MetaGPT runs:

    Terminal
    export SPECSTREAM_API_KEY="YOUR_SPECSTREAM_API_KEY"
  2. 2. Write the search function

    MetaGPT calls a custom engine as run_func(query, max_results=max_results, as_string=as_string). With as_string=False it expects a list of dicts with title, link and snippet. With as_string=True it takes one string.

    This function sends the query to the Search API and returns both shapes. It uses aiohttp, which MetaGPT already installs:

    specstream_search.py
    import os
    import aiohttp
    SPECSTREAM_SEARCH = "https://api.specstream.ai/v1/web-search"
    async def specstream_search(query: str, max_results: int = 8, as_string: bool = True):
    headers = {"Authorization": f"Bearer {os.environ['SPECSTREAM_API_KEY']}"}
    body = {"q": query, "num": max_results}
    async with aiohttp.ClientSession() as session:
    async with session.post(SPECSTREAM_SEARCH, json=body, headers=headers) as response:
    response.raise_for_status()
    reply = await response.json()
    results = [
    {"title": source.get("title") or "", "link": source["url"], "snippet": source.get("snippet") or ""}
    for source in reply["data"]["sources"][:max_results]
    ]
    if as_string:
    return "\n\n".join(f"{r['title']}\n{r['link']}\n{r['snippet']}" for r in results)
    return results
  3. 3. Point MetaGPT's search at it

    Set api_type to SearchEngineType.CUSTOM_ENGINE and search_func to the function, before you create a role. MetaGPT builds each role's search engine from config.search, so the Researcher's CollectLinks action now searches through SpecStream.

    A YAML file cannot hold a function, so this setting lives in code. Leave the search block of config2.yaml as it is, or remove it.

    research.py
    import asyncio
    from metagpt.config2 import config
    from metagpt.configs.search_config import SearchEngineType
    from metagpt.roles.researcher import Researcher
    from specstream_search import specstream_search
    config.search.api_type = SearchEngineType.CUSTOM_ENGINE
    config.search.search_func = specstream_search
    async def main():
    role = Researcher(language="en-us")
    await role.run("tensorflow vs. pytorch")
    asyncio.run(main())
  4. 4. Run it

    Run python research.py. The Researcher writes its search questions, searches each one through SpecStream, reads the top pages with its browser engine and writes the report as a Markdown file in data/research under the MetaGPT root.

    To see the calls, open the Search API screen of your workspace. Each search is listed with its query and price.

What MetaGPT sends and gets back

SearchEngine.run asks for 8 results by default, and CollectLinks keeps that default. Each call of the function is one POST /v1/web-search:

MetaGPTSpecStream
queryq, in everyday words, any language, 1 to 2,000 characters
max_resultsnum, 1 to 100
titledata.sources[].title
linkdata.sources[].url
snippetdata.sources[].snippet

Use it for one Searcher only

To keep the global config as it is, pass the engine to one role. SearchEngine.from_search_func builds a custom engine from the function:

Or change one setting: the Bing engine

MetaGPT's Bing engine reads its address from bing_url, and the search block passes extra keys through to it. SpecStream answers Bing Web Search API v7 calls in Bing's shape, so this works with no code. In ~/.metagpt/config2.yaml:

Searches through the Bing address run on the fast tier. Its price is on the Search API page. Microsoft retired the Bing Search APIs on August 11, 2025, so a config that still points at Bing gets no results today. Moving off Bing Web Search v7 has the other settings.

Search quality

The SpecStream Search API scores 97.9 F-score on all 1,000 SimpleQA Verified questions and 64.0 item F1 on all 200 WideSearch tasks. Scores and method are on the benchmarks page.

Cost

Each call of the function is one Search API call at $5 per 1,000 searches of up to 10 results, from your balance, with no quote and no approval. A call that returns no results costs nothing.

A Researcher topic makes one search for each keyword the model writes, then one for each research question, four by default. New accounts start with $10 of credit, no card. Balance and billing has the details.

Remove

Delete the two config.search lines, or set api_type back to the engine you used before. To stop the key working everywhere, revoke it in Agents & API. Other ways to build with SpecStream are in agent frameworks. Questions go to [email protected].

More

SpecStream for MetaGPT covers what it does in MetaGPT 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].