---
seo_title: Lookup (fase 2): API di ricerca web per agenti | EnConvert
meta_desc: Beta privata, fase 2: esegui una ricerca web, news, scholar o maps e applica l'auto-perceive ai primi risultati. Ricerca neutrale in un solo endpoint.
keywords: api di ricerca web per llm, alternativa a serper api, api ricerca google per agenti ai, api di ricerca con contenuto della pagina, alternativa a firecrawl search, api di ricerca web per rag, api ricerca notizie in json, serp api alternativa senza vincolo di provider
---

# API di Ricerca Web per Agenti LLM

<div class="alert alert-warning">
<strong>Beta privata.</strong> Lookup è già chiamabile oggi con la tua normale chiave API, su qualsiasi piano compreso Founding, e scala dalla tua quota mensile di ops come ogni altra chiamata. Non è annunciato né disponibile in generale: le forme di richiesta e risposta possono cambiare senza preavviso e non c'è alcun impegno di stabilità o di supporto, quindi non costruirci ancora sopra nulla di critico. La roadmap è su <a href="/it/docs/coming-soon">In arrivo</a>, e ogni rilascio viene annunciato nel <a href="/it/changelog">changelog</a>.
</div>

`POST /v2/lookup` è un'API di ricerca web pensata per agenti LLM: esegue
una ricerca in una di sei categorie e restituisce un elenco di risultati
piatto e neutrale rispetto al provider. Imposta `perceive_top` e
l'endpoint renderizza anche gli URL dei primi N risultati in un browser
reale, così un agente ottiene la pagina dei risultati del motore di
ricerca (SERP) *e* il contenuto della pagina dietro ogni risultato in
un'unica chiamata. Come alternativa alle SERP API, comprime lo stack
abituale (interrogare un'API di ricerca, analizzarne i risultati, poi
lanciare uno scraper) in un'unica chiamata. Sarà la risposta di EnConvert
a Firecrawl `/search`.

È Serper a fornire il backend di ricerca. La richiesta e la risposta
parlano un vocabolario di ricerca neutrale (`category`, `country`,
`locale`, `time_filter`), così un futuro cambio di provider non modifica
il contratto su cui scrivi il codice.

Ecco la chiamata minima utile. Invia una query e ricevi i migliori
risultati 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 risposta è un elenco di risultati piatto più la provenienza per la
correlazione con il supporto:

```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": []
}
```

---

## Endpoint

| Metodo | Percorso | Scopo |
|--------|------|---------|
| `POST` | `/v2/lookup` | Esegue una ricerca e, opzionalmente, applica l'auto-perceive ai primi N URL dei risultati. |

`/v2/lookup` è un endpoint a chiamata singola: non esiste un percorso
separato di stato o di recupero. Quando esegui l'auto-perceive, ogni
pagina renderizzata diventa un'operazione [perceive](/it/docs/endpoints/perceive.md)
a pieno titolo con un proprio `operation_id`, che puoi recuperare in
seguito tramite `GET /v2/perceive/{operation_id}`.

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

---

## Autenticazione

Autenticati con una chiave privata nell'header `X-API-Key` per le chiamate
server-to-server. Questo è il percorso usato negli esempi seguenti.

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

Funzionano anche le chiavi pubbliche con un token bearer JWT, usando lo
stesso flusso di ogni altro endpoint: genera un token con la tua chiave
`pk_`, poi invialo come `Authorization: Bearer <token>`. Il flusso
completo, incluso il domain locking e il refresh del token, si trova
nella [guida all'autenticazione](/it/docs/authentication.md).

Ogni chiave API porta con sé un'allowlist di endpoint consentiti. Se `/v2/lookup`
non è nella lista della chiave, la richiesta viene rifiutata con `403`.

---

## Come funziona lookup

Una richiesta esegue una ricerca sul provider e poi, solo se lo richiedi,
renderizza i primi risultati.

1. **Controllo quota.** Prima che venga addebitato qualsiasi costo,
   l'handler controlla la quota mensile unificata di ops del tuo piano.
   Se il piano è disabilitato o la quota è esaurita, la richiesta
   viene rifiutata con `402` e non viene addebitato nulla.
2. **Ricerca.** La query viene inviata al provider di ricerca (Serper)
   sull'endpoint corrispondente alla tua `category`. Recency, country,
   locale, location, dimensione della pagina e autocorrect vengono
   mappati sui parametri del provider.
3. **Normalizzazione.** Ogni risultato del provider viene appiattito in
   un `LookupResult` neutrale che porta `title`, `url`, `snippet` e
   `position`. Gli extra specifici della categoria finiscono in `extra`,
   così il contratto non cresce di una colonna per ogni stranezza del
   provider.
4. **Addebito e audit.** La ricerca è riuscita, quindi viene addebitata
   una op e viene scritta una riga di audit
   `ch_lookup_queries`. L'id della riga torna come `lookup_id` per la
   correlazione con il supporto.
5. **Auto-perceive (opzionale).** Se `perceive_top > 0`, gli URL dei
   primi N risultati vengono renderizzati uno alla volta tramite il
   singleton condiviso di Chrome headless, la stessa pipeline
   dell'[endpoint perceive](/it/docs/endpoints/perceive.md). Ogni render è
   un'operazione `/v2/perceive` a pieno titolo: propria op addebitata
   sulla quota condivisa, propria riga di operazione, proprio
   `operation_id`. Per impostazione predefinita l'auto-perceive richiede
   solo Markdown, senza screenshot, PDF o estrazione LLM; invia un
   oggetto `enrich` per ampliare gli output, eseguirli in parallelo,
   oppure aggiungere l'estrazione guidata da schema e una risposta
   sintetizzata.

Auto-perceive è best-effort. Un singolo URL che fallisce, o l'esaurimento
della quota di ops a metà del processo, degrada a un warning e
restituisce comunque i risultati di ricerca. Qui la SERP è il prodotto
primario, quindi un problema di auto-perceive non affonda mai l'intera
chiamata.

---

## Parametri della richiesta

### Query e categoria

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `query` | `string` | nessuno | La query di ricerca. 1–512 caratteri, dopo la rimozione degli spazi bianchi iniziali/finali. Una query vuota dopo il trimming viene rifiutata con `422`. Obbligatorio. |
| `category` | `string` | `"web"` | Una tra `web`, `news`, `images`, `scholar`, `patents`, `maps`. |

### Targeting e recency

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `country` | `string` | `null` | Codice paese Google `gl`, ad es. `us`, `in`. Massimo 8 caratteri. |
| `locale` | `string` | `null` | Lingua dell'interfaccia Google `hl`, ad es. `en`. Massimo 16 caratteri. |
| `time_filter` | `string` | `null` | Limita ai risultati del periodo passato: `hour`, `day`, `week`, `month` o `year`. |
| `location` | `string` | `null` | Stringa di località in testo libero, ad es. `"Austin, Texas"`. Massimo 128 caratteri. |
| `autocorrect` | `boolean` | `true` | Se il provider può correggere automaticamente l'ortografia della query. |

### Paginazione

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `num_results` | `integer` | `10` | Risultati per pagina. 1–100. |
| `page` | `integer` | `1` | Numero di pagina. 1–10. |

### Auto-perceive

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `perceive_top` | `integer` | `0` | Applica l'auto-perceive ai primi N URL dei risultati che hanno un link navigabile. 0–10. Ognuno è un render completo del browser che addebita una op dalla tua quota mensile, ed è per questo che il limite è 10. Per insiemi più grandi, prendi i campi `url` e chiama [l'endpoint perceive batch](/it/docs/endpoints/perceive.md#batch-perception). `0` disabilita l'auto-perceive. |

### Arricchimento (`enrich`)

Un oggetto `enrich` opzionale regola come vengono letti i primi N
risultati (`perceive_top`), e può sintetizzare un'unica risposta fondata
sulle fonti attraverso di essi. Quando `enrich` viene omesso,
`perceive_top` mantiene il suo comportamento predefinito (solo Markdown,
un risultato alla volta).

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `enrich.outputs` | `string[]` | `["markdown"]` | Quali output perceive produrre per ogni risultato arricchito, ad esempio `markdown`, `html_cleaned`, `links`, `screenshot`, `structured`. Vedi [gli output perceive](/it/docs/endpoints/perceive.md#outputs). |
| `enrich.concurrency` | `integer` | `3` | Quanti URL dei risultati arricchire in parallelo. 1–5. I render Markdown/HTML si parallelizzano; i render screenshot/PDF vengono serializzati sul browser condiviso. |
| `enrich.schema` | `object` | `null` | Esegue l'estrazione strutturata guidata da schema su ogni risultato arricchito. I dati estratti compaiono sotto `perceive.structured` di ciascun risultato. Oggetto JSON-Schema o una mappa piatta `{field: description}`. |
| `enrich.synthesize_answer` | `boolean` | `false` | Sintetizza un'unica risposta citata e fondata alla query attraverso i risultati arricchiti, restituita come `answer` (con `answer_sources`). Usa il contenuto della pagina sottoposta a perceive quando disponibile, altrimenti gli snippet dei risultati. |
| `enrich.answer_prompt` | `string` | `null` | Una domanda a cui rispondere invece della query grezza. Usato solo quando `synthesize_answer` è `true`. Massimo 1,000 caratteri. |

`enrich.schema` e `enrich.synthesize_answer` usano il livello di
estrazione LLM. Se quel passaggio di estrazione non può essere eseguito,
degradano a un warning e il resto della risposta non viene toccato.

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

---

## Categorie

Ogni categoria interroga un endpoint diverso del provider e restituisce
una forma di risultato leggermente diversa. I campi universali (`title`,
`url`, `snippet`, `position`) sono sempre tipizzati; i campi specifici
della categoria finiscono in `extra`.

| `category` | Cosa cerca | Campi rilevanti popolati |
|------------|------------------|--------------------------|
| `web` | Risultati web generali | `title`, `url`, `snippet`, `date`, `position` |
| `news` | Articoli di notizie | aggiunge `source`, `image_url` |
| `images` | Risultati immagine | `image_url`, `thumbnail_url`, `source` (spesso senza `snippet`) |
| `scholar` | Risultati accademici | stessa forma di `web`; conteggi delle citazioni in `extra` |
| `patents` | Risultati brevetti | stessa forma di `web`; campi brevetto in `extra` |
| `maps` | Luoghi locali | `url` è il sito web del luogo; `snippet` riporta l'indirizzo; rating, coordinate in `extra` |

Da notare: per `images` e `maps`, `url` può essere `null` per un dato
risultato quando il provider non restituisce un link navigabile.
L'auto-perceive salta qualsiasi risultato il cui `url` è `null`, quindi un
`perceive_top` di 5 su una SERP con due risultati privi di URL applica
l'auto-perceive ad al massimo tre pagine.

---

## Risposta

L'endpoint omette i campi `null`, quindi un risultato `web` minimo porta
solo i campi effettivamente popolati.

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `lookup_id` | `integer` | L'id della riga di audit `ch_lookup_queries`. Riportalo al supporto. `null` se la scrittura dell'audit è fallita, anche se i risultati restano comunque validi. |
| `query` | `string` | La query (dopo il trimming) che hai inviato. |
| `category` | `string` | La categoria cercata. |
| `country` | `string` | Eco del `country` che hai inviato, se presente. |
| `locale` | `string` | Eco del `locale` che hai inviato, se presente. |
| `time_filter` | `string` | Eco del `time_filter` che hai inviato, se presente. |
| `total` | `integer` | Numero di risultati restituiti. |
| `results` | `LookupResult[]` | L'elenco dei risultati. Vedi sotto. |
| `perceive_top` | `integer` | Quanti risultati sono stati *effettivamente* sottoposti a perceive: al massimo il valore richiesto, e meno se la quota di ops è esaurita o gli URL sono falliti. |
| `perceive_operation_ids` | `string[]` | Gli id operazione `per_...` dei risultati sottoposti a perceive, in ordine. |
| `answer_box` | `object` | L'answer box del provider, quando presente. |
| `knowledge_graph` | `object` | Il pannello knowledge graph del provider, quando presente. |
| `answer` | `string` | La risposta citata sintetizzata attraverso i risultati arricchiti. Presente solo quando `enrich.synthesize_answer` è `true` ed è riuscita. |
| `answer_sources` | `string[]` | Gli URL usati come fondamento per `answer`, in ordine di citazione. |
| `credits` | `integer` | Crediti del provider consumati da questa query. |
| `cost_cents` | `number` | Costo monetario della ricerca in centesimi. Oggi un valore fisso di `0.06` per query. |
| `warnings` | `string[]` | Note non fatali: un risultato privo di URL saltato, un fallimento dell'auto-perceive, l'esaurimento della quota di ops a metà ciclo. |

### LookupResult

| Campo | Tipo | Descrizione |
|-------|------|-------------|
| `title` | `string` | Titolo del risultato. |
| `url` | `string` | Link canonico della pagina, ciò a cui applicheresti perceive. `null` per i risultati senza URL navigabile. |
| `snippet` | `string` | Snippet del risultato. Per `maps`, riporta l'indirizzo. |
| `position` | `integer` | La posizione del risultato sulla SERP. |
| `source` | `string` | Fonte/editore, per `news` e `images`. |
| `date` | `string` | Data di pubblicazione, quando il provider la riporta. |
| `image_url` | `string` | URL immagine, per `images` e `news`. |
| `thumbnail_url` | `string` | URL miniatura, per `images`. |
| `extra` | `object` | Campi specifici della categoria non presenti nel set neutrale: rating, coordinate, conteggi delle citazioni, e così via. |
| `perceive` | `PerceiveResponse` | Il risultato perceive inline completo per questo URL, presente solo per i primi N quando `perceive_top > 0` e il render è riuscito. Stessa forma di oggetto dell'[endpoint perceive](/it/docs/endpoints/perceive.md#response). |

---

## Lettura dei risultati auto-perceive

Quando invii `perceive_top`, scorri i risultati e controlla il campo
`perceive`. È presente solo nei risultati sottoposti a perceive, e solo
quando il loro render è riuscito. Il Markdown di ciascuno si trova dietro
un URL di download pre-firmato (un link firmato e di breve durata verso
l'object storage) sotto `perceive.outputs.markdown.url`, come per una
chiamata perceive diretta.

```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": []
}
```

Quegli URL firmati scadono dopo 15 minuti. Per scaricare in seguito una
pagina sottoposta a perceive, recupera di nuovo la sua operazione con
`GET /v2/perceive/{operation_id}` usando l'id da `perceive_operation_ids`.
Questo rifirma gli URL e non ri-renderizza, quindi non costa ops. Per
i dettagli vedi
[la sezione recupero perceive](/it/docs/endpoints/perceive.md#retrieve-an-operation).

---

## Esempi di codice

### curl: ricerca 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: notizie recenti, localizzate

```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: ricerca più auto-perceive dei primi 3

```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);
}
```

Se chiami EnConvert da Claude, Cursor o un altro client Model Context
Protocol (MCP), la capacità di ricerca sarà esposta come tool anche lì.
Vedi [la pagina del server MCP](/it/mcp.md).

---

## Risposte di errore

L'handler non restituisce mai al client il testo grezzo del provider. I
dettagli su provider e SSRF restano nei log del server, e il client
riceve un messaggio pulito e generico.

| Status | Condizione |
|--------|-----------|
| `401 Unauthorized` | Chiave API / token JWT mancante o non valido. |
| `402 Payment Required` | Lookup non è incluso nel tuo piano attuale, oppure la tua quota mensile di ops è esaurita. |
| `403 Forbidden` | `/v2/lookup` non è tra gli endpoint consentiti della chiave API. |
| `422 Unprocessable Entity` | Validazione della richiesta fallita: `query` vuota/troppo lunga, `category` o `time_filter` sconosciuti, `num_results` o `page` fuori intervallo, `perceive_top` superiore a 10. |
| `502 Bad Gateway` | Il provider di ricerca ha restituito una risposta di errore o un errore di trasporto non ritentabile (`SearchUpstreamError`). Riprovare potrebbe aiutare. |
| `503 Service Unavailable` | Il provider di ricerca è configurato in modo errato lato server (una chiave mancante dalla nostra parte, `SearchConfigError`), oppure è temporaneamente non disponibile: il circuit breaker è aperto, o il provider ci ha applicato un rate limit (`SearchUnavailableError`). Riprova più tardi. |
| `500 Internal Server Error` | Un errore imprevisto. Il messaggio è generico; riporta al supporto l'orario della chiamata. |

Un auto-perceive che fallisce non genera mai un proprio errore. Finisce
in `warnings` e la chiamata risponde comunque `200`. Il riferimento
completo dei codici di stato si trova nella
[guida ai codici di errore](/it/docs/reference/errors.md).

---

## Limiti

| Limite | Valore |
|-------|-------|
| Lunghezza `query` | 1–512 caratteri (dopo trimming) |
| Lunghezza `country` | 8 caratteri |
| Lunghezza `locale` | 16 caratteri |
| Lunghezza `location` | 128 caratteri |
| `num_results` | 1–100 |
| `page` | 1–10 |
| `perceive_top` | 0–10 |
| Ops per chiamata | 1 per la query, più 1 per ogni risultato auto-percepito |
| Output auto-perceive | Markdown per impostazione predefinita; ampliabili con `enrich.outputs` |
| Concorrenza auto-perceive | Sequenziale per impostazione predefinita; 1–5 con `enrich.concurrency` |
| Costo per ricerca | 0.06 centesimi fisso |
| Scadenza URL firmato pagina perceive | 15 minuti |

---

## Domande frequenti

### Come eseguo una ricerca web e ottengo il contenuto della pagina in un'unica chiamata REST API?

Invia `POST /v2/lookup` con una `query` e imposta `perceive_top` (0–10). Gli URL dei primi N risultati vengono renderizzati in un browser reale, e ogni risultato sottoposto a perceive porta un oggetto `perceive` inline il cui Markdown si trova dietro un URL pre-firmato in `perceive.outputs.markdown.url`.

### /v2/lookup è un'alternativa alle SERP API che posso adottare senza provider lock-in?

Sì. È Serper a fornire il backend di ricerca, ma la richiesta e la risposta parlano un vocabolario di ricerca neutrale (`category`, `country`, `locale`, `time_filter`), così un futuro cambio di provider non modifica il contratto su cui scrivi il codice.

### Quali categorie di ricerca supporta l'API lookup?

Sei: `web` (predefinita), `news`, `images`, `scholar`, `patents` e `maps`. I campi universali (`title`, `url`, `snippet`, `position`) sono sempre tipizzati, e gli extra specifici della categoria finiscono in `extra`.

### Perché lookup applica il perceive a meno pagine rispetto al mio perceive_top?

L'auto-perceive salta i risultati il cui `url` è `null`, si ferma se la quota mensile di ops si esaurisce a metà ciclo, e riduce un render fallito a un warning. Il `perceive_top` della risposta riporta quante pagine sono state effettivamente sottoposte a perceive, e `warnings` spiega le lacune.
