---
seo_title: Lookup (Phase 2): Websuche-API für Agenten | EnConvert
meta_desc: Private Beta, Phase 2: eine Web-, News-, Scholar- oder Maps-Suche ausführen und die Top-Treffer optional perceiven. Anbieterneutrale Suche über einen Endpunkt.
keywords: websuche api für llm agenten, serp api alternative, google search api für ki agenten, such api mit seiteninhalt, serper api alternative, firecrawl search alternative, web search api für rag pipeline, nachrichten suche api json
---

# Websuche-API für LLM-Agenten

<div class="alert alert-warning">
<strong>Private Beta.</strong> Lookup ist heute mit deinem normalen API-Schlüssel aufrufbar, in jedem Tarif einschließlich Founding, und zieht wie jeder andere Aufruf von deinem monatlichen Ops-Kontingent ab. Angekündigt oder allgemein verfügbar ist Lookup nicht: Request- und Response-Formen können sich jederzeit ohne Vorankündigung ändern, und es gibt keine Stabilitäts- oder Supportzusage, baue also noch nichts Tragendes darauf. Die Roadmap steht unter <a href="/de/docs/coming-soon">Demnächst</a>, und jedes Release wird im <a href="/de/changelog">Changelog</a> angekündigt.
</div>

`POST /v2/lookup` ist eine Websuche-API für LLM-Agenten: Sie führt eine
Suche in einer von sechs Kategorien aus und liefert eine flache,
anbieterneutrale Ergebnisliste zurück. Setzt du `perceive_top`, rendert sie
zusätzlich die Top-N-Ergebnis-URLs in einem echten Browser, sodass ein
Agent die Suchmaschinen-Ergebnisseite (SERP) *und* den Seiteninhalt hinter
jedem Treffer in einem einzigen Roundtrip erhält. Als SERP-API-Alternative
fasst sie den üblichen Stack (eine Such-API ansprechen, deren Ergebnisse
parsen, dann einen Scraper hinterherschicken) zu einem einzigen Aufruf
zusammen. Sie wird EnConverts Antwort auf Firecrawl `/search` sein.

Serper ist der Suchanbieter dahinter. Request und Response sprechen ein
neutrales Suchvokabular (`category`, `country`, `locale`, `time_filter`),
sodass ein künftiger Anbieterwechsel den Vertrag, gegen den du
programmierst, nicht ändert.

Hier ist der kleinste sinnvolle Aufruf. Sende eine Query, erhalte die
obersten Webergebnisse zurück:

```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"
  }'
```

Die Response ist eine flache Ergebnisliste plus Provenienz für die
Support-Korrelation:

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

---

## Endpunkte

| Methode | Pfad | Zweck |
|--------|------|-------|
| `POST` | `/v2/lookup` | Eine Suche ausführen und optional die Top-N-Ergebnis-URLs automatisch perceiven. |

`/v2/lookup` ist ein Single-Call-Endpunkt ohne separaten Status- oder
Abrufpfad. Beim Auto-Perceive wird jede gerenderte Seite zu
einer vollwertigen [Perceive](/de/docs/endpoints/perceive.md)-Operation mit eigener
`operation_id`, die du später über `GET /v2/perceive/{operation_id}`
erneut abrufen kannst.

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

---

## Authentifizierung

Authentifiziere dich mit einem privaten Schlüssel im `X-API-Key`-Header
für Server-zu-Server-Aufrufe. Diesen Weg nutzen die Beispiele unten.

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

Öffentliche Schlüssel mit einem JWT-Bearer-Token funktionieren ebenfalls
und nutzen denselben Ablauf wie jeder andere Endpunkt: Erzeuge ein
Token mit deinem `pk_`-Schlüssel und sende es dann als
`Authorization: Bearer <token>`. Der vollständige Ablauf, inklusive
Domain-Locking und Token-Refresh, steht im
[Authentifizierungsleitfaden](/de/docs/authentication.md).

Jeder API-Schlüssel trägt eine Allowlist erlaubter Endpunkte. Wenn
`/v2/lookup` nicht auf der Liste des Schlüssels steht, wird der Request
mit `403` abgewiesen.

---

## Wie lookup funktioniert

Ein Request führt eine Anbietersuche aus und rendert, nur wenn du es
anforderst, anschließend die obersten Ergebnisse.

1. **Quota-Gate.** Bevor irgendetwas abgerechnet wird, prüft der Handler
   das einheitliche monatliche Ops-Kontingent deines Plans. Ein
   deaktivierter Plan oder ein ausgeschöpftes Kontingent wird mit `402`
   abgewiesen, sodass bei einer Ablehnung nichts berechnet wird.
2. **Suche.** Die Query geht an den Suchanbieter (Serper) auf dem
   Endpunkt für deine `category`. Aktualität, Land, Locale, Standort,
   Seitengröße und Autokorrektur werden auf die Parameter des Anbieters
   abgebildet.
3. **Normalisieren.** Jeder Anbietertreffer wird in ein neutrales
   `LookupResult` mit `title`, `url`, `snippet` und `position`
   abgeflacht. Die kategoriespezifischen Extras landen in `extra`, sodass
   der Vertrag nie eine Spalte pro Anbieter-Eigenheit dazubekommt.
4. **Abrechnen und auditieren.** Die Suche war erfolgreich, also wird
   eine Op berechnet und eine `ch_lookup_queries`-Audit-
   Zeile geschrieben. Die Zeilen-ID kommt als `lookup_id` für die
   Support-Korrelation zurück.
5. **Auto-Perceive (optional).** Wenn `perceive_top > 0`, werden die
   Top-N-Ergebnis-URLs eine nach der anderen durch das gemeinsame
   Headless-Chrome-Singleton gerendert, dieselbe Pipeline wie
   [der Perceive-Endpunkt](/de/docs/endpoints/perceive.md). Jedes Rendering ist
   eine vollständige `/v2/perceive`-Operation: eigene berechnete Op,
   eigene Operations-Zeile, eigene `operation_id`. Standardmäßig fordert
   Auto-Perceive nur Markdown an, ohne Screenshot, ohne PDF und ohne
   LLM-Extraktion; sende ein `enrich`-Objekt, um die Ausgaben zu
   erweitern, sie parallel laufen zu lassen oder Schema-Extraktion und
   eine synthetisierte Antwort zu ergänzen.

Auto-Perceive ist Best-Effort. Eine einzelne URL, die fehlschlägt, oder
ein mitten im Durchlauf erschöpftes Ops-Kontingent stuft auf eine
Warnung herab und liefert trotzdem die Suchergebnisse. Die SERP ist
hier das primäre Produkt, ein Wahrnehmungsproblem versenkt also nie den
gesamten Aufruf.

---

## Request-Parameter

### Query und Kategorie

| Parameter | Typ | Standard | Beschreibung |
|-----------|------|---------|-------------|
| `query` | `string` | -- | Die Suchanfrage. 1 bis 512 Zeichen, von führendem/nachgestelltem Whitespace bereinigt. Eine nach dem Trimmen leere Query wird mit `422` abgewiesen. Erforderlich. |
| `category` | `string` | `"web"` | Eines von `web`, `news`, `images`, `scholar`, `patents`, `maps`. |

### Targeting und Aktualität

| Parameter | Typ | Standard | Beschreibung |
|-----------|------|---------|-------------|
| `country` | `string` | `null` | Google-`gl`-Ländercode, z. B. `us`, `in`. Max. 8 Zeichen. |
| `locale` | `string` | `null` | Google-`hl`-Oberflächensprache, z. B. `en`. Max. 16 Zeichen. |
| `time_filter` | `string` | `null` | Auf Ergebnisse aus dem vergangenen Zeitraum beschränken: `hour`, `day`, `week`, `month` oder `year`. |
| `location` | `string` | `null` | Freitext-Standortangabe, z. B. `"Austin, Texas"`. Max. 128 Zeichen. |
| `autocorrect` | `boolean` | `true` | Ob der Anbieter die Rechtschreibung der Query automatisch korrigieren darf. |

### Pagination

| Parameter | Typ | Standard | Beschreibung |
|-----------|------|---------|-------------|
| `num_results` | `integer` | `10` | Ergebnisse pro Seite. 1 bis 100. |
| `page` | `integer` | `1` | Seitenzahl. 1 bis 10. |

### Auto-Perceive

| Parameter | Typ | Standard | Beschreibung |
|-----------|------|---------|-------------|
| `perceive_top` | `integer` | `0` | Perceive die ersten N Ergebnis-URLs automatisch, die einen navigierbaren Link haben. 0 bis 10. Jede ist ein vollständiges Browser-Rendering, das eine Op aus deinem monatlichen Kontingent verbraucht, weshalb es bei 10 gedeckelt ist. Für größere Mengen nimm die `url`-Felder und rufe [den Perceive-Batch-Endpunkt](/de/docs/endpoints/perceive.md#batch-perception) auf. `0` deaktiviert Auto-Perceive. |

### Anreicherung (`enrich`)

Ein optionales `enrich`-Objekt steuert, wie die Top-N-Ergebnisse
(`perceive_top`) gelesen werden, und kann eine fundierte Antwort über sie
hinweg synthetisieren. Wenn `enrich` weggelassen wird, behält
`perceive_top` sein Standardverhalten (nur Markdown, ein Ergebnis nach
dem anderen).

| Parameter | Typ | Standard | Beschreibung |
|-----------|------|---------|-------------|
| `enrich.outputs` | `string[]` | `["markdown"]` | Welche Perceive-Ausgaben pro angereichertem Ergebnis erzeugt werden, z. B. `markdown`, `html_cleaned`, `links`, `screenshot`, `structured`. Siehe [Perceive-Ausgaben](/de/docs/endpoints/perceive.md#outputs). |
| `enrich.concurrency` | `integer` | `3` | Wie viele Ergebnis-URLs parallel angereichert werden. 1 bis 5. Markdown-/HTML-Renderings parallelisieren; Screenshot-/PDF-Renderings serialisieren auf dem gemeinsamen Browser. |
| `enrich.schema` | `object` | `null` | Führt eine schema-gesteuerte strukturierte Extraktion gegen jedes angereicherte Ergebnis aus. Die extrahierten Daten erscheinen unter `perceive.structured` des jeweiligen Ergebnisses. JSON-Schema-Objekt oder eine flache `{field: description}`-Map. |
| `enrich.synthesize_answer` | `boolean` | `false` | Synthetisiert eine zitierte, fundierte Antwort auf die Query über die angereicherten Ergebnisse hinweg, zurückgegeben als `answer` (mit `answer_sources`). Verwendet den perceiveten Seiteninhalt, wenn verfügbar, andernfalls die Ergebnis-Snippets. |
| `enrich.answer_prompt` | `string` | `null` | Eine Frage, die anstelle der rohen Query beantwortet wird. Wird nur verwendet, wenn `synthesize_answer` gleich `true` ist. Max. 1,000 Zeichen. |

`enrich.schema` und `enrich.synthesize_answer` verwenden die
LLM-Extraktionsstufe. Wenn dieser Extraktionsschritt nicht ausgeführt
werden kann, stufen sie auf eine Warnung herab, und der Rest der
Response bleibt unberührt.

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

---

## Kategorien

Jede Kategorie spricht einen anderen Anbieter-Endpunkt an und liefert
eine leicht abweichende Ergebnisform. Die universellen Felder (`title`,
`url`, `snippet`, `position`) sind stets typisiert; kategoriespezifische
Felder landen in `extra`.

| `category` | Was sie durchsucht | Bemerkenswert befüllte Felder |
|------------|--------------------|--------------------------|
| `web` | Allgemeine Webergebnisse | `title`, `url`, `snippet`, `date`, `position` |
| `news` | Nachrichtenartikel | ergänzt `source`, `image_url` |
| `images` | Bildergebnisse | `image_url`, `thumbnail_url`, `source` (oft kein `snippet`) |
| `scholar` | Akademische Ergebnisse | gleiche Form wie `web`; Zitationszahlen in `extra` |
| `patents` | Patentergebnisse | gleiche Form wie `web`; Patentfelder in `extra` |
| `maps` | Lokale Orte | `url` ist die Website des Ortes; `snippet` trägt die Adresse; Bewertung, Koordinaten in `extra` |

Erwähnenswert: Bei `images` und `maps` kann `url` für einen bestimmten
Treffer `null` sein, wenn der Anbieter keinen navigierbaren Link
zurückgibt. Auto-Perceive überspringt jedes Ergebnis, dessen `url`
`null` ist, sodass ein `perceive_top` von 5 auf einer SERP mit zwei
URL-losen Treffern höchstens drei Seiten perceived.

---

## Antwort

Der Endpunkt lässt `null`-Felder weg, sodass ein minimales `web`-Ergebnis
nur die Felder trägt, die tatsächlich befüllt sind.

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `lookup_id` | `integer` | Die `ch_lookup_queries`-Audit-Zeilen-ID. Nenne sie dem Support. `null`, wenn der Audit-Write fehlschlug, wobei die Ergebnisse trotzdem gültig sind. |
| `query` | `string` | Die (getrimmte) Query, die du gesendet hast. |
| `category` | `string` | Die durchsuchte Kategorie. |
| `country` | `string` | Echo des gesendeten `country`, falls vorhanden. |
| `locale` | `string` | Echo des gesendeten `locale`, falls vorhanden. |
| `time_filter` | `string` | Echo des gesendeten `time_filter`, falls vorhanden. |
| `total` | `integer` | Anzahl der zurückgegebenen Ergebnisse. |
| `results` | `LookupResult[]` | Die Ergebnisliste. Siehe unten. |
| `perceive_top` | `integer` | Wie viele Ergebnisse *tatsächlich* perceived wurden: höchstens der von dir angeforderte Wert und niedriger, wenn das Ops-Kontingent erschöpft war oder URLs fehlschlugen. |
| `perceive_operation_ids` | `string[]` | Die `per_...`-Operations-IDs der perceiveten Ergebnisse, in Reihenfolge. |
| `answer_box` | `object` | Die Answer-Box des Anbieters, falls vorhanden. |
| `knowledge_graph` | `object` | Das Knowledge-Graph-Panel des Anbieters, falls vorhanden. |
| `answer` | `string` | Die synthetisierte, zitierte Antwort über die angereicherten Ergebnisse hinweg. Nur vorhanden, wenn `enrich.synthesize_answer` gleich `true` ist und sie erfolgreich war. |
| `answer_sources` | `string[]` | Die als Grundlage für `answer` verwendeten URLs, in Zitationsreihenfolge. |
| `credits` | `integer` | Von dieser Query verbrauchte Anbieter-Credits. |
| `cost_cents` | `number` | Geldkosten der Suche in Cent. Heute pauschal `0.06` pro Query. |
| `warnings` | `string[]` | Nicht-fatale Hinweise: ein übersprungenes URL-loses Ergebnis, ein Auto-Perceive-Fehler, ein mitten im Loop erschöpftes Ops-Kontingent. |

### LookupResult

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `title` | `string` | Ergebnistitel. |
| `url` | `string` | Kanonischer Seiten-Link, also das, was du perceiven würdest. `null` für Ergebnisse ohne navigierbare URL. |
| `snippet` | `string` | Ergebnis-Snippet. Bei `maps` trägt dies die Adresse. |
| `position` | `integer` | Die Position des Ergebnisses auf der SERP. |
| `source` | `string` | Quelle/Herausgeber, bei `news` und `images`. |
| `date` | `string` | Veröffentlichungsdatum, wenn der Anbieter eines meldet. |
| `image_url` | `string` | Bild-URL, bei `images` und `news`. |
| `thumbnail_url` | `string` | Thumbnail-URL, bei `images`. |
| `extra` | `object` | Kategoriespezifische Felder außerhalb des neutralen Sets: Bewertungen, Koordinaten, Zitationszahlen und so weiter. |
| `perceive` | `PerceiveResponse` | Das vollständige Inline-Perceive-Ergebnis für diese URL, nur vorhanden für die Top-N, wenn `perceive_top > 0` und das Rendering erfolgreich war. Dieselbe Objektform wie [der Perceive-Endpunkt](/de/docs/endpoints/perceive.md#response). |

---

## Auto-Perceive-Ergebnisse lesen

Wenn du `perceive_top` sendest, gehe die Ergebnisse durch und prüfe auf
das `perceive`-Feld. Es ist nur auf den Ergebnissen vorhanden, die
perceived wurden, und nur, wenn ihr Rendering erfolgreich war. Das
Markdown für jedes liegt hinter einer vorsignierten Download-URL (ein
kurzlebiger, signierter Link zum Objektspeicher) unter
`perceive.outputs.markdown.url`, genauso wie bei einem direkten
Perceive-Aufruf.

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

Diese signierten URLs laufen nach 15 Minuten ab. Um eine perceivete
Seite später herunterzuladen, rufe ihre Operation erneut mit
`GET /v2/perceive/{operation_id}` ab, unter Verwendung der ID aus
`perceive_operation_ids`. Das signiert die URLs neu und rendert nicht
erneut, kostet also keine Ops. Siehe
[den Perceive-Abruf-Abschnitt](/de/docs/endpoints/perceive.md#retrieve-an-operation)
für die Details.

---

## Codebeispiele

### curl: Websuche

```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: aktuelle Nachrichten, lokalisiert

```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: Suche plus Auto-Perceive der Top 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);
}
```

Wenn du EnConvert aus Claude, Cursor oder einem anderen Model-Context-
Protocol-(MCP-)Client aufrufst, wird die Suchfähigkeit auch dort als Tool
bereitgestellt. Siehe [die MCP-Serverseite](/de/mcp.md).

---

## Fehlerantworten

Der Handler gibt nie rohen Anbietertext an den Client zurück. Anbieter-
und SSRF-Details bleiben in den Server-Logs, und der Client erhält eine
saubere, generische Meldung.

| Status | Bedingung |
|--------|-----------|
| `401 Unauthorized` | Fehlender oder ungültiger API-Schlüssel / JWT-Token. |
| `402 Payment Required` | Lookup ist nicht in deinem aktuellen Plan, oder dein monatliches Ops-Kontingent ist erschöpft. |
| `403 Forbidden` | `/v2/lookup` ist nicht in den erlaubten Endpunkten des API-Schlüssels. |
| `422 Unprocessable Entity` | Request-Validierung fehlgeschlagen: leere/zu lange `query`, eine unbekannte `category` oder `time_filter`, `num_results` oder `page` außerhalb des Bereichs, `perceive_top` über 10. |
| `502 Bad Gateway` | Der Suchanbieter hat eine Fehler-Response oder einen nicht wiederholbaren Transportfehler zurückgegeben (`SearchUpstreamError`). Ein erneuter Versuch kann helfen. |
| `503 Service Unavailable` | Der Suchanbieter ist serverseitig fehlkonfiguriert (ein fehlender Schlüssel auf unserer Seite, `SearchConfigError`), oder er ist vorübergehend nicht verfügbar: der Circuit-Breaker ist offen, oder der Anbieter hat uns rate-limitiert (`SearchUnavailableError`). Versuche es später erneut. |
| `500 Internal Server Error` | Ein unerwarteter Fehler. Die Meldung ist generisch; nenne dem Support die Uhrzeit des Aufrufs. |

Ein fehlschlagendes Auto-Perceive löst nie einen eigenen Fehler aus. Es
landet in `warnings`, und der Aufruf antwortet trotzdem mit `200`. Die
vollständige Statuscode-Referenz steht im
[Fehlercode-Leitfaden](/de/docs/reference/errors.md).

---

## Limits

| Limit | Wert |
|-------|------|
| `query`-Länge | 1 bis 512 Zeichen (getrimmt) |
| `country`-Länge | 8 Zeichen |
| `locale`-Länge | 16 Zeichen |
| `location`-Länge | 128 Zeichen |
| `num_results` | 1 bis 100 |
| `page` | 1 bis 10 |
| `perceive_top` | 0 bis 10 |
| Ops pro Aufruf | 1 für die Query, plus 1 pro auto-perceivetem Ergebnis |
| Auto-Perceive-Outputs | Standardmäßig Markdown; erweiterbar mit `enrich.outputs` |
| Auto-Perceive-Nebenläufigkeit | Standardmäßig sequenziell; 1 bis 5 mit `enrich.concurrency` |
| Kosten pro Suche | 0.06 Cent pauschal |
| Ablauf der signierten URL einer perceiveten Seite | 15 Minuten |

---

## Häufig gestellte Fragen

### Wie führe ich mit einem einzigen REST-API-Aufruf eine Websuche durch und erhalte gleichzeitig den Seiteninhalt zurück?

Sende `POST /v2/lookup` mit einer `query` und setze `perceive_top` (0 bis 10). Die Top-N-Ergebnis-URLs werden in einem echten Browser gerendert, und jedes perceivete Ergebnis trägt ein inline `perceive`-Objekt, dessen Markdown hinter einer vorsignierten URL unter `perceive.outputs.markdown.url` liegt.

### Ist /v2/lookup eine SERP-API-Alternative, die ich ohne Provider-Lock-in einsetzen kann?

Ja. Serper ist der Suchanbieter dahinter, aber Request und Response sprechen ein neutrales Suchvokabular (`category`, `country`, `locale`, `time_filter`), sodass ein künftiger Anbieterwechsel den Vertrag, gegen den du programmierst, nicht ändert.

### Welche Suchkategorien unterstützt die Lookup-API?

Sechs: `web` (die Standardeinstellung), `news`, `images`, `scholar`, `patents` und `maps`. Die universellen Felder (`title`, `url`, `snippet`, `position`) sind stets typisiert, und kategoriespezifische Extras landen in `extra`.

### Warum hat lookup weniger Seiten perceived als mein `perceive_top`-Wert?

Auto-Perceive überspringt Ergebnisse, deren `url` `null` ist, stoppt, wenn das monatliche Ops-Kontingent mitten im Loop erschöpft ist, und stuft ein fehlgeschlagenes Rendering auf eine Warnung herab. Das `perceive_top` der Antwort meldet, wie viele Seiten tatsächlich perceived wurden, und `warnings` erklärt die Lücken.
