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
| 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ć.
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.
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.
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 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 wyjaśnia dlaczego i pokazuje helper.