Skip to the page

CrewAI

Give a CrewAI agent any list from the live web. Most web tools hand an agent pages to read, and the agent builds the list itself, one search and one scrape at a time; SpecStream hands it the finished list. The crewai-specstream package adds two tools: the agent asks in plain words, like “dental clinics in Lisbon with online booking”, and gets back every matching row, each with the page it was read from. You set what one list may cost, in code. The model can’t raise it.

Install

Terminal
pip install -U crewai-specstream
export SPECSTREAM_API_KEY=ss_live_...

Create the key under Agents & API in your workspace (more on keys). A free account comes with $10 of work to use within 30 days and needs no card: sign up. The package works with CrewAI 1.x on Python 3.10 to 3.13.

A crew that finds lists

Python
from crewai import Agent, Crew, Task
from crewai_specstream import specstream_tools
analyst = Agent(
role="Funding analyst",
goal="Answer questions about who is raising money, with a source for every claim",
backstory="You work from lists, not from memory. You cite the page each row came from.",
tools=specstream_tools(max_price_usd=5),
)
task = Task(
description=(
"Find US startups that raised $5M or more in a Form D filed after October 8, 2026. "
"Which industries lead? Link the filing for each company you name."
),
expected_output="The three leading industries, with the companies and their filing links.",
agent=analyst,
)
print(Crew(agents=[analyst], tasks=[task]).kickoff().raw)

The agent writes the request and picks how many rows. You decide what one list may cost.

ToolThe model givesWhat happens
specstream_find_listrequest, max_resultsReads the request, prices it, starts it only within max_price_usd, waits up to seven minutes and returns every row. You pay only for rows that match.
specstream_get_list_resultslist_idEvery row found so far, for a list that was still running. Free.

When the price is above your maximum, nothing runs: the tool returns the price, so the agent can ask for fewer rows or tell the user. When a request is too vague to price (“clinics”), it returns how SpecStream read it, so the agent can add the place or the conditions.

Use the classes directly when you want different settings per tool:

Python
from crewai_specstream import SpecStreamFindListTool, SpecStreamGetListResultsTool
find = SpecStreamFindListTool(max_price_usd=20, wait_seconds=600)
results = SpecStreamGetListResultsTool()

What comes back

One flat row per match: the list’s own columns, then where the row was read. This is a real row from the request above.

JSON
{
"list_id": "fnd_gk5y3d0r97bvyz0t08zzh75kr6",
"position": 26,
"Organization": "RapidCanvas Inc",
"Published address": "801 BARTON SPRINGS RD, AUSTIN, TEXAS, 78704",
"Source industry classification": "Other Technology",
"Amount sold (USD)": "28999988",
"Funding filing date": "2026-10-14",
"source_url": "https://www.sec.gov/Archives/edgar/data/2052031/000205203126000006/primary_doc.xml",
"checked_at": "2026-10-14T19:56:02.016693Z",
"list_link": "https://specstream.ai/workspace/collections/fnd_gk5y3d0r97bvyz0t08zzh75kr6"
}

Columns follow the request. Ask for breweries with their website and the rows carry the brewery and its website instead. A fact a source doesn’t publish comes back empty, never guessed. The list_link opens the same list in your workspace, where you can map it, export it or keep it current.

In a crew project

Python
from crewai import Agent
from crewai.project import CrewBase, agent
from crewai_specstream import specstream_tools
@CrewBase
class LeadCrew:
@agent
def researcher(self) -> Agent:
return Agent(config=self.agents_config["researcher"], tools=specstream_tools(max_price_usd=5))

The key is stored as a secret, so it stays out of logs and dumps. The tools run their requests in a worker thread for async crews, and CrewAI’s tool cache keeps a list only once it has finished, so a list that was still running is read again instead of replayed.

Money, plainly

  • max_price_usd lives on the tool, in your code. The model never sees it and can’t change it.
  • You pay for rows that match. Candidates SpecStream couldn’t confirm are left out and aren’t billed.
  • A retried step asking for the same list within the hour gets the list it already started, not a second bill.
  • Rates and plans: pricing. How charges work: billing.

Every tool over MCP

The package covers finding a list and reading it. For everything else (schedules, exports, sharing, enrichment of your own list), give the agent the whole MCP server:

Python
import os
from crewai import Agent
from crewai.mcp import MCPServerHTTP
agent = Agent(
role="Researcher",
goal="Find, keep and share lists",
backstory="You work from lists with a source on every row.",
mcps=[MCPServerHTTP(
url="https://api.specstream.ai/mcp",
headers={"Authorization": f"Bearer {os.environ['SPECSTREAM_API_KEY']}"},
)],
)

Source and issues: GitHub (opens in a new tab). Package: PyPI (opens in a new tab).