> ## Documentation Index
> Fetch the complete documentation index at: https://docs.venice.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Exa Search

> Effectuez des recherches web avec Exa via l'API Venice, avec une non-conservation des données et sans compte Exa distinct, et donnez aux modèles Venice la recherche Exa comme outil.

[Exa](https://exa.ai) est un moteur de recherche web conçu pour les agents IA. Le [point de terminaison de recherche web](https://docs.venice.ai/fr/api-reference/endpoint/augment/search.md) de Venice peut exécuter vos requêtes sur Exa : définissez `search_provider` sur `"exa"` et Venice effectue la recherche avec Exa en non-conservation des données (Zero Data Retention). Vous vous authentifiez avec votre clé API Venice, Venice facture la recherche et vous n'avez pas besoin de compte Exa.

Les résultats ont le même format quel que soit le fournisseur : Exa s'intègre donc à tout ce que vous avez construit sur la recherche Brave par défaut de Venice, et changer de fournisseur ne demande de modifier qu'un seul champ. Ce guide explique comment appeler le point de terminaison, donner à un modèle Venice la recherche Exa comme outil, ancrer vous-même un prompt dans des résultats Exa, et quand appeler plutôt l'API d'Exa.

## Prérequis

* Une [clé API Venice](https://docs.venice.ai/fr/guides/getting-started/generating-api-key.md)
* Python 3.10 ou version ultérieure, ou Node.js 22 ou version ultérieure
* Pour l'exemple d'agent, un modèle Venice compatible avec l'[appel de fonctions](https://docs.venice.ai/fr/guides/features/function-calling.md). Les exemples utilisent `z-ai-glm-5-3-flash`.

## Configuration

Installez le SDK OpenAI, ainsi que `requests` pour Python. Node.js intègre `fetch` nativement :

```bash Python theme={"system"}
pip install openai requests
```

```bash Node.js theme={"system"}
npm install openai
```

Ajoutez votre clé API Venice à l'environnement :

```bash theme={"system"}
export VENICE_API_KEY=your-venice-api-key
```

## Rechercher avec Exa

Envoyez une requête à `/augment/search` avec `search_provider` défini sur `"exa"` :

```python Python theme={"system"}
import os

import requests

response = requests.post(
    "https://api.venice.ai/api/v1/augment/search",
    headers={"Authorization": f"Bearer {os.environ['VENICE_API_KEY']}"},
    json={
        "query": "open-source alternatives to vector databases",
        "limit": 5,
        "search_provider": "exa",
    },
    timeout=60,
)
response.raise_for_status()

for result in response.json()["results"]:
    print(result["title"], result["url"])
```

```javascript Node.js theme={"system"}
const response = await fetch("https://api.venice.ai/api/v1/augment/search", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VENICE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    query: "open-source alternatives to vector databases",
    limit: 5,
    search_provider: "exa",
  }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);

for (const result of (await response.json()).results) {
  console.log(result.title, result.url);
}
```

```bash cURL theme={"system"}
curl https://api.venice.ai/api/v1/augment/search \
  -H "Authorization: Bearer $VENICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "open-source alternatives to vector databases",
    "limit": 5,
    "search_provider": "exa"
  }'
```

La réponse contient la requête et une liste de résultats, chacun comportant quatre champs :

```json theme={"system"}
{
  "query": "open-source alternatives to vector databases",
  "results": [
    {
      "title": "Page title",
      "url": "https://example.com/article",
      "content": "Page text, up to about 10,000 characters",
      "date": "Publication date, or an empty string"
    }
  ]
}
```

`limit` vaut 10 par défaut et accepte des valeurs de 1 à 20, et `query` accepte de 1 à 400 caractères. Les valeurs hors de ces plages renvoient une erreur `400` au lieu d'être ramenées dans la plage. Considérez `date` comme facultatif, car de nombreuses pages n'en publient pas.

## Donner la recherche Exa à un modèle Venice

Pour laisser un modèle décider quand et quoi rechercher, décrivez une fonction `web_search` dans le tableau `tools` d'une requête de chat, et exécutez vous-même la recherche chaque fois que le modèle l'appelle. Ce programme encapsule la requête ci-dessus dans une fonction de recherche, renvoie les résultats au modèle sous forme de message `tool` et boucle jusqu'à ce que le modèle réponde en texte :

```python Python theme={"system"}
import json
import os

import requests
from openai import OpenAI

BASE_URL = "https://api.venice.ai/api/v1"
VENICE_API_KEY = os.environ["VENICE_API_KEY"]
MODEL = "z-ai-glm-5-3-flash"

venice = OpenAI(api_key=VENICE_API_KEY, base_url=BASE_URL)

SYSTEM_PROMPT = "Search the web for recent or specific facts. Cite the URL of each source."
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": "Search the web with Exa. Describe the pages to find in natural language.",
            "parameters": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
        },
    }
]


def exa_search(query: str, limit: int = 5) -> list[dict]:
    response = requests.post(
        f"{BASE_URL}/augment/search",
        headers={"Authorization": f"Bearer {VENICE_API_KEY}"},
        json={"query": query, "limit": limit, "search_provider": "exa"},
        timeout=60,
    )
    response.raise_for_status()
    return response.json()["results"]


def run_search(call) -> str:
    try:
        query = json.loads(call.function.arguments)["query"]
        return json.dumps(exa_search(query[:400]))
    except Exception as error:
        return f"Error: {error}"


def ask(question: str, max_rounds: int = 6) -> str:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": question},
    ]
    for _ in range(max_rounds):
        completion = venice.chat.completions.create(
            model=MODEL,
            messages=messages,
            tools=TOOLS,
            extra_body={"venice_parameters": {"include_venice_system_prompt": False}},
        )
        message = completion.choices[0].message
        if not message.tool_calls:
            return message.content
        messages.append(message)
        for call in message.tool_calls:
            messages.append({"role": "tool", "tool_call_id": call.id, "content": run_search(call)})
    raise RuntimeError(f"No answer after {max_rounds} rounds")


print(ask("What changed in the latest stable release of Python?"))
```

```javascript Node.js theme={"system"}
import OpenAI from "openai";

const BASE_URL = "https://api.venice.ai/api/v1";
const MODEL = "z-ai-glm-5-3-flash";

const venice = new OpenAI({ apiKey: process.env.VENICE_API_KEY, baseURL: BASE_URL });

const SYSTEM_PROMPT = "Search the web for recent or specific facts. Cite the URL of each source.";
const tools = [
  {
    type: "function",
    function: {
      name: "web_search",
      description: "Search the web with Exa. Describe the pages to find in natural language.",
      parameters: {
        type: "object",
        properties: { query: { type: "string" } },
        required: ["query"],
      },
    },
  },
];

async function exaSearch(query, limit = 5) {
  const response = await fetch(`${BASE_URL}/augment/search`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.VENICE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, limit, search_provider: "exa" }),
  });
  if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
  return (await response.json()).results;
}

async function runSearch(call) {
  try {
    const { query } = JSON.parse(call.function.arguments);
    return JSON.stringify(await exaSearch(query.slice(0, 400)));
  } catch (error) {
    return `Error: ${error.message}`;
  }
}

async function ask(question, maxRounds = 6) {
  const messages = [
    { role: "system", content: SYSTEM_PROMPT },
    { role: "user", content: question },
  ];
  for (let round = 0; round < maxRounds; round++) {
    const completion = await venice.chat.completions.create({
      model: MODEL,
      messages,
      tools,
      venice_parameters: { include_venice_system_prompt: false },
    });
    const message = completion.choices[0].message;
    if (!message.tool_calls?.length) return message.content;
    messages.push(message);
    for (const call of message.tool_calls) {
      messages.push({ role: "tool", tool_call_id: call.id, content: await runSearch(call) });
    }
  }
  throw new Error(`No answer after ${maxRounds} rounds`);
}

console.log(await ask("What changed in the latest stable release of Python?"));
```

Le modèle ne fait qu'écrire la requête. Votre code décide de tout le reste : le fournisseur, le nombre de résultats et ce qui se passe en cas d'échec d'une recherche. Trois détails méritent d'être conservés lorsque vous adaptez la boucle :

* **Tronquez les requêtes écrites par le modèle.** Le point de terminaison rejette les requêtes de plus de 400 caractères au lieu de les raccourcir, et les modèles en écrivent parfois de longues.
* **Renvoyez les erreurs comme résultats d'outil.** Une recherche échouée devient un message d'outil commençant par `Error:`, ce qui permet au modèle d'essayer une autre requête ou d'expliquer l'échec au lieu de faire planter la boucle.
* **Ajoutez le message de l'assistant avant ses résultats d'outil.** Chaque message `tool` doit suivre le tour de l'assistant qui l'a demandé. Ajouter l'objet message lui-même conserve aussi `reasoning_content`, que renvoient les modèles de raisonnement comme GLM 5.3 Flash, afin que le modèle garde son raisonnement d'un tour à l'autre.

Le champ `content` de chaque résultat contient le texte de la page, jusqu'à environ 10 000 caractères, de sorte que le modèle peut généralement répondre à partir des seuls résultats de recherche. Lorsqu'il a besoin d'une page entière, ajoutez un second outil qui appelle [`/augment/scrape`](https://docs.venice.ai/fr/api-reference/endpoint/augment/scrape), lequel renvoie la page en Markdown.

## Commencer par la recherche

L'appel de fonctions laisse le modèle décider quand et quoi rechercher. Lorsque votre code sait déjà quoi rechercher, vous pouvez vous passer de la boucle d'outils : lancez une seule recherche Exa, numérotez les résultats et demandez à Venice de répondre à partir de ceux-ci. Cela fonctionne avec n'importe quel modèle de texte Venice, et comme vous conservez la liste des sources incluses dans le prompt, chaque citation de la réponse renvoie à une URL.

En reprenant la fonction de recherche, le client et le modèle de l'exemple précédent :

```python Python theme={"system"}
question = "What are the main techniques for reducing LLM inference latency?"

results = exa_search(question, limit=8)
sources = "\n\n".join(
    f"[{i}] {result['title']}\n{result['url']}\n{result['content']}"
    for i, result in enumerate(results, start=1)
)

completion = venice.chat.completions.create(
    model=MODEL,
    messages=[
        {
            "role": "system",
            "content": "Answer only from the numbered sources. Cite each claim by number, like [2].",
        },
        {"role": "user", "content": f"Question: {question}\n\nSources:\n\n{sources}"},
    ],
    extra_body={"venice_parameters": {"include_venice_system_prompt": False}},
)

print(completion.choices[0].message.content)
for i, result in enumerate(results, start=1):
    print(f"[{i}] {result['url']}")
```

```javascript Node.js theme={"system"}
const question = "What are the main techniques for reducing LLM inference latency?";

const results = await exaSearch(question, 8);
const sources = results
  .map((r, i) => `[${i + 1}] ${r.title}\n${r.url}\n${r.content}`)
  .join("\n\n");

const completion = await venice.chat.completions.create({
  model: MODEL,
  messages: [
    {
      role: "system",
      content: "Answer only from the numbered sources. Cite each claim by number, like [2].",
    },
    { role: "user", content: `Question: ${question}\n\nSources:\n\n${sources}` },
  ],
  venice_parameters: { include_venice_system_prompt: false },
});

console.log(completion.choices[0].message.content);
results.forEach((r, i) => console.log(`[${i + 1}] ${r.url}`));
```

[Réponses sourcées avec la recherche web](https://docs.venice.ai/fr/guides/tools/cited-web-answers.md) étend ce modèle avec la sélection des sources, l'extraction des pages et la gestion des pages qui ne se chargent pas. Sa fonction de recherche accepte un argument de fournisseur : passez `"exa"` pour l'exécuter sur Exa.

## Appeler Exa directement

Le point de terminaison de recherche de Venice accepte une requête et un nombre de résultats. L'API d'Exa propose davantage de contrôles : des catégories pour les actualités, les articles de recherche, les entreprises, les personnes et les rapports financiers, des filtres par domaine et par date, des extraits ciblés sur la requête et le texte intégral des pages, ainsi qu'une recherche approfondie en plusieurs étapes. Pour les utiliser, appelez Exa directement avec une clé API obtenue depuis le [tableau de bord Exa](https://dashboard.exa.ai/api-keys). Ces requêtes vont de votre application à Exa, relèvent des conditions d'Exa et non de celles de Venice, et sont facturées sur votre compte Exa. Exa propose la [non-conservation des données](https://exa.ai/docs/admin/security/zero-data-retention) avec ses offres Enterprise.

Les SDK Exa, `exa-py` et `exa-js` 2.24.0 ou version ultérieure, incluent des outils au format Chat Completions qui s'intègrent directement à la boucle ci-dessus. Remplacez la liste d'outils par les outils d'Exa :

```python Python theme={"system"}
from exa_py import Exa

exa = Exa()  # reads EXA_API_KEY
TOOLS = [
    exa.openai.web_search(),
    exa.openai.get_contents(text={"max_characters": 10000}),
]
```

```javascript Node.js theme={"system"}
import Exa from "exa-js";

const exa = new Exa(); // reads EXA_API_KEY
const tools = [
  exa.openai.webSearch(),
  exa.openai.getContents({ text: { maxCharacters: 10000 } }),
];
```

Transmettez ensuite chaque message contenant des appels d'outils au SDK au lieu de votre propre gestionnaire d'outils : `messages += exa.openai.handle_tool_calls(message)` en Python, ou `messages.push(...(await exa.openai.handleToolCalls(message)))` en Node.js. Le SDK exécute chaque appel Exa et renvoie les messages `tool` correspondants, les échecs étant signalés sous forme de résultats `Error:`. Conservez la limite de texte par page, car `exa-js` renvoie des pages entières sans limite. Les arguments nommés tels que `category`, `include_domains` et `start_published_date` sont transmis à la recherche Exa. Consultez le [guide d'appel d'outils](https://exa.ai/docs/integrations/tool-calling/openai) d'Exa pour toutes les options.

## Modèles recommandés

| Cas d'usage | Modèle | Pourquoi |
| - | - | - |
| Agents de recherche | `z-ai-glm-5-3-flash` | Modèle privé rapide et peu coûteux avec appel de fonctions |
| Choix par défaut pour l'utilisation d'outils | `zai-org-glm-5-2` | Le `function_calling_default` actuel de Venice |
| Recherche non censurée | `venice-uncensored-1-2` | Appel de fonctions sans filtrage de contenu |

Les identifiants de modèles évoluent. Déterminez le modèle par défaut actuel pour l'appel de fonctions à l'exécution avec [`GET /models/traits`](https://docs.venice.ai/fr/api-reference/endpoint/models/traits), ou parcourez les [modèles de texte](https://docs.venice.ai/fr/models/text.md) pour en trouver d'autres compatibles avec l'appel de fonctions.

## Confidentialité

Avec `search_provider: "exa"`, votre application communique uniquement avec Venice, et Venice effectue chaque recherche avec Exa en non-conservation des données. Venice ne stocke pas le contenu des prompts ni des réponses, et le [niveau de confidentialité](https://docs.venice.ai/fr/overview/privacy.md) du modèle détermine ce qui se passe au niveau de l'inférence. Les modèles privés s'exécutent sur une infrastructure sans conservation des données.

Un point facile à négliger : le modèle rédige les requêtes de recherche à partir de la conversation, si bien que des détails d'un prompt peuvent se retrouver dans une requête envoyée à Exa. Si les prompts peuvent contenir des informations personnelles ou confidentielles, demandez au modèle dans votre prompt système de ne pas inclure ces détails dans les recherches, ou utilisez le [modèle « rechercher d'abord »](#commencer-par-la-recherche) afin que votre propre code rédige chaque requête.

Si vous [appelez directement l'API Exa](#appeler-exa-directement), ces requêtes sont envoyées à Exa et relèvent alors des conditions d'Exa.

## Coûts

Chaque recherche coûte 0,01 $, soit 10 $ pour 1 000, comme une recherche Brave ou Google. Venice la facture sur votre compte Venice avec les tokens utilisés par vos requêtes de chat : il n'y a donc pas de facture Exa séparée. Les recherches qui échouent avec une erreur ne sont pas facturées, mais une recherche qui ne renvoie aucun résultat l'est. Les résultats de recherche sont renvoyés au modèle en entrée au tour suivant, et comme ils contiennent le texte des pages, une recherche de cinq résultats ajoute souvent 5 000 à 10 000 tokens d'entrée. Quelques habitudes permettent de limiter les deux :

* Réduisez `limit` lorsque quelques sources suffisent. Une recherche coûte le même prix quelle que soit la valeur de `limit`, mais moins de résultats signifie moins de tokens d'entrée au tour suivant.
* Gardez `max_rounds` bas. Chaque tour supplémentaire peut ajouter des recherches en plus des tokens.
* Utilisez le modèle « rechercher d'abord » lorsque votre code sait quoi rechercher. Il exécute exactement une recherche et une requête de chat.

> [!TIP]
>
> Les agents disposant d'un portefeuille crypto peuvent se passer de clé API. `/augment/search` et `/chat/completions` acceptent tous deux l'[authentification par portefeuille x402](https://docs.venice.ai/fr/guides/integrations/x402-venice-api.md), de sorte qu'un agent peut payer les recherches Exa et l'inférence à partir d'un seul solde USDC prépayé.

## Dépannage

> **401 Unauthorized ou identifiants manquants**
>
> Vérifiez que `VENICE_API_KEY` est défini dans le processus qui exécute votre code. Sans elle, Python lève `KeyError: 'VENICE_API_KEY'`, et le SDK OpenAI pour Node.js se rabat sur `OPENAI_API_KEY`. Si celle-ci n'est pas définie non plus, vous obtenez une erreur `Missing credentials`. Si elle est définie, le SDK envoie votre clé OpenAI à Venice, qui la rejette avec une erreur `401`. Une erreur `401` alors que `VENICE_API_KEY` est défini signifie que Venice a rejeté la clé elle-même : vérifiez qu'elle ne contient pas de faute de frappe ou [générez-en une nouvelle](https://docs.venice.ai/fr/guides/getting-started/generating-api-key.md).

> **Erreur 400 du point de terminaison de recherche**
>
> `query` doit contenir de 1 à 400 caractères, `limit` doit être compris entre 1 et 20, et `search_provider` doit valoir `brave`, `google` ou `exa`. Les valeurs hors de ces plages sont rejetées au lieu d'être ramenées dans la plage. Dans la boucle d'outils, une recherche rejetée parvient au modèle sous forme de résultat d'outil `Error:` au lieu de lever une exception.

> **402 Payment Required**
>
> Votre solde Venice ne suffit pas à couvrir la requête. Les requêtes effectuées avec une clé API renvoient `INSUFFICIENT_BALANCE` : rechargez alors votre solde Venice. Les requêtes effectuées avec l'authentification par portefeuille x402 renvoient des instructions de rechargement dans la réponse.

> **Erreur 400 de Venice après un tour d'outils**
>
> Certains modèles, dont les modèles Claude et GPT, renvoient une erreur `400` lorsqu'un appel d'outil n'est pas suivi d'un message `tool` portant son `tool_call_id`. Les modèles GLM acceptent tout de même la conversation, si bien que cette erreur peut n'apparaître qu'au moment où vous changez de modèle. La boucle répond à chaque appel juste après le message de l'assistant qui l'a émis : conservez cette structure si vous ajoutez des outils.

> **Le modèle répond sans effectuer de recherche**
>
> Vérifiez que le modèle prend en charge l'appel de fonctions, et indiquez dans le prompt système quand effectuer une recherche. Les modèles ignorent souvent l'outil lorsqu'ils pensent pouvoir répondre de mémoire, sauf si vous leur demandez de rechercher les faits récents ou précis.

## Ressources

> **[API Web Search](https://docs.venice.ai/fr/api-reference/endpoint/augment/search.md)**
>
> Paramètres, fournisseurs de recherche et schéma de réponse

> **[Réponses sourcées](https://docs.venice.ai/fr/guides/tools/cited-web-answers.md)**
>
> Un pipeline complet de recherche, d'extraction et de citation sur Venice

> **[Appel de fonctions](https://docs.venice.ai/fr/guides/features/function-calling.md)**
>
> Fonctionnement des appels d'outils sur Venice

> **[Documentation Exa](https://exa.ai/docs)**
>
> L'API, les SDK et les options de recherche d'Exa


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.