Startseite Developer API v1

KI-Suche als API — deine UI, unsere Relevanz.

Jede Antwort ist bereits gewichtet und kategorisiert: Top-Treffer für Teaser, Liste für den Rest, Gruppen für Filter und „Häufig gesucht“ als Tags. Titel, Kurztext, Bild und URL kommen mit.

1 · Widget (Copy & Paste)

Volle Funktionalität ohne Code — Aussehen und Verhalten steuerst du im Backend, Feinschliff per Custom CSS.

<script src="https://cdn.querrio.com/widget.js"
  data-key="pk_dein_browser_key" defer></script>

2 · JS-SDK für eigene UIs

Rund 2 KB, ohne Abhängigkeiten. Enthält Debouncing, Session-Handling und Tracking.

<script src="https://cdn.querrio.com/sdk.js"></script>
<script>
  const asky = querrio.createClient({ key: 'pk_dein_browser_key' });

  const res = await asky.search('beispiel suchanfrage');
  // res.top_hits    → 1–2 Treffer für die Teaser-Darstellung
  // res.results     → restliche Treffer als Liste
  // res.groups      → Kategorien mit Anzahl und Treffer-IDs
  // res.suggestions → "Häufig gesucht" als Tags
  // res.intent      → erkannte Absicht, Zeitraum, Zielgruppe

  asky.trackClick(res.results[0], 1);
</script>

TypeScript-Typen

Die vollständigen Typen (inkl. Kategorien, Tier-Gewichtung, Intent und Debug-Objekt) liegen unter /sdk.d.ts. Datei ablegen oder per curl ins Projekt ziehen — danach greifen Autocompletion und Typprüfung für SDK und REST-Antworten.

curl -o types/asky.d.ts https://cdn.querrio.com/sdk.d.ts

// app.ts
/// <reference path="./types/asky.d.ts" />

const asky = window.Querrio.createClient({ key: 'pk_dein_browser_key' });

const res: querrio.SearchResponse = await asky.search('beispiel suchanfrage');
res.top_hits[0].tier;        // "top" | "list"
res.groups[0].category;      // "event" | "article" | "product" | …
res.intent.date_from;        // "2026-04-19" oder null
asky.trackClick(res.results[0], 1);

Eigene UI – komplette Beispiele

Statt des Widgets deine eigene Oberfläche: Suchfeld mit Vorschlägen, Top-Treffer mit Bild, Kategorie-Filter, „Häufig gesucht“, Hinweis bei null Treffern und Klick-Auswertung. Kopieren, Schlüssel eintragen, fertig.

<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8" />
  <title>Eigene Suche mit querrio</title>
  <script src="https://cdn.querrio.com/sdk.js"></script>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 720px; margin: 40px auto; padding: 0 16px; }
    input { width: 100%; padding: 12px 16px; border: 1px solid #ccc; border-radius: 999px; font-size: 16px; }
    .suggest { list-style: none; padding: 0; margin: 4px 0; border: 1px solid #eee; border-radius: 12px; }
    .suggest li { padding: 8px 14px; cursor: pointer; }
    .suggest li:hover { background: #f4f4f5; }
    .chips button { margin: 4px 4px 0 0; padding: 4px 12px; border: 1px solid #ddd; border-radius: 999px; background: #fff; cursor: pointer; }
    .chips button.active { background: #111; color: #fff; }
    .hit { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid #eee; text-decoration: none; color: inherit; }
    .hit img { width: 96px; height: 64px; object-fit: cover; border-radius: 8px; }
    .top { background: #f7f7f8; border-radius: 12px; padding: 12px; }
    small { color: #666; }
  </style>
</head>
<body>
  <input id="q" type="search" placeholder="Wonach suchst du?" autocomplete="off" />
  <ul id="suggest" class="suggest" hidden></ul>
  <div id="filters" class="chips"></div>
  <div id="out"></div>
  <p><small>Suche von querrio</small></p>

  <script>
    const asky = querrio.createClient({ key: "pk_dein_browser_key" });
    const $ = (id) => document.getElementById(id);
    let last = null;       // letzte Antwort
    let activeCat = null;  // gewählter Kategorie-Filter
    let timer;

    // Vorschläge beim Tippen
    $("q").addEventListener("input", () => {
      clearTimeout(timer);
      const q = $("q").value.trim();
      if (q.length < 2) { $("suggest").hidden = true; return; }
      timer = setTimeout(async () => {
        const s = await asky.suggest(q);
        if (!s.enabled || !s.items.length) { $("suggest").hidden = true; return; }
        $("suggest").innerHTML = s.items
          .map((i) => `<li data-text="${i.text}">${i.text}</li>`).join("");
        $("suggest").hidden = false;
      }, 150);
    });

    $("suggest").addEventListener("click", (e) => {
      const text = e.target.dataset.text;
      if (!text) return;
      asky.trackSuggestClick(text);
      $("q").value = text;
      $("suggest").hidden = true;
      run(text);
    });

    $("q").addEventListener("keydown", (e) => {
      if (e.key === "Enter") { $("suggest").hidden = true; run($("q").value); }
    });

    async function run(query) {
      if (!query.trim()) return;
      $("out").innerHTML = "<small>Suche läuft …</small>";
      activeCat = null;
      last = await asky.search(query);
      render();
    }

    function hitHtml(h, pos, top) {
      return `<a class="hit ${top ? "top" : ""}" href="${h.url}" data-pos="${pos}" data-id="${h.id}">
        ${h.image_url ? `<img src="${h.image_url}" alt="" />` : ""}
        <div><strong>${h.title}</strong><br /><small>${h.snippet ?? ""}</small></div>
      </a>`;
    }

    function render() {
      const r = last;
      const all = [...r.top_hits, ...r.results];

      // Keine Treffer: vorgeschlagene Begriffe anbieten
      if (!all.length) {
        $("filters").innerHTML = "";
        $("out").innerHTML = `<p>Noch nichts Passendes gefunden. Versuch es mit:</p>
          <div class="chips">${r.suggestions.map((s) => `<button data-q="${s}">${s}</button>`).join("")}</div>`;
        return;
      }

      // Kategorie-Filter aus den Gruppen
      $("filters").innerHTML = r.groups.map((g) =>
        `<button data-cat="${g.category}" class="${g.category === activeCat ? "active" : ""}">${g.label} (${g.count})</button>`
      ).join("");

      const visible = activeCat
        ? all.filter((h) => r.groups.find((g) => g.category === activeCat)?.hit_ids.includes(h.id))
        : null;

      $("out").innerHTML =
        (r.date_label ? `<small>Zeitraum: ${r.date_label}</small>` : "") +
        (visible
          ? visible.map((h, i) => hitHtml(h, i + 1, false)).join("")
          : r.top_hits.map((h, i) => hitHtml(h, i + 1, true)).join("") +
            r.results.map((h, i) => hitHtml(h, r.top_hits.length + i + 1, false)).join("")) +
        (r.suggestions.length
          ? `<p><small>Häufig gesucht</small></p><div class="chips">${r.suggestions
              .map((s) => `<button data-q="${s}">${s}</button>`).join("")}</div>`
          : "");
    }

    // Filter- und Stichwort-Klicks
    document.addEventListener("click", (e) => {
      const t = e.target.closest("button");
      if (!t) return;
      if (t.dataset.cat) { activeCat = activeCat === t.dataset.cat ? null : t.dataset.cat; render(); }
      if (t.dataset.q) { $("q").value = t.dataset.q; run(t.dataset.q); }
    });

    // Klick-Auswertung: Analytics + Autopilot lernen mit
    $("out").addEventListener("click", (e) => {
      const a = e.target.closest("a.hit");
      if (!a || !last) return;
      const hit = [...last.top_hits, ...last.results].find((h) => h.id === a.dataset.id);
      if (hit) asky.trackClick(hit, Number(a.dataset.pos));
    });
  </script>
</body>
</html>

Checkliste vor dem Livegang

  • Im Browser nur den Browser-Schlüssel (pk_…) verwenden und deine Domain im Backend unter „API & Widget“ freigeben.
  • Server-Schlüssel ausschließlich auf deinem Server nutzen – nie im Browser-Code.
  • Klick-Auswertung einbauen – ohne sie lernen Analytics und Autopilot nicht mit.
  • Den Hinweis „Suche von querrio“ gemäß deinem Abo sichtbar lassen.

3 · REST direkt

Server-Key im Header x-querrio-key. Browser-Keys sind auf die erlaubten Origins beschränkt.

curl -X POST https://cdn.querrio.com/api/public/v1/search \

  -H 'content-type: application/json' \
  -H 'x-querrio-key: sk_dein_server_key' \
  -d '{ "query": "beispiel suchanfrage", "limit": 10 }'

Auto-Suggest

GET /api/public/v1/suggest?q= liefert Vorschläge aus erfolgreichen früheren Suchen und aus Titeln und Kategorien des Index — also auch direkt nach dem ersten Crawl. Aktivierung, Mindestzeichen, Anzahl und Direkttreffer stellst du im Backend unter „API & Widget“ ein; ist Auto-Suggest aus, antwortet die API mit enabled: false und leeren Listen.

// Typeahead: Vorschläge während der Eingabe
const asky = querrio.createClient({ key: "pk_dein_browser_key" });

input.addEventListener("input", async () => {
  const res = await asky.suggest(input.value); // ältere Anfragen brechen automatisch ab
  if (!res.enabled) return;                    // im Backend deaktiviert
  render(res.items);  // [{ text: "beispielbegriff", type: "popular" | "content" }]
  render(res.hits);   // Direkttreffer: id, url, title, category, image_url
  showDate(res.date_label); // erkannter Zeitbezug, z. B. "morgen"
});

// Klick auf einen Vorschlag melden (fließt in Analytics und Autopilot):
asky.trackSuggestClick(item.text);
asky.trackSuggestClick(hit.title, hit.id);

Antwortstruktur

{
  "query": "beispiel suchanfrage",
  "intent": { "topic": "beispielthema", "audience": null },
  "date_from": null, "date_to": null, "date_label": null,
  "top_hits": [
    { "id": "…", "url": "https://…", "title": "Beispiel-Seitentitel",
      "snippet": "…", "image_url": "https://…", "category": "article",
      "tags": ["beispiel"], "score": 0.91, "tier": "top",
      "is_event": false, "event_start": null,
      "event_location": null }
  ],
  "results": [ … ],
  "groups": [ { "category": "article", "label": "Beiträge", "count": 6, "hit_ids": [ … ] } ],
  "suggestions": ["verwandter suchbegriff", "weiterer suchbegriff"],
  "total": 7, "took_ms": 240, "query_id": "…"
}

Debug-Mode

"debug": true (oder ?debug=1) liefert zusätzlich ein debug-Objekt: erkannter Intent, aufgelöstes Datum und Zielgruppe samt Quelle, aktive Filter und Ranking-Gewichte, alle Kandidaten vor dem Reranking, die Reihenfolge davor/danach, Zero-Result-Rescue, Teaser-Schwellen, Score-Details je Treffer und Timings. Server-Keys dürfen das immer; für Browser-Keys musst du es im Backend unter „API & Widget“ freischalten (sonst antwortet die API mit dem Header x-asky-debug: denied und ohne Debug-Objekt).

curl -X POST https://cdn.querrio.com/api/public/v1/search \
  -H 'content-type: application/json' \
  -H 'x-querrio-key: sk_dein_server_key' \
  -d '{ "query": "beispiel suchanfrage", "debug": true }'
"debug": {
  "version": 1,
  "query": { "raw": "beispiel suchanfrage",
             "normalized": "beispiel suchanfrage",
             "language": "de", "limit": 10, "candidate_limit": 30 },
  "intent": { "topic": "beispielthema", "audience": null,
              "entities": { … }, "prompt_override": false },
  "resolved": { "date_from": null, "date_to": null,
                "date_label": null, "date_source": "none",
                "audience": null, "audience_source": "none",
                "now": "2026-04-18T09:12:00Z" },
  "filters": { "date_from": null, "date_to": null,
               "audience": null, "category": null, "tags": [] },
  "weights": { "vector": 0.6, "fts": 0.25, "quality": 0.1,
               "date_boost": 0.15, "audience_boost": 0.1 },
  "candidates": [ { "rank": 1, "id": "…", "url": "https://…",
                    "title": "Beispiel-Seitentitel", "score": 0.83,
                    "is_event": false, "event_start": null } ],
  "rerank":  { "applied": true, "model_used": true, "before": [ … ], "after": [ … ] },
  "rescue":  { "attempted": false, "from_cache": false, "candidates": [], "used": null },
  "tiering": { "top_score": 0.91, "max_top_hits": 2,
               "tier_threshold": 0.12, "top_hit_ids": [ … ] },
  "scores":  [ { "id": "…", "position": 1, "final_score": 0.91,
                 "relative_to_top": 1, "tier": "top", "category": "article" } ],
  "timings": { "config_ms": 12, "intent_embed_ms": 180, "retrieval_ms": 41,
               "rerank_ms": 260, "total_ms": 512 },
  "notes": []
}

Endpunkte

  • POST/api/public/v1/searchIntent-Suche. Liefert Top-Treffer, Liste, Kategorien und Vorschläge.
  • GET/api/public/v1/suggest?q=Typeahead: Vorschläge und Instant-Treffer.
  • GET/api/public/v1/schemaKategorien, Sprachen und Widget-Konfiguration.
  • POST/api/public/v1/trackKlicks, Verweildauer und Feedback melden.
  • GET/api/public/v1/openapi.jsonOpenAPI-3.1-Spezifikation für Codegen.

Limits & Hinweise

  • Rate-Limit pro Key und Minute; bei Überschreitung antwortet die API mit 429.
  • Feedback- und Klick-Events fließen direkt in das automatische Ranking-Training ein.
  • Alle Endpunkte sind CORS-fähig und benötigen keinen Proxy.