Use Webz.io Contextual News Search from Python with webzio-news-search. It calls the News Search API over HTTPS and returns typed results: title, URL, publish date, match score, source metadata, and the excerpt that matched the query.
The package has one dependency (httpx). It does not use the MCP server and it does not require LangChain, LlamaIndex, CrewAI, or any other agent framework. Use it in scripts, services, and notebooks, or hand its JSON tool schema to any LLM that supports function calling.
The n8n community node calls this same REST API. Framework wrappers that connect through MCP are documented separately: LangChain, LlamaIndex, AG2, CrewAI, Vercel AI SDK, Groq, and Dify. The n8n page also covers n8n's built-in MCP Client Tool.
Get your token from the Webz.io dashboard.
pip install webzio-news-search
export WEBZ_API_TOKEN="your-webz-api-token"
Package: pypi.org/project/webzio-news-search
Source: github.com/Webhose/webz-news-search (packages/news-search)
from webzio_news_search import WebzNewsSearch
client = WebzNewsSearch() # reads WEBZ_API_TOKEN
# client = WebzNewsSearch(api_token="your-webz-api-token")
response = client.search("recent developments on EU AI regulation", k=10, days=30)
print(response.total_results, "results |", response.credits_used, "credits used")
for result in response:
print(result.score, result.title, result.url)
print(" ", result.text)
Each call is a regular News Search request. The same credits and rate limits apply. See Errors, Rate Limits & Credits.
Coverage is the last 30 days. days=N is shorthand for published_from set to today minus N days (UTC). Pass days or published_from, not both.
Keyword arguments and a filters dict both work. A bare string for a list filter is wrapped in a list, so language="english" becomes ["english"].
client.search(
"trade agreements between USA and Germany",
k=10,
days=7,
language=["english"],
country=["US", "DE"],
)
client.search(
"earnings guidance and analyst reactions",
ticker=["NVDA"],
domain=["yahoo.com", "cnn.com"],
score_gte=5,
k=5,
)
client.search(
"central bank interest rate decision",
filters={
"published_from": "2026-07-01",
"published_to": "2026-07-31",
"sentiment": ["negative"],
"political_bias": ["center"],
},
)
| Argument | Sent as | Description |
|---|---|---|
query | body | Natural-language topic or question. Required. Max 750 characters and 100 words |
k | body | Articles to return. Server default 10 |
score_gte, score_lte | body | Match-score bounds, 0–10. Server default floor is 4; pass score_gte=0 to disable it |
allow_multiple_chunks_per_article | body | Return more than one matching passage from the same article |
days | filters.published_from | Client-side shorthand. Not an API field |
language, country, category, sentiment | filters | Same values as Filters. Country codes are ISO-2 (US, GB) |
published_from, published_to | filters | Date window, YYYY-MM-DD |
domain, exclude_domain | filters | Keep or skip source domains. A domain cannot be in both lists |
topic, person, organization, location, ticker | filters | Entity and topic tags |
political_bias | filters | left, center, or right |
trust_category | filters | trusted_news, fake_news, or satirical_news |
source_type | filters | local_news, newsroom, or gov_news |
domain_rank_gte, domain_rank_lte | filters | Tranco domain rank range (lower means more popular) |
Filter names the package does not know about are forwarded as-is, so a new server-side filter works without a new release.
response.query # echoed query
response.total_results # matches returned, up to k
response.requests_left # remaining credit balance
response.credits_used # credits charged for this call
response.raw # full JSON payload
result = response.results[0]
result.score # match quality, 0–10
result.article # article_id, url, title, published_at, summary, main_image
result.chunk.text # the most relevant excerpt
result.metadata # language, country, category, sentiment, domain, site_type,
# topic, person, organization, location, ticker,
# political_bias, trust_category, source_type, domain_rank
print(response.to_text()) # prompt-friendly text
rows = response.to_dicts() # one flat dict per result
To fetch the full article body, use result.article.article_id as a uuid: query on the News API. See Response format.
from webzio_news_search import WebzNewsSearch
async with WebzNewsSearch() as client:
response = await client.asearch("renewable energy investments", k=5)
One-shot helpers open a client, run the search, and close it:
from webzio_news_search import news_search, anews_search
response = news_search("semiconductor export controls", k=3)
response = await anews_search("semiconductor export controls", k=3)
tool_definition() returns a JSON schema named news_search_by_webz — the same name as the MCP tool, so a prompt written for one works with the other. openai_tool_definition() wraps that schema in the OpenAI chat-completions envelope. run_tool() executes the model's arguments and returns response.to_text().
from openai import OpenAI
from webzio_news_search import WebzNewsSearch
openai_client = OpenAI()
news = WebzNewsSearch()
messages = [{"role": "user", "content": "What happened with Nvidia this week? Cite sources."}]
completion = openai_client.chat.completions.create(
model="gpt-4.1-mini",
messages=messages,
tools=[WebzNewsSearch.openai_tool_definition()],
)
for call in completion.choices[0].message.tool_calls or []:
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": news.run_tool(call.function.arguments),
})
run_tool() accepts a dict or the JSON string the model returns. Inside an event loop, use await news.arun_tool(...).
| Exception | When |
|---|---|
WebzConfigError | Missing token, empty or over-long query, k below 1, score_gte above score_lte, days together with published_from, a domain listed in both domain and exclude_domain |
WebzAPIError | The API returned an HTTP error, or the host could not be reached. status_code and detail are set. The message includes a short hint for 400, 401, 402, 403, 422, 429, and 5xx. A network failure uses status_code 0 |
Failed requests are not charged.
| Name | Default | Purpose |
|---|---|---|
WEBZ_API_TOKEN | required | Webz API token from the dashboard |
WEBZ_NEWS_SEARCH_URL | https://api.webz.io/api/news/context | Endpoint override for testing |
You can also pass api_token= and api_url= to WebzNewsSearch(). timeout= takes seconds or an httpx.Timeout. Pass client= or async_client= to reuse your own httpx clients.
| Approach | Best for |
|---|---|
| webzio-news-search | Python code that should call the News Search API directly, with typed results and no MCP client |
| MCP Server | Cursor, Claude, ChatGPT, and any client that speaks MCP |
| Framework packages | LangChain, LlamaIndex, AG2, CrewAI, the Vercel AI SDK, n8n, Dify |
All of them use the same token and the same search. Pick the client that matches the runtime.