无需 KYC — 无需身份证件、自拍或文件。用加密货币支付:BTC, USDT, USDC, ETH, SOL, LTC
语言: 简体中文
获取 API 密钥

文档 · Node.js

Node.js 示例:用 fetch 搜索、小型池和带引用的回答

Node 18 及更新版本自带 fetch,所以 API 完全不需要任何包。本页展示一次搜索、一个可以并行运行多次搜索而不超出 rate limit 的池,以及一个把来源变成带编号引用回答的循环。

最后更新: · 3 分钟阅读

快速回答

如何从 Node.js 为 AI agent 调用网页搜索 API?

使用 Node 18+ 内置的 fetch,用 Bearer 密钥把你的查询 POST 到 https://llmwebsearch.com/api/v1/search。LLMWebSearch 返回排序后的结果,以及前 3 个页面的 Markdown,可直接编号并传给模型生成带引用的回答。一次搜索花费 1 积分,低至 US$1.20/千次搜索(截至 2026 年 10 月)。无需 npm 包。无需 KYC;加密货币支付。

你需要什么

Node.js18 或更新版本,以使用内置的 fetch;把脚本保存为 .mjs 以使用顶层 await
软件包无
密钥在你的环境中的 LLMWEBSEARCH_API_KEY
用于回答循环一个 OpenAI 兼容的聊天 endpoint:LLM_BASE_URL、LLM_API_KEY、LLM_MODEL
计费1 积分支付一次查询:最多 20 条排名结果,其中前 3 条的页面会被读取并以 Markdown 返回。读取 4 到 10 个页面为 2 积分。以错误结束的调用不收费

如何编写可复用的搜索函数?

把调用封装一次,到处复用。下面的函数发送 body,在任何 HTTP 失败时抛出可读的错误,并返回解析后的 JSON。错误会带上 API 自己的 code,例如 rate_limited 或 insufficient_credits,让调用方决定如何处理。

search.mjs
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 ?? '');
}

如何在不触发速率限制的情况下运行多次搜索?

通过一个小型池来运行:固定数量的 worker,每个 worker 从共享列表中取出下一个查询。四个 worker 是一个合理的起点。每个账户都有每分钟调用上限,显示在控制面板中;联系支持即可提高。

下面的池不需要任何库。每个 worker 保留自己的结果,失败的搜索会连同错误一起记录,而不会中断整个运行。这对研究型 agent 很重要,因为一个有问题的子问题不应让整个任务失败。

pool.mjs
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);

如何把结果变成带引用的答案?

给来源编号,在每个来源旁附上 URL,按预算裁剪内容,然后让模型按编号引用。API 返回的是来源,而不是写好的答案:由你自己的模型读取页面、撰写回复并引用 URL。下面的脚本可以与任何 OpenAI 兼容的 chat/completions endpoint 通信,通过三个环境变量选择,因此托管模型和本地模型都能用。

约 24,000 个字符(大致 6,000 个 token)的预算在各来源之间平均分配。页面无法读取的结果会以摘要形式传入,并加以标注。

answer.mjs
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?'));

从 Node 调用时该用 GET 还是 POST?

在应用代码中用 POST,带 JSON body。像 include_domains 这样的数组和较长的查询放在 body 里比放在 query string 里更干净。GET 用于快速测试和无法发送 body 的工具;用 URLSearchParams 构建它的 query string,并用逗号连接列表。

两种方式的费用相同,返回的 JSON 也相同。搜索参考 列出了两种方式的全部参数。

密钥应该放在哪里?

放在运行时读取的环境变量里,绝不要放在源文件中。在服务器上,使用你平台的 secret store。在浏览器中,根本不要调用 API:密钥会暴露给每位访客。改为调用你自己的后端,再由后端调用 LLMWebSearch。

为每个 agent、应用或团队成员各建一个 API 密钥,并可在控制面板中吊销其中任意一个,而不影响其他密钥。每个服务用一个密钥,仪表盘历史更易读,泄露的密钥也容易吊销。

Node.js:常见问题

有 npm 包吗?

没有。fetch 已内置于 Node 18 及更新版本,API 就是通过 HTTPS 传输的纯 JSON。本页这些函数就是全部集成代码。

它能在 Deno、Bun 或 edge 运行时上使用吗?

代码只用到 fetch、JSON 和 process.env。具有 fetch 的运行时,把 process.env 换成其读取 secret 的方式后即可使用。

如何重试失败的调用?

只重试 429、502 和 504,每次暂停时间翻倍。绝不重试 400、401 或 402。错误页面 解释了原因,并给出了一个辅助函数。

低至 US$1.20/千次搜索 · 无需 KYC

获取 API 密钥