# Przykłady w Node.js: wyszukiwanie z fetch, mała pula i odpowiedzi z cytatami

> Przykłady API wyszukiwania webowego w Node.js: fetch, mała pula współbieżności i pętla odpowiedzi z cytatami dla każdego modelu OpenAI. Bez KYC, kryptowaluty.

- URL: https://llmwebsearch.com/pl/docs/node/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: LLMWebSearch (https://llmwebsearch.com)

Node 18 i nowsze mają wbudowany `fetch`, więc API nie potrzebuje żadnego pakietu. Ta strona pokazuje jedno wyszukiwanie, pulę uruchamiającą kilka wyszukiwań bez zalewania rate limit oraz pętlę, która zamienia źródła w odpowiedź z numerowanymi cytatami.

**Szybka odpowiedź — Jak wywołać API wyszukiwania webowego dla agentów AI z Node.js?**

Użyj wbudowanego `fetch` z Node 18+, aby wysłać swoje zapytanie jako POST do `https://llmwebsearch.com/api/v1/search` z kluczem Bearer. **LLMWebSearch** odpowiada rankingiem wyników i 3 najlepszymi stronami jako Markdown, gotowymi do ponumerowania i przekazania modelowi w celu uzyskania odpowiedzi z cytatami. Jedno wyszukiwanie kosztuje 1 kredyt, od 1,20 USD/1K wyszukiwań według stanu na październik 2026. Pakiet npm nie jest potrzebny. Bez KYC; płatność kryptowalutą.

## Co potrzebujesz

| Atrybut | Wartość |
| --- | --- |
| Node.js | 18 lub nowszy, dla wbudowanego `fetch`; zapisz skrypty jako `.mjs` dla top-level `await` |
| Pakiety | Brak |
| Klucz | `LLMWEBSEARCH_API_KEY` w Twoim środowisku |
| Dla pętli odpowiedzi | Endpoint czatu zgodny z OpenAI: `LLM_BASE_URL`, `LLM_API_KEY`, `LLM_MODEL` |
| Rozliczanie | Jeden kredyt opłaca jedno wyszukiwanie: do 20 wyników z rankingiem, przy czym strony z pierwszych 3 są odczytywane i zwracane jako Markdown. Odczyt od 4 do 10 stron to 2 kredyty. Wywołanie zakończone błędem nic nie kosztuje |

## Jak napisać funkcję wyszukiwania wielokrotnego użytku?

Owiń wywołanie raz i używaj go wszędzie. Poniższa funkcja wysyła ciało, rzuca czytelny błąd przy każdym błędzie HTTP i zwraca sparsowany JSON. Błędy zawierają własny `code` API, taki jak `rate_limited` lub `insufficient_credits`, więc wywołujący może zdecydować, co zrobić.

*search.mjs*

```javascript
const API = 'https://llmwebsearch.com/api/v1/search';

export async function search(body) {
  const res = await fetch(API, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  const data = await res.json();
  if (!res.ok) {
    const err = new Error(`${res.status} ${data.error?.code}: ${data.error?.message}`);
    err.status = res.status;
    throw err;
  }
  return data;
}

const data = await search({ q: 'how to size a postgres connection pool', read: 3 });
for (const r of data.results) {
  console.log(r.position, r.content_status, r.url, r.word_count ?? '');
}

```

## Jak uruchomić kilka wyszukiwań bez trafiania na rate limit?

Uruchom je przez niewielką pulę: stałą liczbę workerów, z których każdy pobiera kolejne zapytanie ze wspólnej listy. Cztery workery to rozsądny początek. Każde konto ma limit wywołań na minutę, widoczny w dashboardzie; poproś support, a można go podnieść.

Poniższa pula nie wymaga żadnej biblioteki. Każdy worker przechowuje własne wyniki, a nieudane wyszukiwanie jest zapisywane wraz z błędem, zamiast przerywać całe uruchomienie. To ważne dla agentów badawczych, gdzie jedno błędne podpytanie nie powinno kosztować całego zadania.

*pool.mjs*

```javascript
const API = 'https://llmwebsearch.com/api/v1/search';
const questions = [
  'what is connection pooling in postgres',
  'recommended pool size per cpu core',
  'pgbouncer transaction mode limitations',
  'connection pool exhaustion symptoms',
  'pool size for serverless functions',
];

async function search(q) {
  const res = await fetch(API, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ q, read: 2, max_chars: 4000 }),
  });
  const data = await res.json();
  if (!res.ok) return { q, error: data.error?.code ?? String(res.status) };
  return { q, urls: data.results.filter((r) => r.content_status === 'ok').map((r) => r.url) };
}

async function runPool(items, size, task) {
  const out = [];
  let next = 0;
  const worker = async () => {
    while (next < items.length) {
      const i = next++;
      out[i] = await task(items[i]);
    }
  };
  await Promise.all(Array.from({ length: size }, worker));
  return out;
}

for (const row of await runPool(questions, 4, search)) console.log(row);

```

## Jak zamienić wyniki w odpowiedź z cytatami?

Ponumeruj źródła, umieść URL obok każdego, przytnij je do budżetu i poproś model o cytowanie według numeru. API zwraca źródła, a nie gotową odpowiedź: Twój własny model czyta strony, pisze odpowiedź i cytuje adresy URL. Poniższy skrypt łączy się z dowolnym endpointem zgodnym z OpenAI `chat/completions`, wybieranym przez trzy zmienne środowiskowe, więc działa zarówno z modelami hostowanymi, jak i lokalnymi.

Budżet około 24 000 znaków, czyli mniej więcej 6000 tokenów, jest dzielony równo między źródłami. Wynik, którego strony nie udało się odczytać, jest przekazywany jako jego snippet, odpowiednio oznaczony.

*answer.mjs*

```javascript
const SEARCH = 'https://llmwebsearch.com/api/v1/search';
const BUDGET = 24000; // characters, about 6,000 tokens

async function sources(question) {
  const res = await fetch(SEARCH, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ q: question, max_results: 5, read: 3 }),
  });
  const { results } = await res.json();
  const share = Math.floor(BUDGET / Math.max(results.length, 1));
  return results
    .map((r, i) => {
      const body = r.content_status === 'ok' ? r.content.slice(0, share) : `(snippet only) ${r.snippet}`;
      return `[${i + 1}] ${r.title} - ${r.url}\n${body}`;
    })
    .join('\n\n');
}

async function answer(question) {
  const prompt = 'Answer using only the sources below. Cite them as [1], [2] and so on. '
    + 'Say so if they do not answer the question.\n\n' + (await sources(question)) + '\n\nQuestion: ' + question;
  const res = await fetch(`${process.env.LLM_BASE_URL.replace(/\/$/, '')}/chat/completions`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.LLM_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ model: process.env.LLM_MODEL, messages: [{ role: 'user', content: prompt }] }),
  });
  const data = await res.json();
  return data.choices[0].message.content;
}

console.log(await answer('How should I size a Postgres connection pool?'));

```

## Czy z Node powinienem używać GET czy POST?

POST, z ciałem JSON, w kodzie aplikacji. Tablice takie jak `include_domains` i długie pytania podróżują czyściej w ciele niż w query stringu. GET jest do szybkich testów i dla narzędzi, które nie mogą wysłać ciała; zbuduj jego query string za pomocą `URLSearchParams` i łącz listy przecinkami.

Obie formy kosztują tyle samo i zwracają ten sam JSON. [Dokumentacja wyszukiwania](https://llmwebsearch.com/pl/docs/search/) wymienia każdy parametr dla obu.

## Gdzie powinien znajdować się klucz?

W zmiennej środowiskowej odczytywanej w czasie działania, nigdy w pliku źródłowym. Na serwerze używaj magazynu sekretów swojej platformy. W przeglądarce nie wywołuj API w ogóle: klucz byłby widoczny dla każdego odwiedzającego. Wywołuj własny backend, który następnie wywołuje LLMWebSearch.

Twórz jeden klucz API na agenta, aplikację lub członka zespołu i unieważniaj dowolny z nich z dashboardu, nie ruszając pozostałych. Jeden klucz na usługę ułatwia czytanie historii w dashboardzie i łatwe unieważnienie wyciekniętego klucza.

## Node.js: najczęstsze pytania

### Czy istnieje pakiet npm?

Nie. `fetch` jest wbudowane w Node 18 i nowsze, a API to zwykły JSON przez HTTPS. Funkcje na tej stronie to cała integracja.

### Czy działa na Deno, Bun lub w środowiskach edge?

Kod używa tylko `fetch`, `JSON` i `process.env`. Środowiska z `fetch` działają po zamianie `process.env` na własny sposób odczytu sekretów.

### Jak ponawiać nieudane wywołania?

Ponawiaj tylko `429`, `502` i `504`, z pauzą, która za każdym razem się podwaja. Nigdy nie ponawiaj `400`, `401` ani `402`. [Strona błędów](https://llmwebsearch.com/pl/docs/errors/) wyjaśnia dlaczego i pokazuje helper.

---

LLMWebSearch: LLMWebSearch to web search API dla agentów AI, który zwraca uszeregowane wyniki wraz z treścią najlepszych stron w formacie Markdown, z płatnością kryptowalutą i bez przesyłania dokumentu tożsamości. Kredyty przedpłacone od 1,20 USD/1K wyszukiwań nigdy nie wygasają. Bez KYC — bez dokumentu, bez selfie, bez zaświadczeń. Płatność kryptowalutą.
