---
seo_title: Lookup (phase 2) : API de recherche web pour agents | EnConvert
meta_desc: Bêta privée, phase 2 : lancez une recherche web, actualités, scholar ou maps et auto-percevez au besoin les meilleurs résultats. Un endpoint neutre.
keywords: api de recherche web pour llm, alternative à une api serp, api google search pour agents ia, api de recherche avec rendu de page, alternative à serper, alternative à firecrawl search, api de recherche web pour rag, api de recherche d'actualités en json
---

# API de recherche web pour agents LLM

<div class="alert alert-warning">
<strong>Bêta privée.</strong> Lookup est appelable dès aujourd'hui avec votre clé d'API habituelle, sur tous les forfaits y compris Founding, et décompte votre quota mensuel d'ops comme n'importe quel autre appel. Ce n'est ni annoncé ni disponible de façon générale : les formes de requête et de réponse peuvent changer sans préavis, et il n'y a aucun engagement de stabilité ni de support, donc ne construisez rien de critique dessus pour l'instant. La feuille de route est sur <a href="/fr/docs/coming-soon">Bientôt disponible</a>, et chaque publication est annoncée dans <a href="/fr/changelog">le changelog</a>.
</div>

`POST /v2/lookup` est une API de recherche web conçue pour les agents LLM :
elle exécute une recherche dans l'une des six catégories et renvoie une liste
de résultats plate, neutre vis-à-vis du fournisseur. Définissez `perceive_top`
et elle effectue en plus le rendu des N premières URL de résultat dans un vrai
navigateur, de sorte qu'un agent obtient la page de résultats du moteur de
recherche (SERP) *et* le contenu de la page derrière chaque résultat en un seul
aller-retour. En tant qu'alternative à une API SERP, elle condense la pile
habituelle (interroger une API de recherche, analyser ses résultats, puis
déployer un scraper en éventail) en un seul appel. Ce sera la réponse
d'EnConvert à `/search` de Firecrawl.

Serper est le fournisseur de recherche qui l'alimente. La requête et la réponse
parlent un vocabulaire de recherche neutre (`category`, `country`, `locale`,
`time_filter`), de sorte qu'un futur changement de fournisseur ne modifie pas le
contrat sur lequel vous codez.

Voici le plus petit appel utile. Envoyez une requête, récupérez les meilleurs
résultats web :

```bash
curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "headless chrome pdf rendering"
  }'
```

La réponse est une liste de résultats plate plus la provenance pour la corrélation
côté support :

```json
{
    "lookup_id": 81423,
    "query": "headless chrome pdf rendering",
    "category": "web",
    "total": 10,
    "results": [
        {
            "title": "Generate PDFs with headless Chrome",
            "url": "https://example.com/guide/chrome-pdf",
            "snippet": "Render a page and print it to PDF...",
            "position": 1
        },
        {
            "title": "Print to PDF with the Chrome DevTools Protocol",
            "url": "https://example.dev/cdp/print-to-pdf",
            "snippet": "Page.printToPDF returns base64 PDF data...",
            "position": 2
        }
    ],
    "perceive_top": 0,
    "perceive_operation_ids": [],
    "credits": 1,
    "cost_cents": 0.06,
    "warnings": []
}
```

---

## Points de terminaison

| Méthode | Chemin | Objectif |
|--------|------|---------|
| `POST` | `/v2/lookup` | Exécuter une recherche et, en option, auto-percevoir les N premières URL de résultat. |

`/v2/lookup` est un point de terminaison à appel unique, sans chemin distinct de
statut ou de récupération. Lorsque vous auto-percevez, chaque page rendue devient
une opération [perceive](/fr/docs/endpoints/perceive.md) de premier ordre dotée de son propre
`operation_id`, que vous pouvez récupérer ultérieurement via
`GET /v2/perceive/{operation_id}`.

**Content-Type :** `application/json`.

---

## Authentification

Authentifiez-vous avec une clé privée dans l'en-tête `X-API-Key` pour les appels
serveur à serveur. C'est la voie qu'utilisent les exemples ci-dessous.

```http
X-API-Key: sk_your_private_key
```

Les clés publiques avec un jeton bearer JWT fonctionnent aussi, selon le même flux
que tout autre point de terminaison : générez un jeton avec votre clé `pk_`, puis envoyez-le
sous la forme `Authorization: Bearer <token>`. Le flux complet, y compris le
verrouillage de domaine et le rafraîchissement du jeton, figure dans
[le guide d'authentification](/fr/docs/authentication.md).

Chaque clé API porte une liste blanche de points de terminaison autorisés. Si `/v2/lookup`
n'est pas sur la liste de la clé, la requête est rejetée avec `403`.

---

## Comment fonctionne lookup

Une requête exécute une recherche fournisseur puis, seulement si vous le demandez,
effectue le rendu des meilleurs résultats.

1. **Contrôle de quota.** Avant toute facturation, le gestionnaire vérifie le
   quota mensuel unifié d'ops de votre plan. Un plan désactivé ou un quota
   épuisé est rejeté avec `402`, de sorte que rien n'est facturé en cas de rejet.
2. **Recherche.** La requête part vers le fournisseur de recherche (Serper)
   sur le point de terminaison correspondant à votre `category`. Récence, pays,
   locale, localisation, taille de page et autocorrection sont mappés sur les
   paramètres du fournisseur.
3. **Normalisation.** Chaque résultat du fournisseur est aplati en un
   `LookupResult` neutre qui porte `title`, `url`, `snippet` et `position`. Les
   extras propres à la catégorie atterrissent dans `extra`, de sorte que le
   contrat n'ajoute jamais une colonne par particularité de fournisseur.
4. **Facturation et audit.** La recherche a réussi, donc une op est
   facturée et une ligne d'audit `ch_lookup_queries` est écrite. L'identifiant de la
   ligne revient sous `lookup_id` pour la corrélation côté support.
5. **Auto-perception (optionnelle).** Si `perceive_top > 0`, les N premières URL de
   résultat sont rendues une par une via le singleton headless Chrome partagé, le
   même pipeline que [le point de terminaison perceive](/fr/docs/endpoints/perceive.md). Chaque rendu est une
   opération `/v2/perceive` complète : sa propre op facturée sur le quota partagé, sa propre ligne
   d'opération, son propre `operation_id`. Par défaut, l'auto-perception ne
   demande que du Markdown, sans capture d'écran, PDF ni extraction LLM ;
   envoyez un objet `enrich` pour élargir les sorties, les exécuter en
   parallèle, ou ajouter une extraction par schéma et une réponse synthétisée.

L'auto-perception est au mieux. Une URL isolée qui échoue, ou le quota d'ops qui
s'épuise en cours de route, se dégrade en avertissement et renvoie tout de même les
résultats de recherche. La SERP est ici le produit principal, donc un problème de
perception ne fait jamais couler l'appel entier.

---

## Paramètres de requête

### Requête et catégorie

| Paramètre | Type | Valeur par défaut | Description |
|-----------|------|---------|-------------|
| `query` | `string` | aucune | La requête de recherche. 1–512 caractères, dépouillée des espaces de début et de fin. Une requête vide après ce nettoyage est rejetée avec `422`. Obligatoire. |
| `category` | `string` | `"web"` | L'une de `web`, `news`, `images`, `scholar`, `patents`, `maps`. |

### Ciblage et récence

| Paramètre | Type | Valeur par défaut | Description |
|-----------|------|---------|-------------|
| `country` | `string` | `null` | Code pays Google `gl`, par ex. `us`, `in`. 8 caractères maximum. |
| `locale` | `string` | `null` | Langue d'interface Google `hl`, par ex. `en`. 16 caractères maximum. |
| `time_filter` | `string` | `null` | Restreindre aux résultats de la période passée : `hour`, `day`, `week`, `month`, ou `year`. |
| `location` | `string` | `null` | Chaîne de localisation en texte libre, par ex. `"Austin, Texas"`. 128 caractères maximum. |
| `autocorrect` | `boolean` | `true` | Indique si le fournisseur peut autocorriger l'orthographe de la requête. |

### Pagination

| Paramètre | Type | Valeur par défaut | Description |
|-----------|------|---------|-------------|
| `num_results` | `integer` | `10` | Résultats par page. 1–100. |
| `page` | `integer` | `1` | Numéro de page. 1–10. |

### Auto-perception

| Paramètre | Type | Valeur par défaut | Description |
|-----------|------|---------|-------------|
| `perceive_top` | `integer` | `0` | Auto-percevoir les N premières URL de résultat qui ont un lien navigable. 0–10. Chacune est un rendu navigateur complet qui facture une op sur votre quota mensuel, ce qui explique le plafond de 10. Pour des ensembles plus grands, prenez les champs `url` et appelez [le point de terminaison de perception par lot](/fr/docs/endpoints/perceive.md#batch-perception). `0` désactive l'auto-perception. |

### Enrichissement (`enrich`)

Un objet `enrich` optionnel ajuste la manière dont les N premiers résultats
(`perceive_top`) sont lus, et peut synthétiser une réponse unique et fondée à
travers eux. Lorsque `enrich` est omis, `perceive_top` conserve son
comportement par défaut (Markdown uniquement, un résultat à la fois).

| Paramètre | Type | Valeur par défaut | Description |
|-----------|------|---------|-------------|
| `enrich.outputs` | `string[]` | `["markdown"]` | Les sorties perceive à produire par résultat enrichi, par exemple `markdown`, `html_cleaned`, `links`, `screenshot`, `structured`. Voir [les sorties de perceive](/fr/docs/endpoints/perceive.md#outputs). |
| `enrich.concurrency` | `integer` | `3` | Combien d'URL de résultat enrichir en parallèle. 1–5. Les rendus Markdown/HTML se parallélisent ; les rendus capture d'écran/PDF se sérialisent sur le navigateur partagé. |
| `enrich.schema` | `object` | `null` | Exécute une extraction structurée pilotée par schéma sur chaque résultat enrichi. Les données extraites apparaissent sous le `perceive.structured` de chaque résultat. Objet JSON-Schema ou une correspondance plate `{field: description}`. |
| `enrich.synthesize_answer` | `boolean` | `false` | Synthétise une réponse citée et fondée à la requête à travers les résultats enrichis, renvoyée sous `answer` (avec `answer_sources`). Utilise le contenu de page perçu lorsqu'il est disponible, sinon les extraits de résultat. |
| `enrich.answer_prompt` | `string` | `null` | Une question à laquelle répondre au lieu de la requête brute. Utilisée uniquement lorsque `synthesize_answer` vaut `true`. 1,000 caractères max. |

`enrich.schema` et `enrich.synthesize_answer` utilisent le palier d'extraction
LLM. Si cette étape d'extraction ne peut pas s'exécuter, ils se dégradent en
avertissement et le reste de la réponse n'est pas affecté.

```json
{
  "query": "best open-source vector databases",
  "perceive_top": 3,
  "enrich": {
    "outputs": ["markdown"],
    "concurrency": 3,
    "synthesize_answer": true
  }
}
```

---

## Catégories

Chaque catégorie atteint un point de terminaison fournisseur différent et fait apparaître une
forme de résultat légèrement différente. Les champs universels (`title`, `url`,
`snippet`, `position`) sont toujours typés ; les champs propres à la catégorie
atterrissent dans `extra`.

| `category` | Ce qu'elle recherche | Champs notables renseignés |
|------------|------------------|--------------------------|
| `web` | Résultats web généraux | `title`, `url`, `snippet`, `date`, `position` |
| `news` | Articles d'actualité | ajoute `source`, `image_url` |
| `images` | Résultats d'images | `image_url`, `thumbnail_url`, `source` (souvent pas de `snippet`) |
| `scholar` | Résultats académiques | même forme que `web` ; nombre de citations dans `extra` |
| `patents` | Résultats de brevets | même forme que `web` ; champs de brevet dans `extra` |
| `maps` | Lieux locaux | `url` est le site web du lieu ; `snippet` porte l'adresse ; note, coordonnées dans `extra` |

À signaler : pour `images` et `maps`, `url` peut être `null` pour un résultat donné
lorsque le fournisseur ne renvoie aucun lien navigable. L'auto-perception ignore
tout résultat dont l'`url` est `null`, de sorte qu'un `perceive_top` de 5 sur une
SERP comptant deux résultats sans URL perçoit au plus trois pages.

---

## Réponse

Le point de terminaison omet les champs `null`, de sorte qu'un résultat `web` minimal ne porte
que les champs réellement renseignés.

| Champ | Type | Description |
|-------|------|-------------|
| `lookup_id` | `integer` | L'identifiant de la ligne d'audit `ch_lookup_queries`. Citez-le au support. `null` si l'écriture d'audit a échoué, sachant que les résultats restent valides. |
| `query` | `string` | La requête (nettoyée) que vous avez envoyée. |
| `category` | `string` | La catégorie recherchée. |
| `country` | `string` | Écho du `country` que vous avez envoyé, le cas échéant. |
| `locale` | `string` | Écho du `locale` que vous avez envoyé, le cas échéant. |
| `time_filter` | `string` | Écho du `time_filter` que vous avez envoyé, le cas échéant. |
| `total` | `integer` | Nombre de résultats renvoyés. |
| `results` | `LookupResult[]` | La liste de résultats. Voir ci-dessous. |
| `perceive_top` | `integer` | Combien de résultats ont *réellement* été perçus : au plus la valeur que vous avez demandée, et moins si le quota d'ops s'est épuisé ou si des URL ont échoué. |
| `perceive_operation_ids` | `string[]` | Les identifiants d'opération `per_...` des résultats perçus, dans l'ordre. |
| `answer_box` | `object` | La boîte de réponse du fournisseur, lorsqu'elle est présente. |
| `knowledge_graph` | `object` | Le panneau knowledge graph du fournisseur, lorsqu'il est présent. |
| `answer` | `string` | La réponse citée synthétisée à travers les résultats enrichis. Présente uniquement lorsque `enrich.synthesize_answer` vaut `true` et qu'elle a réussi. |
| `answer_sources` | `string[]` | Les URL utilisées comme fondement pour `answer`, dans l'ordre de citation. |
| `credits` | `integer` | Crédits fournisseur consommés par cette requête. |
| `cost_cents` | `number` | Coût monétaire de la recherche en cents. Un forfait de `0.06` par requête aujourd'hui. |
| `warnings` | `string[]` | Notes non fatales : un résultat sans URL ignoré, un échec d'auto-perception, le quota d'ops épuisé en cours de boucle. |

### LookupResult

| Champ | Type | Description |
|-------|------|-------------|
| `title` | `string` | Titre du résultat. |
| `url` | `string` | Lien canonique de la page, celui que vous percevriez. `null` pour les résultats sans URL navigable. |
| `snippet` | `string` | Extrait du résultat. Pour `maps`, il porte l'adresse. |
| `position` | `integer` | La position du résultat sur la SERP. |
| `source` | `string` | Source/éditeur, pour `news` et `images`. |
| `date` | `string` | Date de publication, lorsque le fournisseur en signale une. |
| `image_url` | `string` | URL de l'image, pour `images` et `news`. |
| `thumbnail_url` | `string` | URL de la miniature, pour `images`. |
| `extra` | `object` | Champs propres à la catégorie absents de l'ensemble neutre : notes, coordonnées, nombre de citations, et ainsi de suite. |
| `perceive` | `PerceiveResponse` | Le résultat de perception complet en ligne pour cette URL, présent uniquement pour les N premiers lorsque `perceive_top > 0` et que le rendu a réussi. Même forme d'objet que [le point de terminaison perceive](/fr/docs/endpoints/perceive.md#response). |

---

## Lire les résultats d'auto-perception

Lorsque vous envoyez `perceive_top`, parcourez les résultats et vérifiez la présence
du champ `perceive`. Il n'est présent que sur les résultats qui ont été perçus, et
seulement lorsque leur rendu a réussi. Le Markdown de chacun se trouve derrière une
URL de téléchargement pré-signée (un lien signé à courte durée de vie vers le
stockage objet) sous `perceive.outputs.markdown.url`, comme pour un appel perceive
direct.

```json
{
    "lookup_id": 81910,
    "query": "react server components data fetching",
    "category": "web",
    "total": 10,
    "results": [
        {
            "title": "Data fetching with RSC",
            "url": "https://example.com/rsc/data",
            "snippet": "Fetch on the server, stream to the client...",
            "position": 1,
            "perceive": {
                "operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
                "status": "completed",
                "url": "https://example.com/rsc/data",
                "outputs": {
                    "markdown": {
                        "url": "https://spaces.example.com/...signed...",
                        "size_bytes": 7421,
                        "content_type": "text/markdown; charset=utf-8",
                        "expires_in": 900
                    }
                },
                "cost_cents": 0.0,
                "duration_ms": 5840
            }
        }
    ],
    "perceive_top": 1,
    "perceive_operation_ids": [
        "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7"
    ],
    "credits": 1,
    "cost_cents": 0.06,
    "warnings": []
}
```

Ces URL signées expirent au bout de 15 minutes. Pour télécharger une page perçue
plus tard, récupérez à nouveau son opération avec `GET /v2/perceive/{operation_id}`
en utilisant l'identifiant tiré de `perceive_operation_ids`. Cela re-signe les URL
et n'effectue pas de nouveau rendu, donc cela ne coûte aucune op. Consultez
[la section de récupération de perceive](/fr/docs/endpoints/perceive.md#retrieve-an-operation)
pour les détails.

---

## Exemples de code

### curl : recherche web

```bash
curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "open source vector database",
    "num_results": 20
  }'
```

### curl : actualités récentes, localisées

```bash
curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "rbi monetary policy",
    "category": "news",
    "country": "in",
    "locale": "en",
    "time_filter": "week"
  }'
```

### curl : recherche plus auto-perception des 3 premiers

```bash
curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "langchain retrieval augmented generation",
    "perceive_top": 3
  }'
```

### Python

```python
import requests

response = requests.post(
    "https://api.enconvert.com/v2/lookup",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "query": "langchain retrieval augmented generation",
        "perceive_top": 3,
    },
)
response.raise_for_status()
data = response.json()

# Pull the Markdown of every result that was perceived
for result in data["results"]:
    perceived = result.get("perceive")
    if not perceived:
        continue
    markdown_url = perceived["outputs"]["markdown"]["url"]
    page_text = requests.get(markdown_url).text
    print(result["url"], len(page_text), "chars")

for note in data["warnings"]:
    print("warning:", note)
```

### Node.js

```javascript
const res = await fetch("https://api.enconvert.com/v2/lookup", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        query: "langchain retrieval augmented generation",
        perceive_top: 3
    })
});

const data = await res.json();

// Pull the Markdown of every result that was perceived
for (const result of data.results) {
    if (!result.perceive) continue;
    const markdownUrl = result.perceive.outputs.markdown.url;
    const pageText = await fetch(markdownUrl).then(r => r.text());
    console.log(result.url, pageText.length, "chars");
}

for (const note of data.warnings) {
    console.log("warning:", note);
}
```

Si vous appelez EnConvert depuis Claude, Cursor, ou un autre client Model Context
Protocol (MCP), la capacité de recherche y sera également exposée comme un outil.
Voir [la page du serveur MCP](/fr/mcp.md).

---

## Réponses d'erreur

Le gestionnaire ne renvoie jamais le texte brut du fournisseur au client. Les
détails du fournisseur et de la SSRF restent dans les journaux du serveur, et le
client reçoit un message propre et générique.

| Statut | Condition |
|--------|-----------|
| `401 Unauthorized` | Clé API / jeton JWT manquant ou invalide. |
| `402 Payment Required` | Lookup ne figure pas dans votre plan actuel, ou votre quota mensuel d'ops est épuisé. |
| `403 Forbidden` | `/v2/lookup` ne figure pas dans les points de terminaison autorisés de la clé API. |
| `422 Unprocessable Entity` | La validation de la requête a échoué : `query` vide/trop longue, `category` ou `time_filter` inconnu, `num_results` ou `page` hors plage, `perceive_top` supérieur à 10. |
| `502 Bad Gateway` | Le fournisseur de recherche a renvoyé une réponse d'erreur ou une faute de transport non réessayable (`SearchUpstreamError`). Réessayer peut aider. |
| `503 Service Unavailable` | Le fournisseur de recherche est mal configuré côté serveur (une clé manquante de notre côté, `SearchConfigError`), ou il est temporairement indisponible : le disjoncteur est ouvert, ou le fournisseur nous a limités en débit (`SearchUnavailableError`). Réessayez plus tard. |
| `500 Internal Server Error` | Un échec inattendu. Le message est générique ; citez l'heure de l'appel au support. |

Une auto-perception qui échoue ne lève jamais d'erreur de son propre fait. Elle
atterrit dans `warnings` et l'appel répond tout de même `200`. La référence complète
des codes de statut figure dans [le guide des codes d'erreur](/fr/docs/reference/errors.md).

---

## Limites

| Limite | Valeur |
|-------|-------|
| Longueur de `query` | 1–512 caractères (nettoyée) |
| Longueur de `country` | 8 caractères |
| Longueur de `locale` | 16 caractères |
| Longueur de `location` | 128 caractères |
| `num_results` | 1–100 |
| `page` | 1–10 |
| `perceive_top` | 0–10 |
| Ops par appel | 1 pour la requête, plus 1 par résultat auto-perçu |
| Sorties d'auto-perception | Markdown par défaut ; élargies avec `enrich.outputs` |
| Concurrence d'auto-perception | Séquentielle par défaut ; 1–5 avec `enrich.concurrency` |
| Coût par recherche | 0.06 cent forfaitaire |
| Expiration de l'URL signée d'une page perçue | 15 minutes |

---

## Questions fréquentes

### Comment lancer une recherche web et récupérer le contenu de page en un seul appel API REST ?

Envoyez `POST /v2/lookup` avec une `query` et définissez `perceive_top` (0–10). Les URL des N premiers résultats sont rendues dans un vrai navigateur, et chaque résultat perçu porte un objet `perceive` en ligne dont le Markdown se trouve derrière une URL pré-signée à `perceive.outputs.markdown.url`.

### `/v2/lookup` est-elle une alternative à une API SERP sans dépendance à un fournisseur ?

Oui. Serper est le fournisseur de recherche qui l'alimente, mais la requête et la réponse parlent un vocabulaire de recherche neutre (`category`, `country`, `locale`, `time_filter`), de sorte qu'un futur changement de fournisseur ne modifie pas le contrat sur lequel vous codez.

### Quelles catégories de recherche l'API lookup prend-elle en charge ?

Six : `web` (par défaut), `news`, `images`, `scholar`, `patents` et `maps`. Les champs universels (`title`, `url`, `snippet`, `position`) sont toujours typés, et les extras propres à la catégorie atterrissent dans `extra`.

### Pourquoi lookup a-t-il perçu moins de pages que ma valeur `perceive_top` ?

L'auto-perception ignore les résultats dont `url` est `null`, s'arrête si le quota mensuel d'ops s'épuise en cours de boucle, et dégrade un rendu échoué en simple avertissement. Le `perceive_top` de la réponse indique combien de pages ont réellement été perçues, et `warnings` explique les écarts.
