Skip to the page

Mistral API

Give a Mistral model web search as a function tool. When the model asks, your code calls the SpecStream Search API and returns the answer, the page it was read from and the ranked results. It works on Chat Completions, where Mistral's own web_search tool does not.

SpecStream for Mistral APIAgent frameworks & builders

What you need

  • A Mistral API key, and Python with the Mistral SDK and httpx (pip install mistralai httpx).
  • A SpecStream API key. New accounts start with $10 of credit, no card. Create an account.
  1. 1. Create a SpecStream API key

    In SpecStream, open Agents & API, choose API keys, then Create API key. Give it execute permission and a spending limit above zero, so it can search. Set it as SPECSTREAM_API_KEY, and your Mistral key as MISTRAL_API_KEY, on the machine that runs your code.

  2. 2. Declare the search function

    Mistral takes your functions in tools, each with a name, a description and a JSON schema for its parameters. Declare one that takes the question. Add each block of this guide to one file, mistral_search.py, in order:

    mistral_search.py
    tools = [
    {
    "type": "function",
    "function": {
    "name": "web_search",
    "description": "Search the live web. Use it for news, prices, people in roles and any fact that may have changed since training.",
    "parameters": {
    "type": "object",
    "properties": {
    "q": {"type": "string", "description": "The question, in everyday words"}
    },
    "required": ["q"],
    },
    },
    }
    ]
  3. 3. Call the Search API

    The function calls GET /v1/web-search with the question in q. num sets how many results come back. Return JSON text, which Mistral takes as the tool's content:

    mistral_search.py
    import json
    import os
    import httpx
    specstream = httpx.Client(
    base_url="https://api.specstream.ai",
    headers={"Authorization": f"Bearer {os.environ['SPECSTREAM_API_KEY']}"},
    timeout=60,
    )
    def web_search(q: str) -> str:
    r = specstream.get("/v1/web-search", params={"q": q, "num": 5})
    if r.status_code != 200:
    return json.dumps({"error": r.status_code, "message": r.text[:500]})
    data = r.json()["data"]
    answer = data.get("answer") or {}
    return json.dumps({
    "answer": answer.get("value"),
    "answer_page": answer.get("page"),
    "sources": [
    {
    "title": s.get("title"),
    "url": s["url"],
    "snippet": s.get("snippet"),
    "published": s.get("published"),
    }
    for s in data["sources"]
    ],
    })
    functions = {"web_search": web_search}
  4. 4. Answer each tool call

    Mistral does not run your function: the model returns tool_calls, your code runs each one and sends the result back as a tool message with the call's tool_call_id. The model can follow one call with another, so loop until it answers:

    mistral_search.py
    from mistralai.client import Mistral
    client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
    model = "mistral-large-latest"
    messages = [
    {"role": "system", "content": "Use web_search for any fact that may have changed since your training. Cite the URLs it returns."},
    {"role": "user", "content": "What did the European Central Bank decide at its last meeting?"},
    ]
    response = client.chat.complete(model=model, messages=messages, tools=tools)
    messages.append(response.choices[0].message)
    while response.choices[0].message.tool_calls:
    for tool_call in response.choices[0].message.tool_calls:
    args = json.loads(tool_call.function.arguments)
    messages.append({
    "role": "tool",
    "name": tool_call.function.name,
    "content": functions[tool_call.function.name](**args),
    "tool_call_id": tool_call.id,
    })
    response = client.chat.complete(model=model, messages=messages, tools=tools)
    messages.append(response.choices[0].message)
    print(response.choices[0].message.content)
  5. 5. Run it

    Run python mistral_search.py. The answer names the pages it came from. Print the content of each tool message to see every result the model read.

Mistral's own web_search and web_search_premium tools run only in the Conversations API and the Agents API, not in Chat Completions, and Mistral's pricing page says tool APIs are priced per call without showing the amount. A SpecStream function differs in three ways:

  • It runs on any endpoint that takes function tools: Chat Completions, Conversations and Agents, and on every Mistral model that calls functions.
  • Your code holds the results. Each call returns the answer, the page it was read from and the ranked results with their URLs and publication dates, so you can log them, show them or drop a site you do not trust.
  • The price is on this page: $5 per 1,000 searches of up to 10 results, from your balance. The Search API is the most accurate web search API on SimpleQA Verified and WideSearch (scores and method).

In the Agents and Conversations API

An agent takes the same tools list. When the last output is a function.call, run it and append a FunctionResultEntry to the conversation:

Read pages too

Add a second function that reads the pages a search found, through the Extract API: POST /v1/extract with up to 20 URLs returns each page's main text, at $0.80 per 1,000 pages. A page that gives no text is not charged.

Keep queries in the EU

A SpecStream key can send its searches only from SpecStream's server in Madrid, and can be set to zero retention, so no request text or answer is kept after the reply. Set both on the key in your workspace, or with PATCH /v1/account/keys/{id} and {"zero_retention": true, "eu_egress_only": true}.

Cost

Mistral bills the model's tokens. SpecStream bills each search at $5 per 1,000 searches of up to 10 results, and each page read at $0.80 per 1,000 pages, from your balance, with no quote and no approval. A search that returns no results costs nothing. Balance, quotes, and billing has the details.

More

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