serpkite 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -4
- package/dist/index.cjs +36 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +640 -21
- package/dist/index.d.ts +640 -21
- package/dist/index.js +36 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,6 +94,8 @@ vertical-specific extras and `meta` (`request_id`, `credits_used`, `cached`, `la
|
|
|
94
94
|
| `patents(params)` | `POST /v1/patents` | `SearchParams` | `PatentsResponse` |
|
|
95
95
|
| `autocomplete(params)` | `POST /v1/autocomplete` | `SearchParams` | `AutocompleteResponse` (`results[].value`) |
|
|
96
96
|
| `webpage(params)` | `POST /v1/webpage` | `WebpageParams` (`url`, `include_html`) | `WebpageResponse` (`markdown`, `text`, `metadata`) |
|
|
97
|
+
| `chatgpt(params)` | `POST /v1/chatgpt` | `ChatGPTParams` (`prompt`, `web_search`, `country`, `track`, `include_html`) | `AssistantResponse` |
|
|
98
|
+
| `gemini` / `copilot` / `aiMode` / `braveAi(params)` | `POST /v1/gemini` / `copilot` / `ai_mode` / `brave_ai` | `AssistantParams` (`prompt`, `country`, `language`, `track`, `include_html`) | `AssistantResponse` (`text`, `markdown`, `sources`, `search_queries`, `products`, `places`, `mentions`) |
|
|
97
99
|
| `rank(params)` | `POST /v1/rank` | `RankParams` (`q`, `domain`, `num`: 10\|20\|30\|50\|100) | `RankResponse` (`position` or `null`, `matches`, `checked`) |
|
|
98
100
|
| `answer(params)` | `POST /v1/answer` | `AnswerParams` (`q`, `depth`: `fast`\|`standard`, `num_sources` 1-5, `output_schema`, search options) | `AnswerResponse` (`answer`, `citations`, `results`, `output`) |
|
|
99
101
|
| `answerStream(params)` | `POST /v1/answer` (`stream: true`) | `AnswerParams` | `AsyncIterable<AnswerStreamEvent>` (`results`, `delta`…, `done`) |
|
|
@@ -247,6 +249,42 @@ console.log(news.answer?.text, news.meta.warnings); // no answer + a warning (re
|
|
|
247
249
|
[serpkite.com/pricing](https://serpkite.com/pricing); `meta.credits` on an answer splits the
|
|
248
250
|
search and the model part.
|
|
249
251
|
|
|
252
|
+
## AI assistants
|
|
253
|
+
|
|
254
|
+
Ask ChatGPT, Gemini, Copilot (Bing Copilot Search), Google AI Mode or Brave's AI answer a `prompt` as a
|
|
255
|
+
logged-out user and get the answer with the sources it cited, the searches it ran and the products
|
|
256
|
+
and places it showed. `track` lists brands and domains to look for: each comes back in `mentions`.
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
const res = await sk.chatgpt({
|
|
260
|
+
prompt: "What are the best CRMs for a 10-person startup?",
|
|
261
|
+
country: "us", // ask from an exit in this country (prices and shops follow it)
|
|
262
|
+
track: ["hubspot.com", "Pipedrive"],
|
|
263
|
+
});
|
|
264
|
+
console.log(res.model, res.markdown); // citations as [n](link), matching sources[].position
|
|
265
|
+
for (const s of res.sources) console.log(s.position, s.cited ? "cited" : "listed", s.domain);
|
|
266
|
+
console.log(res.search_queries, res.products.map((p) => `${p.title} ${p.price}`));
|
|
267
|
+
for (const m of res.mentions ?? []) {
|
|
268
|
+
// mentioned: the text names it; cited: a cited source matches it; text_position 0-1 (-1: not named)
|
|
269
|
+
console.log(m.term, m.mentioned, m.cited, m.sources, m.text_position);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const md = await sk.aiMode({ prompt: "how to remove a red wine stain from wool", format: "markdown" }); // string
|
|
273
|
+
await sk.gemini({ prompt: "Wie funktionieren Wärmepumpen?", country: "de", language: "de" });
|
|
274
|
+
|
|
275
|
+
// Google's AI Overview on a normal search, when the page shows one
|
|
276
|
+
const serp = await sk.search({ q: "how do heat pumps work" });
|
|
277
|
+
console.log(serp.ai_overview?.text, serp.ai_overview?.sources);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- Prices per answer: ChatGPT 5, Gemini 5, Copilot 3, AI Mode 3, Brave AI 2 credits (the same in
|
|
281
|
+
batches; `max_age` cache hits half). Failed, refused and empty answers are free.
|
|
282
|
+
- Answers take 10-90 s: these methods wait at least `ASSISTANT_TIMEOUT_MS` (120 s) per attempt
|
|
283
|
+
unless you pass `timeoutMs`, and are retried only on `429` (a lost response is still billed).
|
|
284
|
+
- `web_search` (default `true`) is ChatGPT only; `language` isn't taken by ChatGPT.
|
|
285
|
+
- Run many prompts through `batches.create({ endpoint: "chatgpt", requests: [{ prompt }, …] })`, or
|
|
286
|
+
track an answer over time with a monitor (`endpoint: "gemini"`, `prompt`, `track`; see Monitors).
|
|
287
|
+
|
|
250
288
|
## Research
|
|
251
289
|
|
|
252
290
|
```ts
|
|
@@ -335,6 +373,21 @@ per check) and reports it as a `MonitorPageChange` (`url`, `title`, `change: "ne
|
|
|
335
373
|
stored on any monitor and echoed in its `monitor.results` webhooks; on `update`, an object replaces it
|
|
336
374
|
and `null` clears it.
|
|
337
375
|
|
|
376
|
+
An AI assistant monitor (`endpoint`: `chatgpt`, `gemini`, `copilot`, `ai_mode` or `brave_ai`) asks
|
|
377
|
+
`prompt` every run and reports every answer as one `MonitorAnswer` (`text`, `markdown`, `model`,
|
|
378
|
+
`sources`, `new_sources` (links not seen in earlier runs), `search_queries`, `products` and, with
|
|
379
|
+
`track`, `mentions`); its `monitor.results` webhook carries `prompt` instead of `q`. Each run costs
|
|
380
|
+
the endpoint's price.
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
await sk.monitors.create({
|
|
384
|
+
endpoint: "chatgpt",
|
|
385
|
+
prompt: "What are the best CRMs for a 10-person startup?",
|
|
386
|
+
track: ["hubspot.com", "Pipedrive"],
|
|
387
|
+
interval: "daily",
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
338
391
|
```ts
|
|
339
392
|
const watch = await sk.monitors.create({
|
|
340
393
|
endpoint: "webpage",
|
|
@@ -377,7 +430,7 @@ import { isBatchError, SerpKite } from "serpkite";
|
|
|
377
430
|
|
|
378
431
|
const sk = new SerpKite();
|
|
379
432
|
const { batches } = await sk.batches.create({
|
|
380
|
-
endpoint: "search", // or images, news, maps, reviews, webpage, …
|
|
433
|
+
endpoint: "search", // or images, news, maps, reviews, webpage, chatgpt, gemini, …
|
|
381
434
|
requests: [{ q: "a" }, { q: "b" }],
|
|
382
435
|
webhook_url: "https://example.com/hooks/serpkite", // optional
|
|
383
436
|
});
|
|
@@ -442,8 +495,8 @@ Failed, empty and blocked searches are refunded, so they never cost credits.
|
|
|
442
495
|
Requests are retried up to `maxRetries` times (default 2) on `429`, `5xx`, network errors and
|
|
443
496
|
timeouts, with exponential backoff (`retryDelayMs · 2^attempt`, capped at 8 s) and jitter. A
|
|
444
497
|
`Retry-After` header (seconds or HTTP date, capped at 60 s) takes precedence. Other `4xx` errors
|
|
445
|
-
are never retried. `batches.create`, `crawl`, `research`, `extract`, `answer`,
|
|
446
|
-
and `monitors.run` retry only on `429`, so a lost response can never queue (and bill) the same
|
|
498
|
+
are never retried. `batches.create`, `crawl`, `research`, `extract`, `answer`, the AI assistant
|
|
499
|
+
methods, `monitors.create` and `monitors.run` retry only on `429`, so a lost response can never queue (and bill) the same
|
|
447
500
|
work twice. `answerStream` retries only until the stream starts. `answer` and `answerStream` wait
|
|
448
501
|
at least 100 s for the response headers (the search runs first) unless you pass `timeoutMs`.
|
|
449
502
|
|
|
@@ -458,7 +511,8 @@ Types are generated from the OpenAPI contract (`backend/api/serp-api.yaml`) with
|
|
|
458
511
|
`Account`, `Batch`, `BatchCreateParams`, `Meta`, `Engine`, `Provider`, `RouteStep`,
|
|
459
512
|
`CompactResponse`, `MapResponse`, `ExtractResponse`, `CrawlParams`, `CrawlTask`, `CrawlResult`,
|
|
460
513
|
`TaskCreated`, `AnswerParams`, `AnswerResponse`, `AnswerStreamEvent`, `Citation`, `SearchAnswer`,
|
|
461
|
-
`LLMUsage`, `
|
|
514
|
+
`LLMUsage`, `AssistantParams`, `ChatGPTParams`, `AssistantResponse`, `AISource`, `AIProduct`,
|
|
515
|
+
`Mention`, `AIOverview`, `MonitorAnswer`, `ResearchParams`, `ResearchTask`, `ResearchResult`, `ResearchSource`,
|
|
462
516
|
`Monitor`, `MonitorRun`, `MonitorPageChange`, `TaskCompletedEvent`,
|
|
463
517
|
`MonitorResultsEvent`, `WebhookEvent` and more. The raw
|
|
464
518
|
`paths`, `components` and `operations` types are exported as well.
|
package/dist/index.cjs
CHANGED
|
@@ -50,8 +50,9 @@ function errorFromResponse(status, headers, body) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// src/client.ts
|
|
53
|
-
var VERSION = "0.
|
|
53
|
+
var VERSION = "0.8.0";
|
|
54
54
|
var DEFAULT_BASE_URL = "https://api.serpkite.com";
|
|
55
|
+
var ASSISTANT_TIMEOUT_MS = 12e4;
|
|
55
56
|
var TASK_DONE = /* @__PURE__ */ new Set(["completed", "failed", "canceled"]);
|
|
56
57
|
var SerpKite = class {
|
|
57
58
|
baseUrl;
|
|
@@ -124,6 +125,24 @@ var SerpKite = class {
|
|
|
124
125
|
autocomplete = this.#vertical("/v1/autocomplete");
|
|
125
126
|
/** Fetch any public URL as clean Markdown plus metadata. */
|
|
126
127
|
webpage = this.#vertical("/v1/webpage");
|
|
128
|
+
// ── AI assistants ─────────────────────────────────────────────────────
|
|
129
|
+
// Billed even when the response is lost: retried only on 429 (failed, refused and empty
|
|
130
|
+
// answers are free), and each attempt waits at least 120 s (answers take 10-90 s).
|
|
131
|
+
/**
|
|
132
|
+
* Ask ChatGPT as a logged-out user: the answer (`text`, `markdown`), cited `sources`,
|
|
133
|
+
* `search_queries`, `products`, `places` and the guest `model`. `web_search` (default true)
|
|
134
|
+
* makes it search the web; `country` asks from an exit in that country. `track` returns
|
|
135
|
+
* `mentions` of your brands and domains. 5 credits.
|
|
136
|
+
*/
|
|
137
|
+
chatgpt = this.#assistant("/v1/chatgpt");
|
|
138
|
+
/** Ask Google Gemini as a logged-out user: the answer with the sources it grounded on. 5 credits. */
|
|
139
|
+
gemini = this.#assistant("/v1/gemini");
|
|
140
|
+
/** Microsoft Copilot Search on Bing: the answer, cited sources and products. 3 credits. */
|
|
141
|
+
copilot = this.#assistant("/v1/copilot");
|
|
142
|
+
/** Google AI Mode: the answer, cited sources and products. 3 credits. */
|
|
143
|
+
aiMode = this.#assistant("/v1/ai_mode");
|
|
144
|
+
/** Brave Search's AI answer: cited sources, the searches it ran and products. 2 credits. */
|
|
145
|
+
braveAi = this.#assistant("/v1/brave_ai");
|
|
127
146
|
/** Position of `domain` for keyword `q` in the top `num` results (default 100). */
|
|
128
147
|
rank(params, options) {
|
|
129
148
|
return this.request({ method: "POST", path: "/v1/rank", body: params, options });
|
|
@@ -308,7 +327,9 @@ var SerpKite = class {
|
|
|
308
327
|
* and reports it (`MonitorPageChange`) when its content changes. `metadata` (your JSON
|
|
309
328
|
* object, at most 2 KB) is echoed on the monitor and in its webhooks. Without a
|
|
310
329
|
* `webhook_url`, read new results from `monitors.runs`. `active: false`
|
|
311
|
-
* creates it paused.
|
|
330
|
+
* creates it paused. With an AI assistant `endpoint` (`chatgpt`, `gemini`, `copilot`,
|
|
331
|
+
* `ai_mode`, `brave_ai`) and a `prompt` every run reports the answer (`MonitorAnswer`:
|
|
332
|
+
* sources, `new_sources`, `mentions` with `track`). A monitor pauses itself after 10 failed runs in a row
|
|
312
333
|
* (`last_status: "paused"`); `update(id, { active: true })` resumes it. Each run costs
|
|
313
334
|
* what its search costs. Only retried on 429.
|
|
314
335
|
*/
|
|
@@ -431,6 +452,18 @@ var SerpKite = class {
|
|
|
431
452
|
#vertical(path) {
|
|
432
453
|
return ((params, options) => this.request({ method: "POST", path, body: params, options }));
|
|
433
454
|
}
|
|
455
|
+
#assistant(path) {
|
|
456
|
+
return ((params, options) => this.request({
|
|
457
|
+
method: "POST",
|
|
458
|
+
path,
|
|
459
|
+
body: params,
|
|
460
|
+
options: {
|
|
461
|
+
...options,
|
|
462
|
+
timeoutMs: options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, ASSISTANT_TIMEOUT_MS) : 0)
|
|
463
|
+
},
|
|
464
|
+
retry: "rate_limit"
|
|
465
|
+
}));
|
|
466
|
+
}
|
|
434
467
|
async #wait(idOrEntry, o) {
|
|
435
468
|
let id;
|
|
436
469
|
if (typeof idOrEntry === "string") {
|
|
@@ -895,6 +928,7 @@ async function parseWebhook(secret, payload, headers, options = {}) {
|
|
|
895
928
|
return { type, deliveryId: header(headers, "x-serpkite-delivery"), data };
|
|
896
929
|
}
|
|
897
930
|
|
|
931
|
+
exports.ASSISTANT_TIMEOUT_MS = ASSISTANT_TIMEOUT_MS;
|
|
898
932
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
899
933
|
exports.SerpKite = SerpKite;
|
|
900
934
|
exports.SerpKiteError = SerpKiteError;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/util.ts","../src/webhooks.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Every failure thrown by the SDK. API errors carry the fields of the\n * `{\"error\":{\"code\",\"message\",\"request_id\"}}` envelope; network failures and\n * timeouts use `status: 0` with code `connection_error` or `timeout`.\n */\nexport class SerpKiteError extends Error {\n /** HTTP status, or 0 when no response was received. */\n readonly status: number;\n /** Machine-readable code, e.g. `unauthorized`, `insufficient_credits`, `rate_limited`. */\n readonly code: string;\n /** `X-Request-Id` of the failed request, when the API returned one. */\n readonly requestId: string | undefined;\n /** Response headers, when the API responded. */\n readonly headers: Headers | undefined;\n\n constructor(\n status: number,\n code: string,\n message: string,\n requestId?: string,\n headers?: Headers,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = \"SerpKiteError\";\n this.status = status;\n this.code = code;\n this.requestId = requestId || undefined;\n this.headers = headers;\n }\n}\n\nconst fallbackCodes: Record<number, string> = {\n 400: \"invalid_request\",\n 401: \"unauthorized\",\n 402: \"insufficient_credits\",\n 403: \"forbidden\",\n 404: \"not_found\",\n 429: \"rate_limited\",\n // The API sends upstream failures as 503 with a JSON body; a bare 502/504\n // comes from a proxy (Cloudflare replaces origin 502/504 bodies).\n 502: \"upstream_error\",\n 503: \"unavailable\",\n 504: \"upstream_timeout\",\n};\n\n/** Builds a SerpKiteError from a non-2xx response body (JSON envelope or anything else). */\nexport function errorFromResponse(status: number, headers: Headers, body: string): SerpKiteError {\n let code = fallbackCodes[status] ?? (status >= 500 ? \"server_error\" : \"http_error\");\n let message = body.trim().slice(0, 500) || `HTTP ${status}`;\n let requestId = headers.get(\"x-request-id\") ?? undefined;\n try {\n const parsed = JSON.parse(body) as {\n error?: { code?: unknown; message?: unknown; request_id?: unknown };\n };\n const e = parsed?.error;\n if (e && typeof e === \"object\") {\n if (typeof e.code === \"string\" && e.code) code = e.code;\n if (typeof e.message === \"string\" && e.message) message = e.message;\n if (typeof e.request_id === \"string\" && e.request_id) requestId = e.request_id;\n }\n } catch {\n // Not JSON: keep the text body as the message.\n }\n return new SerpKiteError(status, code, message, requestId, headers);\n}\n","import { errorFromResponse, SerpKiteError } from \"./errors\";\nimport type {\n Account,\n AnswerParams,\n AnswerResponse,\n AnswerStreamEvent,\n AutocompleteResponse,\n Batch,\n BatchCreateEntry,\n BatchCreateParams,\n BatchCreateResponse,\n CompactResponse,\n CrawlParams,\n CrawlTask,\n ExtractParams,\n ExtractResponse,\n ImagesResponse,\n MapParams,\n MapResponse,\n Monitor,\n MonitorCreateParams,\n MonitorList,\n MonitorRun,\n MonitorRunList,\n MonitorRunsParams,\n MonitorUpdateParams,\n NewsResponse,\n PatentsResponse,\n PlaceParams,\n PlacesResponse,\n RankParams,\n RankResponse,\n ResearchParams,\n ResearchTask,\n ReviewsParams,\n ReviewsResponse,\n ScholarResponse,\n SearchParams,\n SearchResponse,\n ShoppingResponse,\n TaskCancelResponse,\n TaskCreated,\n VideosResponse,\n WebpageParams,\n WebpageResponse,\n} from \"./types\";\n\nexport const VERSION = \"0.7.0\";\nexport const DEFAULT_BASE_URL = \"https://api.serpkite.com\";\n\nexport interface SerpKiteOptions {\n /** API key (`skt_live_…`). Defaults to the `SERPKITE_API_KEY` environment variable. */\n apiKey?: string;\n /** Defaults to `SERPKITE_BASE_URL` or https://api.serpkite.com. */\n baseUrl?: string;\n /** Per-attempt timeout in milliseconds. Default 60000. */\n timeoutMs?: number;\n /** Retries after the first attempt on 429, 5xx and network errors. Default 2. */\n maxRetries?: number;\n /** Base delay of the exponential backoff in milliseconds. Default 500. */\n retryDelayMs?: number;\n /** Custom fetch implementation. Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n}\n\n/** Billing and tracing details from the response headers. */\nexport interface ResponseInfo {\n status: number;\n headers: Headers;\n requestId: string | undefined;\n creditsUsed: number | undefined;\n creditsRemaining: number | undefined;\n costUsd: number | undefined;\n /** `HIT` when served from cache (`max_age`), else `MISS`. */\n cache: \"HIT\" | \"MISS\" | undefined;\n latencyMs: number | undefined;\n tokensEstimate: number | undefined;\n}\n\nexport interface RequestOptions {\n signal?: AbortSignal;\n /** Overrides the client's per-attempt timeout. */\n timeoutMs?: number;\n /** Overrides the client's retry count. */\n maxRetries?: number;\n headers?: Record<string, string>;\n /** Called with the final response's headers (credits used/remaining, cache, request id). */\n onResponse?: (info: ResponseInfo) => void;\n}\n\nexport interface BatchCreateOptions extends RequestOptions {\n /**\n * Sent as `Idempotency-Key` (1-255 printable ASCII characters, e.g. a UUID).\n * For 24 hours a retry with the same key and body returns the first response\n * instead of queueing and billing the jobs again.\n */\n idempotencyKey?: string;\n}\n\nexport interface WaitOptions {\n /**\n * Give up after this many milliseconds. Default 600000 (10 minutes) for batches,\n * 2100000 (35 minutes) for `waitForCrawl` (a crawl runs for up to 30 minutes) and\n * 900000 (15 minutes) for `waitResearch`.\n */\n timeoutMs?: number;\n /** First delay between polls. Default 1000. Grows 1.5× per poll. */\n pollIntervalMs?: number;\n /** Upper bound of the delay between polls. Default 10000. */\n maxPollIntervalMs?: number;\n signal?: AbortSignal;\n}\n\n/** A vertical whose return type follows `format`: `\"markdown\"` → string, `\"compact\"` → CompactResponse. */\nexport interface VerticalMethod<P, R> {\n (params: P & { format: \"markdown\" }, options?: RequestOptions): Promise<string>;\n (params: P & { format: \"compact\" }, options?: RequestOptions): Promise<CompactResponse>;\n (params: P & { format?: \"json\" }, options?: RequestOptions): Promise<R>;\n (params: P, options?: RequestOptions): Promise<R | CompactResponse | string>;\n}\n\ntype Method = \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n\n/** Task states after which `waitForCrawl` and `waitResearch` return. */\nconst TASK_DONE = new Set([\"completed\", \"failed\", \"canceled\"]);\n\ninterface Call {\n method: Method;\n path: string;\n body?: unknown;\n options?: RequestOptions;\n /** Which failures are safe to retry. `all` = 429, 5xx and network errors. */\n retry?: \"all\" | \"rate_limit\";\n}\n\nexport class SerpKite {\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly maxRetries: number;\n readonly retryDelayMs: number;\n readonly #apiKey: string;\n readonly #fetch: typeof fetch;\n readonly #headers: Record<string, string>;\n\n constructor(options: SerpKiteOptions = {}) {\n const apiKey = options.apiKey ?? env(\"SERPKITE_API_KEY\");\n if (!apiKey) {\n throw new SerpKiteError(\n 0,\n \"missing_api_key\",\n \"No API key: pass { apiKey } or set the SERPKITE_API_KEY environment variable\",\n );\n }\n this.#apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? env(\"SERPKITE_BASE_URL\") ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n \"\",\n );\n this.timeoutMs = options.timeoutMs ?? 60_000;\n this.maxRetries = Math.max(0, options.maxRetries ?? 2);\n this.retryDelayMs = options.retryDelayMs ?? 500;\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new SerpKiteError(\n 0,\n \"missing_fetch\",\n \"No global fetch: pass { fetch } (Node 18+ has one)\",\n );\n }\n this.#fetch = f;\n this.#headers = { ...options.headers };\n }\n\n // ── Verticals ─────────────────────────────────────────────────────────\n\n /** Google web search: organic results, answer box, knowledge graph, people also ask… */\n readonly search = this.#vertical<SearchParams, SearchResponse>(\"/v1/search\");\n /** Google Images. */\n readonly images = this.#vertical<SearchParams, ImagesResponse>(\"/v1/images\");\n /** Google Videos. */\n readonly videos = this.#vertical<SearchParams, VideosResponse>(\"/v1/videos\");\n /** Google News. */\n readonly news = this.#vertical<SearchParams, NewsResponse>(\"/v1/news\");\n /**\n * Google Maps search (places with coordinates). Accepts `ll` (\"@lat,lng,14z\"). With\n * `engine: \"yandex\"` (where enabled) the places come from Yandex Maps and carry an `oid`.\n */\n readonly maps = this.#vertical<SearchParams, PlacesResponse>(\"/v1/maps\");\n /** Google local results. */\n readonly places = this.#vertical<SearchParams, PlacesResponse>(\"/v1/places\");\n /**\n * Details of one place by `place_id`, `cid` or `fid`, or by a Yandex Maps `oid` (implies\n * `engine: \"yandex\"`): address, country, time zone, hours\n * (`opening_hours` text and structured `weekly_hours`), phones, description and photos.\n * `results` holds the place, or is empty (not charged) when no place has that id. 1 credit.\n */\n readonly place = this.#vertical<PlaceParams, PlacesResponse>(\"/v1/place\");\n /** Reviews of a place by `place_id`, `cid` or `fid`. Page with `page_token` = `next_page_token`. */\n readonly reviews = this.#vertical<ReviewsParams, ReviewsResponse>(\"/v1/reviews\");\n /** Google Shopping. */\n readonly shopping = this.#vertical<SearchParams, ShoppingResponse>(\"/v1/shopping\");\n /** Google Scholar. */\n readonly scholar = this.#vertical<SearchParams, ScholarResponse>(\"/v1/scholar\");\n /** Google Patents. */\n readonly patents = this.#vertical<SearchParams, PatentsResponse>(\"/v1/patents\");\n /** Google autocomplete suggestions. */\n readonly autocomplete = this.#vertical<SearchParams, AutocompleteResponse>(\"/v1/autocomplete\");\n /** Fetch any public URL as clean Markdown plus metadata. */\n readonly webpage = this.#vertical<WebpageParams, WebpageResponse>(\"/v1/webpage\");\n\n /** Position of `domain` for keyword `q` in the top `num` results (default 100). */\n rank(params: RankParams, options?: RequestOptions): Promise<RankResponse> {\n return this.request({ method: \"POST\", path: \"/v1/rank\", body: params, options });\n }\n\n /**\n * Read up to 20 URLs (HTML or PDF) as Markdown, text or HTML in one call, with optional\n * query-ranked `highlights` (BM25), `include_links` and `include_images`. 1 credit per URL\n * that comes back (0.5 from cache); URLs that fail are listed in `failed` and cost nothing.\n * `summary: true` adds an AI summary of each page and `output_schema` (a JSON Schema, guided\n * by an optional `prompt`) returns each page's data as JSON in `output`, each at an extra\n * per-page price (https://serpkite.com/pricing); a page whose model step failed has\n * `summary_error` / `output_error` instead and isn't charged for it.\n */\n extract(params: ExtractParams, options?: RequestOptions): Promise<ExtractResponse> {\n // Billed per page even when the response is lost: retried only on 429, and the HTTP\n // timeout covers the server's deadline (timeout: default 50 s, 75 s with AI) + 15 s.\n const ai = params.summary === true || params.output_schema !== undefined;\n const wait = ((params.timeout ?? (ai ? 75 : 50)) + 15) * 1000;\n return this.request({\n method: \"POST\",\n path: \"/v1/extract\",\n body: params,\n options: {\n ...options,\n timeoutMs: options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, wait) : 0),\n },\n retry: \"rate_limit\",\n });\n }\n\n /**\n * The URLs of a site from its sitemaps (XML, RSS/Atom, gzip, plain text) and start page,\n * canonicalised and deduplicated, optionally ranked by `search`. `include_paths` /\n * `exclude_paths` (regular expressions on the URL path) and `ignore_query_parameters`\n * narrow the list. 1 credit; free when nothing is found.\n */\n map(params: MapParams, options?: RequestOptions): Promise<MapResponse> {\n return this.request({ method: \"POST\", path: \"/v1/map\", body: params, options });\n }\n\n /** Balance, limits and this month's usage for the key's account. */\n account(options?: RequestOptions): Promise<Account> {\n return this.request({ method: \"GET\", path: \"/v1/account\", options });\n }\n\n // ── AI answers ────────────────────────────────────────────────────────\n\n /**\n * A cited answer grounded on live Google results: Markdown with `[n]` markers in `answer`,\n * the cited sources in `citations`. `depth: \"fast\"` answers from the result page;\n * `\"standard\"` (default) also reads the top `num_sources` pages and costs more\n * (https://serpkite.com/pricing; `meta.credits` splits search and model). With\n * `output_schema` the answer is a JSON value in `output`.\n * When the model fails the call throws `ai_unavailable` (503) and the model part is refunded;\n * a search with nothing to answer from returns `answer: null` and a `meta.warnings` entry.\n */\n answer(params: AnswerParams, options?: RequestOptions): Promise<AnswerResponse> {\n // Billed even when the response is lost (and a 503 ai_unavailable still charges the\n // search part): retried only on 429, and the HTTP timeout covers a search plus the model.\n return this.request({\n method: \"POST\",\n path: \"/v1/answer\",\n body: params,\n options: { ...options, timeoutMs: this.#answerTimeout(options) },\n retry: \"rate_limit\",\n });\n }\n\n /**\n * Streams an answer as Server-Sent Events: `results` first, then `delta` text pieces,\n * then `done` (final answer, citations, meta). A server `error` event is thrown as a\n * `SerpKiteError` (`ai_unavailable`). Breaking out of the loop closes the connection\n * (the model part is then not charged). Can't be combined with `output_schema`.\n *\n * ```ts\n * for await (const ev of sk.answerStream({ q: \"what is retrieval-augmented generation\" })) {\n * if (ev.event === \"delta\") process.stdout.write(ev.data.text);\n * }\n * ```\n */\n answerStream(params: AnswerParams, options?: RequestOptions): AsyncIterable<AnswerStreamEvent> {\n return this.#stream(\n { ...params, stream: true },\n { ...options, timeoutMs: this.#answerTimeout(options) },\n );\n }\n\n /** The search (pages read included) runs before the first byte: wait at least 100 s. */\n #answerTimeout(options?: RequestOptions): number {\n return options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, 100_000) : 0);\n }\n\n // ── Async tasks: crawl ────────────────────────────────────────────────\n\n /**\n * Starts an async crawl of one site and returns the queued task (`202`). 1 credit per page\n * read (0.5 from cache); `limit` credits (up to 1000 pages, `max_depth` up to 10) are\n * reserved up front and the rest refunded. `sitemap` (`include` default, `only`, `skip`)\n * seeds the crawl with sitemap URLs; `query` reads the most relevant pages first. robots.txt\n * and Crawl-delay are honoured; `result.stats.stopped` says why the crawl ended (`done`,\n * `limit`, `time_limit`, `size_limit`, `too_many_failures`, `canceled`).\n * Only retried on 429, so a lost response never starts (and reserves) a second crawl.\n */\n crawl(params: CrawlParams, options?: RequestOptions): Promise<TaskCreated> {\n return this.request({\n method: \"POST\",\n path: \"/v1/crawl\",\n body: params,\n options,\n retry: \"rate_limit\",\n });\n }\n\n /** Polls a crawl (results are kept for 24 hours). */\n getCrawl(id: string, options?: RequestOptions): Promise<CrawlTask> {\n return this.request({ method: \"GET\", path: `/v1/crawl/${encodeURIComponent(id)}`, options });\n }\n\n /** Cancels a crawl: refunded if still queued, else stops at the next checkpoint. */\n cancelCrawl(id: string, options?: RequestOptions): Promise<TaskCancelResponse> {\n return this.request({\n method: \"DELETE\",\n path: `/v1/crawl/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Polls until the crawl is `completed`, `failed` or `canceled` and returns it (a failed\n * task is returned, not thrown; check `status` and `error`). Accepts an id or the task\n * returned by `crawl`.\n */\n async waitForCrawl(\n idOrTask: string | { id: string },\n options: WaitOptions = {},\n ): Promise<CrawlTask> {\n return this.#waitTask(taskId(idOrTask), (id) => this.getCrawl(id, { signal: options.signal }), {\n ...options,\n timeoutMs: options.timeoutMs ?? 2_100_000,\n });\n }\n\n // ── Async tasks: research ─────────────────────────────────────────────\n\n /**\n * Starts an async research task: the model plans searches, reads the sources and writes a\n * cited Markdown report (`result.report`). `effort` (`lite`, `standard` (default), `deep`)\n * sets how much is searched and read; its flat price (https://serpkite.com/pricing) is\n * reserved up front and charged only when the report is written (a canceled task is charged\n * for the searches already run). With\n * `output_schema` the report is also returned as JSON in `result.output`. Poll with\n * `getResearch` / `waitResearch` or receive the signed `research.completed` webhook.\n * Only retried on 429, so a lost response never starts (and reserves) a second task.\n */\n research(params: ResearchParams, options?: RequestOptions): Promise<TaskCreated> {\n return this.request({\n method: \"POST\",\n path: \"/v1/research\",\n body: params,\n options,\n retry: \"rate_limit\",\n });\n }\n\n /** Polls a research task (`progress` while it runs; results are kept for 24 hours). */\n getResearch(id: string, options?: RequestOptions): Promise<ResearchTask> {\n return this.request({\n method: \"GET\",\n path: `/v1/research/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Cancels a research task: refunded if still queued, else stops at its next round and is\n * charged for the searches run.\n */\n cancelResearch(id: string, options?: RequestOptions): Promise<TaskCancelResponse> {\n return this.request({\n method: \"DELETE\",\n path: `/v1/research/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Polls until the research task is `completed`, `failed` or `canceled` and returns it (a\n * failed task is returned, not thrown; check `status` and `error`). The report is in\n * `result.report`. Accepts an id or the task returned by `research`. Default timeout\n * 15 minutes.\n */\n async waitResearch(\n idOrTask: string | { id: string },\n options: WaitOptions = {},\n ): Promise<ResearchTask> {\n return this.#waitTask(\n taskId(idOrTask),\n (id) => this.getResearch(id, { signal: options.signal }),\n { ...options, timeoutMs: options.timeoutMs ?? 900_000 },\n );\n }\n\n // ── Monitors ──────────────────────────────────────────────────────────\n\n readonly monitors = {\n /**\n * Saves a `search` or `news` request (`q`) that runs on a schedule (hourly to monthly) and\n * POSTs results it hasn't seen before to `webhook_url` (`monitor.results`). With\n * `endpoint: \"webpage\"` and a `url` it checks that page each interval (1 credit per check)\n * and reports it (`MonitorPageChange`) when its content changes. `metadata` (your JSON\n * object, at most 2 KB) is echoed on the monitor and in its webhooks. Without a\n * `webhook_url`, read new results from `monitors.runs`. `active: false`\n * creates it paused. A monitor pauses itself after 10 failed runs in a row\n * (`last_status: \"paused\"`); `update(id, { active: true })` resumes it. Each run costs\n * what its search costs. Only retried on 429.\n */\n create: (params: MonitorCreateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: \"/v1/monitors\",\n body: params,\n options,\n retry: \"rate_limit\",\n }),\n\n /** The account's monitors. */\n list: (options?: RequestOptions): Promise<MonitorList> =>\n this.request({ method: \"GET\", path: \"/v1/monitors\", options }),\n\n get: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({ method: \"GET\", path: `/v1/monitors/${encodeURIComponent(id)}`, options }),\n\n /**\n * Changes `name`, `interval` / `interval_seconds`, `webhook_url` (`\"\"` removes it),\n * `active` (`true` resumes a paused monitor), `metadata` (an object replaces it, `null`\n * clears it) or the saved request (`endpoint`, `q`, `url`, `country`, `num`, domains,\n * `engine`…). Switching `endpoint` between a search and `webpage` drops the other kind's\n * fields. A changed search reports every result as new on the next run.\n */\n update: (id: string, params: MonitorUpdateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"PATCH\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n body: params,\n options,\n }),\n\n delete: async (id: string, options?: RequestOptions): Promise<void> => {\n await this.request({\n method: \"DELETE\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n options,\n });\n },\n\n /** Runs an active monitor at the next scheduler poll (within about 30 seconds).\n * Paused monitors return 409; update(id, { active: true }) resumes and schedules them. */\n run: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: `/v1/monitors/${encodeURIComponent(id)}/run`,\n options,\n retry: \"rate_limit\",\n }),\n\n /**\n * One page of the monitor's run history, newest first (`limit` 1-100, default 20). Each\n * run carries its new results for 24 hours. Pass `next_before` as `before` for the next\n * page; it is null on the last one.\n */\n runs: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): Promise<MonitorRunList> =>\n this.request({\n method: \"GET\",\n path: `/v1/monitors/${encodeURIComponent(id)}/runs${query(params)}`,\n options,\n }),\n\n /**\n * Iterates over every run of the monitor, newest first, fetching pages of `limit` as\n * needed (starting at `before` when given).\n *\n * ```ts\n * for await (const run of sk.monitors.iterRuns(\"m1\")) console.log(run.new_results);\n * ```\n */\n iterRuns: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): AsyncIterable<MonitorRun> => this.#iterRuns(id, params, options),\n };\n\n // ── Batches ───────────────────────────────────────────────────────────\n\n readonly batches = {\n /**\n * Queue 1-100 requests for one endpoint at half price. Returns one entry per\n * request, in order: a queued `Batch` or an error object. Without an\n * `idempotencyKey` it is only retried on 429, so a lost response never queues\n * (and bills) the same requests twice. With one, 5xx and network errors are\n * retried too: the server replays the first response for the same key and body.\n */\n create: (\n params: BatchCreateParams,\n options: BatchCreateOptions = {},\n ): Promise<BatchCreateResponse> => {\n const { idempotencyKey, ...rest } = options;\n return this.request({\n method: \"POST\",\n path: \"/v1/batches\",\n body: params,\n options: idempotencyKey\n ? { ...rest, headers: { ...rest.headers, \"Idempotency-Key\": idempotencyKey } }\n : rest,\n retry: idempotencyKey ? \"all\" : \"rate_limit\",\n });\n },\n\n /** Poll one batch job. */\n get: (id: string, options?: RequestOptions): Promise<Batch> =>\n this.request({ method: \"GET\", path: `/v1/batches/${encodeURIComponent(id)}`, options }),\n\n /**\n * Poll until the job is `done` or `failed` and return it (a failed job is\n * returned, not thrown; check `status` and `error`). Accepts an id or an\n * entry from `create`; an entry that failed to queue throws its error.\n */\n wait: (idOrEntry: string | BatchCreateEntry | undefined, options: WaitOptions = {}) =>\n this.#wait(idOrEntry, options),\n };\n\n // ── Plumbing ──────────────────────────────────────────────────────────\n\n async *#iterRuns(\n id: string,\n params: MonitorRunsParams,\n options?: RequestOptions,\n ): AsyncGenerator<MonitorRun> {\n let before = params.before;\n for (;;) {\n const page = await this.monitors.runs(id, { ...params, before }, options);\n yield* page.results;\n if (!page.next_before || page.results.length === 0) return;\n before = page.next_before;\n }\n }\n\n async #waitTask<T extends { status: string }>(\n id: string,\n get: (id: string) => Promise<T>,\n o: WaitOptions,\n ): Promise<T> {\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const task = await get(id);\n if (TASK_DONE.has(task.status)) return task;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `task ${id} still ${task.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n #vertical<P, R>(path: string): VerticalMethod<P, R> {\n return ((params: P, options?: RequestOptions) =>\n this.request({ method: \"POST\", path, body: params, options })) as VerticalMethod<P, R>;\n }\n\n async #wait(idOrEntry: string | BatchCreateEntry | undefined, o: WaitOptions): Promise<Batch> {\n let id: string | undefined;\n if (typeof idOrEntry === \"string\") {\n id = idOrEntry;\n } else if (idOrEntry && \"id\" in idOrEntry && typeof idOrEntry.id === \"string\") {\n id = idOrEntry.id;\n } else if (idOrEntry && \"error\" in idOrEntry && idOrEntry.error && !(\"id\" in idOrEntry)) {\n const e = idOrEntry.error as { code?: string; message?: string; request_id?: string };\n throw new SerpKiteError(\n 400,\n e.code ?? \"invalid_request\",\n e.message ?? \"batch entry was not queued\",\n e.request_id,\n );\n }\n if (!id) {\n throw new SerpKiteError(0, \"invalid_request\", \"batches.wait needs a batch id\");\n }\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const batch = await this.batches.get(id, { signal: o.signal });\n if (batch.status === \"done\" || batch.status === \"failed\") return batch;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `batch ${id} still ${batch.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n /** Opens `POST /v1/answer` as an event stream (retrying the connect like `request`) and parses it. */\n async *#stream(body: unknown, options: RequestOptions = {}): AsyncGenerator<AnswerStreamEvent> {\n const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const headers: Record<string, string> = {\n Accept: \"text/event-stream, application/json;q=0.9\",\n Authorization: `Bearer ${this.#apiKey}`,\n ...this.#headers,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n if (!isBrowser()) headers[\"User-Agent\"] = `serpkite-typescript/${VERSION}`;\n let res: Response | undefined;\n let cleanup = () => {};\n for (let attempt = 0; !res; attempt++) {\n const canRetry = attempt < maxRetries;\n // The timeout covers connecting (until the headers arrive), not the whole stream.\n const t = withTimeout(options.signal, timeoutMs);\n let r: Response;\n try {\n r = await this.#fetch(`${this.baseUrl}/v1/answer`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: t.signal,\n });\n } catch (err) {\n t.cleanup();\n if (options.signal?.aborted) throw err;\n if (canRetry) {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const code = t.timedOut() ? \"timeout\" : \"connection_error\";\n const msg = t.timedOut()\n ? `request timed out after ${timeoutMs} ms`\n : `request failed: ${err instanceof Error ? err.message : String(err)}`;\n throw new SerpKiteError(0, code, msg, undefined, undefined, { cause: err });\n }\n if (!r.ok) {\n const text = await r.text().catch(() => \"\");\n t.cleanup();\n // Nothing is charged before the stream starts, so 5xx are safe to retry.\n if (canRetry && (r.status === 429 || r.status >= 500)) {\n await sleep(this.#retryDelay(attempt, r.headers), options.signal);\n continue;\n }\n throw errorFromResponse(r.status, r.headers, text);\n }\n // Stop the connect timer but keep forwarding the caller's abort signal.\n t.stopTimer();\n cleanup = t.cleanup;\n res = r;\n }\n options.onResponse?.(responseInfo(res));\n const requestId = res.headers.get(\"x-request-id\") ?? undefined;\n if (!res.body) {\n cleanup();\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response has no body\",\n requestId,\n res.headers,\n );\n }\n // A plain JSON answer (e.g. from a proxy that dropped the stream) becomes results + done.\n if ((res.headers.get(\"content-type\") ?? \"\").includes(\"json\")) {\n let answer: AnswerResponse;\n try {\n answer = JSON.parse(await res.text()) as AnswerResponse;\n } catch (err) {\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response is not valid JSON\",\n requestId,\n res.headers,\n { cause: err },\n );\n } finally {\n cleanup();\n }\n yield { event: \"results\", data: { request: answer.request, results: answer.results } };\n yield {\n event: \"done\",\n data: { answer: answer.answer ?? \"\", citations: answer.citations, meta: answer.meta },\n };\n return;\n }\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n let done = false;\n try {\n while (!done) {\n let chunk: Awaited<ReturnType<typeof reader.read>>;\n try {\n chunk = await reader.read();\n } catch (err) {\n if (options.signal?.aborted) throw err;\n throw new SerpKiteError(\n 0,\n \"connection_error\",\n `stream read failed: ${err instanceof Error ? err.message : String(err)}`,\n requestId,\n res.headers,\n { cause: err },\n );\n }\n if (chunk.done) {\n buf += decoder.decode();\n done = true;\n } else {\n buf += decoder.decode(chunk.value, { stream: true });\n }\n // A CR at the end of a chunk may be the first half of a CRLF split across chunks:\n // keep it until the next chunk shows what follows.\n const held = !done && buf.endsWith(\"\\r\") ? \"\\r\" : \"\";\n if (held) buf = buf.slice(0, -1);\n buf = buf.replace(/\\r\\n?/g, \"\\n\");\n let sep = buf.indexOf(\"\\n\\n\");\n while (sep >= 0 || (done && buf.trim() !== \"\")) {\n const raw = sep >= 0 ? buf.slice(0, sep) : buf;\n buf = sep >= 0 ? buf.slice(sep + 2) : \"\";\n const ev = parseSSE(raw);\n if (ev) {\n if (ev.event === \"error\") throw sseError(ev.data, res.headers, requestId);\n if (ev.event === \"results\" || ev.event === \"delta\" || ev.event === \"done\") {\n yield { event: ev.event, data: ev.data } as AnswerStreamEvent;\n if (ev.event === \"done\") return;\n }\n }\n sep = buf.indexOf(\"\\n\\n\");\n }\n buf += held;\n }\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"the stream ended before the done event\",\n requestId,\n res.headers,\n );\n } finally {\n cleanup();\n reader.cancel().catch(() => {});\n }\n }\n\n /** Low-level request: JSON body in, parsed JSON (or text for non-JSON responses) out. */\n async request<T>(call: Call): Promise<T> {\n const { method, path, body, options = {} } = call;\n const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const headers: Record<string, string> = {\n Accept: \"application/json, text/markdown;q=0.9\",\n Authorization: `Bearer ${this.#apiKey}`,\n ...this.#headers,\n ...options.headers,\n };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n // Browsers would turn a custom User-Agent into a CORS preflight header.\n if (!isBrowser()) headers[\"User-Agent\"] = `serpkite-typescript/${VERSION}`;\n const init: RequestInit = {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n };\n\n for (let attempt = 0; ; attempt++) {\n const canRetry = attempt < maxRetries;\n let res: Response;\n const { signal, cleanup, timedOut } = withTimeout(options.signal, timeoutMs);\n try {\n res = await this.#fetch(this.baseUrl + path, { ...init, signal });\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const msg = timedOut()\n ? `request timed out after ${timeoutMs} ms`\n : `request failed: ${err instanceof Error ? err.message : String(err)}`;\n throw new SerpKiteError(0, code, msg, undefined, undefined, { cause: err });\n }\n\n let text: string;\n try {\n text = await res.text();\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n throw new SerpKiteError(\n 0,\n code,\n \"failed to read the response body\",\n undefined,\n res.headers,\n {\n cause: err,\n },\n );\n }\n cleanup();\n\n if (!res.ok) {\n const retryable = res.status === 429 || (res.status >= 500 && call.retry !== \"rate_limit\");\n if (canRetry && retryable) {\n await sleep(this.#retryDelay(attempt, res.headers), options.signal);\n continue;\n }\n throw errorFromResponse(res.status, res.headers, text);\n }\n\n options.onResponse?.(responseInfo(res));\n const type = res.headers.get(\"content-type\") ?? \"\";\n if (type.includes(\"json\")) {\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response is not valid JSON\",\n res.headers.get(\"x-request-id\") ?? undefined,\n res.headers,\n { cause: err },\n );\n }\n }\n return text as T;\n }\n }\n\n #backoff(attempt: number): number {\n const base = Math.min(this.retryDelayMs * 2 ** attempt, 8_000);\n return base / 2 + Math.random() * (base / 2);\n }\n\n #retryDelay(attempt: number, headers: Headers): number {\n const after = parseRetryAfter(headers.get(\"retry-after\"));\n return after === undefined ? this.#backoff(attempt) : Math.min(after, 60_000);\n }\n}\n\n// ── helpers ─────────────────────────────────────────────────────────────\n\n/** Builds `?a=1&b=2` from the defined values (empty string when there are none). */\nfunction query(params: Record<string, string | number | boolean | undefined>): string {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) q.set(k, String(v));\n }\n const s = q.toString();\n return s ? `?${s}` : \"\";\n}\n\nfunction taskId(idOrTask: string | { id: string }): string {\n const id = typeof idOrTask === \"string\" ? idOrTask : idOrTask?.id;\n if (!id) throw new SerpKiteError(0, \"invalid_request\", \"a task id is required\");\n return id;\n}\n\n/** Parses one SSE block (`event:` / `data:` lines) into its event name and JSON data. */\nfunction parseSSE(block: string): { event: string; data: unknown } | undefined {\n let event = \"message\";\n const data: string[] = [];\n for (const line of block.split(\"\\n\")) {\n if (line === \"\" || line.startsWith(\":\")) continue;\n const i = line.indexOf(\":\");\n const field = i < 0 ? line : line.slice(0, i);\n let value = i < 0 ? \"\" : line.slice(i + 1);\n if (value.startsWith(\" \")) value = value.slice(1);\n if (field === \"event\") event = value;\n else if (field === \"data\") data.push(value);\n }\n if (data.length === 0) return undefined;\n const text = data.join(\"\\n\");\n try {\n return { event, data: JSON.parse(text) };\n } catch {\n return { event, data: text };\n }\n}\n\n/** The `error` event of an answer stream as a SerpKiteError (503, like the JSON error). */\nfunction sseError(data: unknown, headers: Headers, requestId: string | undefined): SerpKiteError {\n const e = (data as { error?: { code?: string; message?: string; request_id?: string } })?.error;\n return new SerpKiteError(\n 503,\n e?.code ?? \"unavailable\",\n e?.message ?? \"the answer stream failed\",\n e?.request_id ?? requestId,\n headers,\n );\n}\n\n/** Parses Retry-After (seconds or an HTTP date) into milliseconds. */\nexport function parseRetryAfter(v: string | null): number | undefined {\n if (!v) return undefined;\n const secs = Number(v);\n if (Number.isFinite(secs)) return Math.max(0, secs * 1000);\n const at = Date.parse(v);\n return Number.isNaN(at) ? undefined : Math.max(0, at - Date.now());\n}\n\nfunction responseInfo(res: Response): ResponseInfo {\n const h = res.headers;\n const num = (name: string) => {\n const v = h.get(name);\n if (v === null || v === \"\") return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n };\n const cache = h.get(\"x-cache\")?.toUpperCase();\n return {\n status: res.status,\n headers: h,\n requestId: h.get(\"x-request-id\") ?? undefined,\n creditsUsed: num(\"x-credits-used\"),\n creditsRemaining: num(\"x-credits-remaining\"),\n costUsd: num(\"x-cost-usd\"),\n cache: cache === \"HIT\" || cache === \"MISS\" ? cache : undefined,\n latencyMs: num(\"x-latency-ms\"),\n tokensEstimate: num(\"x-tokens-estimate\"),\n };\n}\n\nfunction withTimeout(outer: AbortSignal | undefined, ms: number) {\n const ctrl = new AbortController();\n let timedOut = false;\n const onAbort = () => ctrl.abort(outer?.reason);\n if (outer?.aborted) ctrl.abort(outer.reason);\n else outer?.addEventListener(\"abort\", onAbort, { once: true });\n const timer =\n ms > 0 && Number.isFinite(ms)\n ? setTimeout(() => {\n timedOut = true;\n ctrl.abort(new SerpKiteError(0, \"timeout\", `request timed out after ${ms} ms`));\n }, ms)\n : undefined;\n return {\n signal: ctrl.signal,\n timedOut: () => timedOut,\n stopTimer: () => {\n if (timer !== undefined) clearTimeout(timer);\n },\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n outer?.removeEventListener(\"abort\", onAbort);\n },\n };\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(signal?.reason);\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\ntype EnvHost = {\n process?: { env?: Record<string, string | undefined> };\n Deno?: { env?: { get?: (name: string) => string | undefined } };\n document?: unknown;\n};\n\nfunction env(name: string): string | undefined {\n const g = globalThis as EnvHost;\n try {\n const v = g.process?.env?.[name];\n if (v) return v;\n } catch {\n // process.env may be a throwing proxy on some edge runtimes.\n }\n try {\n return g.Deno?.env?.get?.(name) || undefined;\n } catch {\n // Deno without --allow-env.\n return undefined;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof (globalThis as EnvHost).document !== \"undefined\";\n}\n","import type { BatchCreateEntry, BatchCreateError } from \"./types\";\n\n/** True when a `batches.create` entry was rejected instead of queued. */\nexport function isBatchError(entry: BatchCreateEntry): entry is BatchCreateError {\n return !(\"id\" in entry && typeof entry.id === \"string\") && \"error\" in entry && !!entry.error;\n}\n","import { SerpKiteError } from \"./errors\";\nimport type { WebhookEvent } from \"./types\";\n\n/** Options for {@link verifyWebhook}. */\nexport interface VerifyWebhookOptions {\n /** Reject deliveries whose timestamp is further than this from now. Default 300. */\n toleranceSeconds?: number;\n /** Current time in Unix seconds (tests). */\n now?: number;\n}\n\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\nfunction header(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === \"function\") {\n return (headers as Headers).get(name) ?? undefined;\n }\n const rec = headers as Record<string, string | string[] | undefined>;\n const key = Object.keys(rec).find((k) => k.toLowerCase() === name.toLowerCase());\n const v = key === undefined ? undefined : rec[key];\n return Array.isArray(v) ? v[0] : v;\n}\n\n/**\n * Checks a webhook delivery (any event: `batch.completed`, `crawl.completed`, `research.completed`,\n * `monitor.results`): `X-SerpKite-Signature` must be\n * `v1=<hex HMAC-SHA256(secret, \"<X-SerpKite-Timestamp>.<raw body>\")>` and the\n * timestamp recent. Pass the raw request body exactly as received (not\n * re-serialised JSON). Uses Web Crypto (Node 18+, Deno, Bun, browsers).\n *\n * ```ts\n * const ok = await verifyWebhook(process.env.SERPKITE_WEBHOOK_SECRET!, rawBody, req.headers);\n * ```\n */\nexport async function verifyWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<boolean> {\n const signature = header(headers, \"x-serpkite-signature\");\n const ts = header(headers, \"x-serpkite-timestamp\");\n if (!secret || !signature?.startsWith(\"v1=\") || !ts || !/^\\d+$/.test(ts)) return false;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - Number(ts)) > (options.toleranceSeconds ?? 300)) return false;\n\n const enc = new TextEncoder();\n const body = typeof payload === \"string\" ? enc.encode(payload) : payload;\n const prefix = enc.encode(`${ts}.`);\n const msg = new Uint8Array(prefix.length + body.length);\n msg.set(prefix);\n msg.set(body, prefix.length);\n const key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const mac = new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, msg));\n const want = Array.from(mac, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n const got = signature.slice(3).toLowerCase();\n if (got.length !== want.length) return false;\n let diff = 0;\n for (let i = 0; i < want.length; i++) diff |= want.charCodeAt(i) ^ got.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Verifies a delivery (see {@link verifyWebhook}) and returns it typed by its\n * `X-SerpKite-Event` header. `deliveryId` is `X-SerpKite-Delivery` (for\n * `monitor.results` it equals `data.run_id`): use it to deduplicate retries. A\n * `crawl.completed` or `research.completed` result over 4 MB arrives as `result: null` with\n * `result_omitted: true`; fetch it from `data.poll_url`. Throws a\n * `SerpKiteError` with code `invalid_signature` when the signature or timestamp\n * doesn't check out.\n *\n * ```ts\n * const ev = await parseWebhook(secret, rawBody, req.headers);\n * if (ev.type === \"monitor.results\") console.log(ev.data.new_results.length);\n * ```\n */\nexport async function parseWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<WebhookEvent> {\n if (!(await verifyWebhook(secret, payload, headers, options))) {\n throw new SerpKiteError(400, \"invalid_signature\", \"webhook signature or timestamp is invalid\");\n }\n const text = typeof payload === \"string\" ? payload : new TextDecoder().decode(payload);\n let data: { event?: unknown };\n try {\n data = JSON.parse(text);\n } catch (err) {\n throw new SerpKiteError(\n 400,\n \"invalid_payload\",\n \"webhook body is not JSON\",\n undefined,\n undefined,\n {\n cause: err,\n },\n );\n }\n const type =\n header(headers, \"x-serpkite-event\") ??\n (typeof data?.event === \"string\" ? data.event : \"batch.completed\");\n return { type, deliveryId: header(headers, \"x-serpkite-delivery\"), data } as WebhookEvent;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/util.ts","../src/webhooks.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Every failure thrown by the SDK. API errors carry the fields of the\n * `{\"error\":{\"code\",\"message\",\"request_id\"}}` envelope; network failures and\n * timeouts use `status: 0` with code `connection_error` or `timeout`.\n */\nexport class SerpKiteError extends Error {\n /** HTTP status, or 0 when no response was received. */\n readonly status: number;\n /** Machine-readable code, e.g. `unauthorized`, `insufficient_credits`, `rate_limited`. */\n readonly code: string;\n /** `X-Request-Id` of the failed request, when the API returned one. */\n readonly requestId: string | undefined;\n /** Response headers, when the API responded. */\n readonly headers: Headers | undefined;\n\n constructor(\n status: number,\n code: string,\n message: string,\n requestId?: string,\n headers?: Headers,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = \"SerpKiteError\";\n this.status = status;\n this.code = code;\n this.requestId = requestId || undefined;\n this.headers = headers;\n }\n}\n\nconst fallbackCodes: Record<number, string> = {\n 400: \"invalid_request\",\n 401: \"unauthorized\",\n 402: \"insufficient_credits\",\n 403: \"forbidden\",\n 404: \"not_found\",\n 429: \"rate_limited\",\n // The API sends upstream failures as 503 with a JSON body; a bare 502/504\n // comes from a proxy (Cloudflare replaces origin 502/504 bodies).\n 502: \"upstream_error\",\n 503: \"unavailable\",\n 504: \"upstream_timeout\",\n};\n\n/** Builds a SerpKiteError from a non-2xx response body (JSON envelope or anything else). */\nexport function errorFromResponse(status: number, headers: Headers, body: string): SerpKiteError {\n let code = fallbackCodes[status] ?? (status >= 500 ? \"server_error\" : \"http_error\");\n let message = body.trim().slice(0, 500) || `HTTP ${status}`;\n let requestId = headers.get(\"x-request-id\") ?? undefined;\n try {\n const parsed = JSON.parse(body) as {\n error?: { code?: unknown; message?: unknown; request_id?: unknown };\n };\n const e = parsed?.error;\n if (e && typeof e === \"object\") {\n if (typeof e.code === \"string\" && e.code) code = e.code;\n if (typeof e.message === \"string\" && e.message) message = e.message;\n if (typeof e.request_id === \"string\" && e.request_id) requestId = e.request_id;\n }\n } catch {\n // Not JSON: keep the text body as the message.\n }\n return new SerpKiteError(status, code, message, requestId, headers);\n}\n","import { errorFromResponse, SerpKiteError } from \"./errors\";\nimport type {\n Account,\n AnswerParams,\n AnswerResponse,\n AnswerStreamEvent,\n AssistantParams,\n AssistantResponse,\n AutocompleteResponse,\n Batch,\n BatchCreateEntry,\n BatchCreateParams,\n BatchCreateResponse,\n ChatGPTParams,\n CompactResponse,\n CrawlParams,\n CrawlTask,\n ExtractParams,\n ExtractResponse,\n ImagesResponse,\n MapParams,\n MapResponse,\n Monitor,\n MonitorCreateParams,\n MonitorList,\n MonitorRun,\n MonitorRunList,\n MonitorRunsParams,\n MonitorUpdateParams,\n NewsResponse,\n PatentsResponse,\n PlaceParams,\n PlacesResponse,\n RankParams,\n RankResponse,\n ResearchParams,\n ResearchTask,\n ReviewsParams,\n ReviewsResponse,\n ScholarResponse,\n SearchParams,\n SearchResponse,\n ShoppingResponse,\n TaskCancelResponse,\n TaskCreated,\n VideosResponse,\n WebpageParams,\n WebpageResponse,\n} from \"./types\";\n\nexport const VERSION = \"0.8.0\";\nexport const DEFAULT_BASE_URL = \"https://api.serpkite.com\";\n\nexport interface SerpKiteOptions {\n /** API key (`skt_live_…`). Defaults to the `SERPKITE_API_KEY` environment variable. */\n apiKey?: string;\n /** Defaults to `SERPKITE_BASE_URL` or https://api.serpkite.com. */\n baseUrl?: string;\n /** Per-attempt timeout in milliseconds. Default 60000. */\n timeoutMs?: number;\n /** Retries after the first attempt on 429, 5xx and network errors. Default 2. */\n maxRetries?: number;\n /** Base delay of the exponential backoff in milliseconds. Default 500. */\n retryDelayMs?: number;\n /** Custom fetch implementation. Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n}\n\n/** Billing and tracing details from the response headers. */\nexport interface ResponseInfo {\n status: number;\n headers: Headers;\n requestId: string | undefined;\n creditsUsed: number | undefined;\n creditsRemaining: number | undefined;\n costUsd: number | undefined;\n /** `HIT` when served from cache (`max_age`), else `MISS`. */\n cache: \"HIT\" | \"MISS\" | undefined;\n latencyMs: number | undefined;\n tokensEstimate: number | undefined;\n}\n\nexport interface RequestOptions {\n signal?: AbortSignal;\n /** Overrides the client's per-attempt timeout. */\n timeoutMs?: number;\n /** Overrides the client's retry count. */\n maxRetries?: number;\n headers?: Record<string, string>;\n /** Called with the final response's headers (credits used/remaining, cache, request id). */\n onResponse?: (info: ResponseInfo) => void;\n}\n\nexport interface BatchCreateOptions extends RequestOptions {\n /**\n * Sent as `Idempotency-Key` (1-255 printable ASCII characters, e.g. a UUID).\n * For 24 hours a retry with the same key and body returns the first response\n * instead of queueing and billing the jobs again.\n */\n idempotencyKey?: string;\n}\n\nexport interface WaitOptions {\n /**\n * Give up after this many milliseconds. Default 600000 (10 minutes) for batches,\n * 2100000 (35 minutes) for `waitForCrawl` (a crawl runs for up to 30 minutes) and\n * 900000 (15 minutes) for `waitResearch`.\n */\n timeoutMs?: number;\n /** First delay between polls. Default 1000. Grows 1.5× per poll. */\n pollIntervalMs?: number;\n /** Upper bound of the delay between polls. Default 10000. */\n maxPollIntervalMs?: number;\n signal?: AbortSignal;\n}\n\n/** A vertical whose return type follows `format`: `\"markdown\"` → string, `\"compact\"` → CompactResponse. */\nexport interface VerticalMethod<P, R> {\n (params: P & { format: \"markdown\" }, options?: RequestOptions): Promise<string>;\n (params: P & { format: \"compact\" }, options?: RequestOptions): Promise<CompactResponse>;\n (params: P & { format?: \"json\" }, options?: RequestOptions): Promise<R>;\n (params: P, options?: RequestOptions): Promise<R | CompactResponse | string>;\n}\n\ntype Method = \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n\n/** Minimum per-attempt timeout of the AI assistant methods: answers take 10-90 s. */\nexport const ASSISTANT_TIMEOUT_MS = 120_000;\n\n/** Task states after which `waitForCrawl` and `waitResearch` return. */\nconst TASK_DONE = new Set([\"completed\", \"failed\", \"canceled\"]);\n\ninterface Call {\n method: Method;\n path: string;\n body?: unknown;\n options?: RequestOptions;\n /** Which failures are safe to retry. `all` = 429, 5xx and network errors. */\n retry?: \"all\" | \"rate_limit\";\n}\n\nexport class SerpKite {\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly maxRetries: number;\n readonly retryDelayMs: number;\n readonly #apiKey: string;\n readonly #fetch: typeof fetch;\n readonly #headers: Record<string, string>;\n\n constructor(options: SerpKiteOptions = {}) {\n const apiKey = options.apiKey ?? env(\"SERPKITE_API_KEY\");\n if (!apiKey) {\n throw new SerpKiteError(\n 0,\n \"missing_api_key\",\n \"No API key: pass { apiKey } or set the SERPKITE_API_KEY environment variable\",\n );\n }\n this.#apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? env(\"SERPKITE_BASE_URL\") ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n \"\",\n );\n this.timeoutMs = options.timeoutMs ?? 60_000;\n this.maxRetries = Math.max(0, options.maxRetries ?? 2);\n this.retryDelayMs = options.retryDelayMs ?? 500;\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new SerpKiteError(\n 0,\n \"missing_fetch\",\n \"No global fetch: pass { fetch } (Node 18+ has one)\",\n );\n }\n this.#fetch = f;\n this.#headers = { ...options.headers };\n }\n\n // ── Verticals ─────────────────────────────────────────────────────────\n\n /** Google web search: organic results, answer box, knowledge graph, people also ask… */\n readonly search = this.#vertical<SearchParams, SearchResponse>(\"/v1/search\");\n /** Google Images. */\n readonly images = this.#vertical<SearchParams, ImagesResponse>(\"/v1/images\");\n /** Google Videos. */\n readonly videos = this.#vertical<SearchParams, VideosResponse>(\"/v1/videos\");\n /** Google News. */\n readonly news = this.#vertical<SearchParams, NewsResponse>(\"/v1/news\");\n /**\n * Google Maps search (places with coordinates). Accepts `ll` (\"@lat,lng,14z\"). With\n * `engine: \"yandex\"` (where enabled) the places come from Yandex Maps and carry an `oid`.\n */\n readonly maps = this.#vertical<SearchParams, PlacesResponse>(\"/v1/maps\");\n /** Google local results. */\n readonly places = this.#vertical<SearchParams, PlacesResponse>(\"/v1/places\");\n /**\n * Details of one place by `place_id`, `cid` or `fid`, or by a Yandex Maps `oid` (implies\n * `engine: \"yandex\"`): address, country, time zone, hours\n * (`opening_hours` text and structured `weekly_hours`), phones, description and photos.\n * `results` holds the place, or is empty (not charged) when no place has that id. 1 credit.\n */\n readonly place = this.#vertical<PlaceParams, PlacesResponse>(\"/v1/place\");\n /** Reviews of a place by `place_id`, `cid` or `fid`. Page with `page_token` = `next_page_token`. */\n readonly reviews = this.#vertical<ReviewsParams, ReviewsResponse>(\"/v1/reviews\");\n /** Google Shopping. */\n readonly shopping = this.#vertical<SearchParams, ShoppingResponse>(\"/v1/shopping\");\n /** Google Scholar. */\n readonly scholar = this.#vertical<SearchParams, ScholarResponse>(\"/v1/scholar\");\n /** Google Patents. */\n readonly patents = this.#vertical<SearchParams, PatentsResponse>(\"/v1/patents\");\n /** Google autocomplete suggestions. */\n readonly autocomplete = this.#vertical<SearchParams, AutocompleteResponse>(\"/v1/autocomplete\");\n /** Fetch any public URL as clean Markdown plus metadata. */\n readonly webpage = this.#vertical<WebpageParams, WebpageResponse>(\"/v1/webpage\");\n\n // ── AI assistants ─────────────────────────────────────────────────────\n // Billed even when the response is lost: retried only on 429 (failed, refused and empty\n // answers are free), and each attempt waits at least 120 s (answers take 10-90 s).\n\n /**\n * Ask ChatGPT as a logged-out user: the answer (`text`, `markdown`), cited `sources`,\n * `search_queries`, `products`, `places` and the guest `model`. `web_search` (default true)\n * makes it search the web; `country` asks from an exit in that country. `track` returns\n * `mentions` of your brands and domains. 5 credits.\n */\n readonly chatgpt = this.#assistant<ChatGPTParams>(\"/v1/chatgpt\");\n /** Ask Google Gemini as a logged-out user: the answer with the sources it grounded on. 5 credits. */\n readonly gemini = this.#assistant<AssistantParams>(\"/v1/gemini\");\n /** Microsoft Copilot Search on Bing: the answer, cited sources and products. 3 credits. */\n readonly copilot = this.#assistant<AssistantParams>(\"/v1/copilot\");\n /** Google AI Mode: the answer, cited sources and products. 3 credits. */\n readonly aiMode = this.#assistant<AssistantParams>(\"/v1/ai_mode\");\n /** Brave Search's AI answer: cited sources, the searches it ran and products. 2 credits. */\n readonly braveAi = this.#assistant<AssistantParams>(\"/v1/brave_ai\");\n\n /** Position of `domain` for keyword `q` in the top `num` results (default 100). */\n rank(params: RankParams, options?: RequestOptions): Promise<RankResponse> {\n return this.request({ method: \"POST\", path: \"/v1/rank\", body: params, options });\n }\n\n /**\n * Read up to 20 URLs (HTML or PDF) as Markdown, text or HTML in one call, with optional\n * query-ranked `highlights` (BM25), `include_links` and `include_images`. 1 credit per URL\n * that comes back (0.5 from cache); URLs that fail are listed in `failed` and cost nothing.\n * `summary: true` adds an AI summary of each page and `output_schema` (a JSON Schema, guided\n * by an optional `prompt`) returns each page's data as JSON in `output`, each at an extra\n * per-page price (https://serpkite.com/pricing); a page whose model step failed has\n * `summary_error` / `output_error` instead and isn't charged for it.\n */\n extract(params: ExtractParams, options?: RequestOptions): Promise<ExtractResponse> {\n // Billed per page even when the response is lost: retried only on 429, and the HTTP\n // timeout covers the server's deadline (timeout: default 50 s, 75 s with AI) + 15 s.\n const ai = params.summary === true || params.output_schema !== undefined;\n const wait = ((params.timeout ?? (ai ? 75 : 50)) + 15) * 1000;\n return this.request({\n method: \"POST\",\n path: \"/v1/extract\",\n body: params,\n options: {\n ...options,\n timeoutMs: options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, wait) : 0),\n },\n retry: \"rate_limit\",\n });\n }\n\n /**\n * The URLs of a site from its sitemaps (XML, RSS/Atom, gzip, plain text) and start page,\n * canonicalised and deduplicated, optionally ranked by `search`. `include_paths` /\n * `exclude_paths` (regular expressions on the URL path) and `ignore_query_parameters`\n * narrow the list. 1 credit; free when nothing is found.\n */\n map(params: MapParams, options?: RequestOptions): Promise<MapResponse> {\n return this.request({ method: \"POST\", path: \"/v1/map\", body: params, options });\n }\n\n /** Balance, limits and this month's usage for the key's account. */\n account(options?: RequestOptions): Promise<Account> {\n return this.request({ method: \"GET\", path: \"/v1/account\", options });\n }\n\n // ── AI answers ────────────────────────────────────────────────────────\n\n /**\n * A cited answer grounded on live Google results: Markdown with `[n]` markers in `answer`,\n * the cited sources in `citations`. `depth: \"fast\"` answers from the result page;\n * `\"standard\"` (default) also reads the top `num_sources` pages and costs more\n * (https://serpkite.com/pricing; `meta.credits` splits search and model). With\n * `output_schema` the answer is a JSON value in `output`.\n * When the model fails the call throws `ai_unavailable` (503) and the model part is refunded;\n * a search with nothing to answer from returns `answer: null` and a `meta.warnings` entry.\n */\n answer(params: AnswerParams, options?: RequestOptions): Promise<AnswerResponse> {\n // Billed even when the response is lost (and a 503 ai_unavailable still charges the\n // search part): retried only on 429, and the HTTP timeout covers a search plus the model.\n return this.request({\n method: \"POST\",\n path: \"/v1/answer\",\n body: params,\n options: { ...options, timeoutMs: this.#answerTimeout(options) },\n retry: \"rate_limit\",\n });\n }\n\n /**\n * Streams an answer as Server-Sent Events: `results` first, then `delta` text pieces,\n * then `done` (final answer, citations, meta). A server `error` event is thrown as a\n * `SerpKiteError` (`ai_unavailable`). Breaking out of the loop closes the connection\n * (the model part is then not charged). Can't be combined with `output_schema`.\n *\n * ```ts\n * for await (const ev of sk.answerStream({ q: \"what is retrieval-augmented generation\" })) {\n * if (ev.event === \"delta\") process.stdout.write(ev.data.text);\n * }\n * ```\n */\n answerStream(params: AnswerParams, options?: RequestOptions): AsyncIterable<AnswerStreamEvent> {\n return this.#stream(\n { ...params, stream: true },\n { ...options, timeoutMs: this.#answerTimeout(options) },\n );\n }\n\n /** The search (pages read included) runs before the first byte: wait at least 100 s. */\n #answerTimeout(options?: RequestOptions): number {\n return options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, 100_000) : 0);\n }\n\n // ── Async tasks: crawl ────────────────────────────────────────────────\n\n /**\n * Starts an async crawl of one site and returns the queued task (`202`). 1 credit per page\n * read (0.5 from cache); `limit` credits (up to 1000 pages, `max_depth` up to 10) are\n * reserved up front and the rest refunded. `sitemap` (`include` default, `only`, `skip`)\n * seeds the crawl with sitemap URLs; `query` reads the most relevant pages first. robots.txt\n * and Crawl-delay are honoured; `result.stats.stopped` says why the crawl ended (`done`,\n * `limit`, `time_limit`, `size_limit`, `too_many_failures`, `canceled`).\n * Only retried on 429, so a lost response never starts (and reserves) a second crawl.\n */\n crawl(params: CrawlParams, options?: RequestOptions): Promise<TaskCreated> {\n return this.request({\n method: \"POST\",\n path: \"/v1/crawl\",\n body: params,\n options,\n retry: \"rate_limit\",\n });\n }\n\n /** Polls a crawl (results are kept for 24 hours). */\n getCrawl(id: string, options?: RequestOptions): Promise<CrawlTask> {\n return this.request({ method: \"GET\", path: `/v1/crawl/${encodeURIComponent(id)}`, options });\n }\n\n /** Cancels a crawl: refunded if still queued, else stops at the next checkpoint. */\n cancelCrawl(id: string, options?: RequestOptions): Promise<TaskCancelResponse> {\n return this.request({\n method: \"DELETE\",\n path: `/v1/crawl/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Polls until the crawl is `completed`, `failed` or `canceled` and returns it (a failed\n * task is returned, not thrown; check `status` and `error`). Accepts an id or the task\n * returned by `crawl`.\n */\n async waitForCrawl(\n idOrTask: string | { id: string },\n options: WaitOptions = {},\n ): Promise<CrawlTask> {\n return this.#waitTask(taskId(idOrTask), (id) => this.getCrawl(id, { signal: options.signal }), {\n ...options,\n timeoutMs: options.timeoutMs ?? 2_100_000,\n });\n }\n\n // ── Async tasks: research ─────────────────────────────────────────────\n\n /**\n * Starts an async research task: the model plans searches, reads the sources and writes a\n * cited Markdown report (`result.report`). `effort` (`lite`, `standard` (default), `deep`)\n * sets how much is searched and read; its flat price (https://serpkite.com/pricing) is\n * reserved up front and charged only when the report is written (a canceled task is charged\n * for the searches already run). With\n * `output_schema` the report is also returned as JSON in `result.output`. Poll with\n * `getResearch` / `waitResearch` or receive the signed `research.completed` webhook.\n * Only retried on 429, so a lost response never starts (and reserves) a second task.\n */\n research(params: ResearchParams, options?: RequestOptions): Promise<TaskCreated> {\n return this.request({\n method: \"POST\",\n path: \"/v1/research\",\n body: params,\n options,\n retry: \"rate_limit\",\n });\n }\n\n /** Polls a research task (`progress` while it runs; results are kept for 24 hours). */\n getResearch(id: string, options?: RequestOptions): Promise<ResearchTask> {\n return this.request({\n method: \"GET\",\n path: `/v1/research/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Cancels a research task: refunded if still queued, else stops at its next round and is\n * charged for the searches run.\n */\n cancelResearch(id: string, options?: RequestOptions): Promise<TaskCancelResponse> {\n return this.request({\n method: \"DELETE\",\n path: `/v1/research/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Polls until the research task is `completed`, `failed` or `canceled` and returns it (a\n * failed task is returned, not thrown; check `status` and `error`). The report is in\n * `result.report`. Accepts an id or the task returned by `research`. Default timeout\n * 15 minutes.\n */\n async waitResearch(\n idOrTask: string | { id: string },\n options: WaitOptions = {},\n ): Promise<ResearchTask> {\n return this.#waitTask(\n taskId(idOrTask),\n (id) => this.getResearch(id, { signal: options.signal }),\n { ...options, timeoutMs: options.timeoutMs ?? 900_000 },\n );\n }\n\n // ── Monitors ──────────────────────────────────────────────────────────\n\n readonly monitors = {\n /**\n * Saves a `search` or `news` request (`q`) that runs on a schedule (hourly to monthly) and\n * POSTs results it hasn't seen before to `webhook_url` (`monitor.results`). With\n * `endpoint: \"webpage\"` and a `url` it checks that page each interval (1 credit per check)\n * and reports it (`MonitorPageChange`) when its content changes. `metadata` (your JSON\n * object, at most 2 KB) is echoed on the monitor and in its webhooks. Without a\n * `webhook_url`, read new results from `monitors.runs`. `active: false`\n * creates it paused. With an AI assistant `endpoint` (`chatgpt`, `gemini`, `copilot`,\n * `ai_mode`, `brave_ai`) and a `prompt` every run reports the answer (`MonitorAnswer`:\n * sources, `new_sources`, `mentions` with `track`). A monitor pauses itself after 10 failed runs in a row\n * (`last_status: \"paused\"`); `update(id, { active: true })` resumes it. Each run costs\n * what its search costs. Only retried on 429.\n */\n create: (params: MonitorCreateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: \"/v1/monitors\",\n body: params,\n options,\n retry: \"rate_limit\",\n }),\n\n /** The account's monitors. */\n list: (options?: RequestOptions): Promise<MonitorList> =>\n this.request({ method: \"GET\", path: \"/v1/monitors\", options }),\n\n get: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({ method: \"GET\", path: `/v1/monitors/${encodeURIComponent(id)}`, options }),\n\n /**\n * Changes `name`, `interval` / `interval_seconds`, `webhook_url` (`\"\"` removes it),\n * `active` (`true` resumes a paused monitor), `metadata` (an object replaces it, `null`\n * clears it) or the saved request (`endpoint`, `q`, `url`, `country`, `num`, domains,\n * `engine`…). Switching `endpoint` between a search and `webpage` drops the other kind's\n * fields. A changed search reports every result as new on the next run.\n */\n update: (id: string, params: MonitorUpdateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"PATCH\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n body: params,\n options,\n }),\n\n delete: async (id: string, options?: RequestOptions): Promise<void> => {\n await this.request({\n method: \"DELETE\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n options,\n });\n },\n\n /** Runs an active monitor at the next scheduler poll (within about 30 seconds).\n * Paused monitors return 409; update(id, { active: true }) resumes and schedules them. */\n run: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: `/v1/monitors/${encodeURIComponent(id)}/run`,\n options,\n retry: \"rate_limit\",\n }),\n\n /**\n * One page of the monitor's run history, newest first (`limit` 1-100, default 20). Each\n * run carries its new results for 24 hours. Pass `next_before` as `before` for the next\n * page; it is null on the last one.\n */\n runs: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): Promise<MonitorRunList> =>\n this.request({\n method: \"GET\",\n path: `/v1/monitors/${encodeURIComponent(id)}/runs${query(params)}`,\n options,\n }),\n\n /**\n * Iterates over every run of the monitor, newest first, fetching pages of `limit` as\n * needed (starting at `before` when given).\n *\n * ```ts\n * for await (const run of sk.monitors.iterRuns(\"m1\")) console.log(run.new_results);\n * ```\n */\n iterRuns: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): AsyncIterable<MonitorRun> => this.#iterRuns(id, params, options),\n };\n\n // ── Batches ───────────────────────────────────────────────────────────\n\n readonly batches = {\n /**\n * Queue 1-100 requests for one endpoint at half price. Returns one entry per\n * request, in order: a queued `Batch` or an error object. Without an\n * `idempotencyKey` it is only retried on 429, so a lost response never queues\n * (and bills) the same requests twice. With one, 5xx and network errors are\n * retried too: the server replays the first response for the same key and body.\n */\n create: (\n params: BatchCreateParams,\n options: BatchCreateOptions = {},\n ): Promise<BatchCreateResponse> => {\n const { idempotencyKey, ...rest } = options;\n return this.request({\n method: \"POST\",\n path: \"/v1/batches\",\n body: params,\n options: idempotencyKey\n ? { ...rest, headers: { ...rest.headers, \"Idempotency-Key\": idempotencyKey } }\n : rest,\n retry: idempotencyKey ? \"all\" : \"rate_limit\",\n });\n },\n\n /** Poll one batch job. */\n get: (id: string, options?: RequestOptions): Promise<Batch> =>\n this.request({ method: \"GET\", path: `/v1/batches/${encodeURIComponent(id)}`, options }),\n\n /**\n * Poll until the job is `done` or `failed` and return it (a failed job is\n * returned, not thrown; check `status` and `error`). Accepts an id or an\n * entry from `create`; an entry that failed to queue throws its error.\n */\n wait: (idOrEntry: string | BatchCreateEntry | undefined, options: WaitOptions = {}) =>\n this.#wait(idOrEntry, options),\n };\n\n // ── Plumbing ──────────────────────────────────────────────────────────\n\n async *#iterRuns(\n id: string,\n params: MonitorRunsParams,\n options?: RequestOptions,\n ): AsyncGenerator<MonitorRun> {\n let before = params.before;\n for (;;) {\n const page = await this.monitors.runs(id, { ...params, before }, options);\n yield* page.results;\n if (!page.next_before || page.results.length === 0) return;\n before = page.next_before;\n }\n }\n\n async #waitTask<T extends { status: string }>(\n id: string,\n get: (id: string) => Promise<T>,\n o: WaitOptions,\n ): Promise<T> {\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const task = await get(id);\n if (TASK_DONE.has(task.status)) return task;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `task ${id} still ${task.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n #vertical<P, R>(path: string): VerticalMethod<P, R> {\n return ((params: P, options?: RequestOptions) =>\n this.request({ method: \"POST\", path, body: params, options })) as VerticalMethod<P, R>;\n }\n\n #assistant<P>(path: string): VerticalMethod<P, AssistantResponse> {\n return ((params: P, options?: RequestOptions) =>\n this.request({\n method: \"POST\",\n path,\n body: params,\n options: {\n ...options,\n timeoutMs:\n options?.timeoutMs ??\n (this.timeoutMs > 0 ? Math.max(this.timeoutMs, ASSISTANT_TIMEOUT_MS) : 0),\n },\n retry: \"rate_limit\",\n })) as VerticalMethod<P, AssistantResponse>;\n }\n\n async #wait(idOrEntry: string | BatchCreateEntry | undefined, o: WaitOptions): Promise<Batch> {\n let id: string | undefined;\n if (typeof idOrEntry === \"string\") {\n id = idOrEntry;\n } else if (idOrEntry && \"id\" in idOrEntry && typeof idOrEntry.id === \"string\") {\n id = idOrEntry.id;\n } else if (idOrEntry && \"error\" in idOrEntry && idOrEntry.error && !(\"id\" in idOrEntry)) {\n const e = idOrEntry.error as { code?: string; message?: string; request_id?: string };\n throw new SerpKiteError(\n 400,\n e.code ?? \"invalid_request\",\n e.message ?? \"batch entry was not queued\",\n e.request_id,\n );\n }\n if (!id) {\n throw new SerpKiteError(0, \"invalid_request\", \"batches.wait needs a batch id\");\n }\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const batch = await this.batches.get(id, { signal: o.signal });\n if (batch.status === \"done\" || batch.status === \"failed\") return batch;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `batch ${id} still ${batch.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n /** Opens `POST /v1/answer` as an event stream (retrying the connect like `request`) and parses it. */\n async *#stream(body: unknown, options: RequestOptions = {}): AsyncGenerator<AnswerStreamEvent> {\n const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const headers: Record<string, string> = {\n Accept: \"text/event-stream, application/json;q=0.9\",\n Authorization: `Bearer ${this.#apiKey}`,\n ...this.#headers,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n if (!isBrowser()) headers[\"User-Agent\"] = `serpkite-typescript/${VERSION}`;\n let res: Response | undefined;\n let cleanup = () => {};\n for (let attempt = 0; !res; attempt++) {\n const canRetry = attempt < maxRetries;\n // The timeout covers connecting (until the headers arrive), not the whole stream.\n const t = withTimeout(options.signal, timeoutMs);\n let r: Response;\n try {\n r = await this.#fetch(`${this.baseUrl}/v1/answer`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: t.signal,\n });\n } catch (err) {\n t.cleanup();\n if (options.signal?.aborted) throw err;\n if (canRetry) {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const code = t.timedOut() ? \"timeout\" : \"connection_error\";\n const msg = t.timedOut()\n ? `request timed out after ${timeoutMs} ms`\n : `request failed: ${err instanceof Error ? err.message : String(err)}`;\n throw new SerpKiteError(0, code, msg, undefined, undefined, { cause: err });\n }\n if (!r.ok) {\n const text = await r.text().catch(() => \"\");\n t.cleanup();\n // Nothing is charged before the stream starts, so 5xx are safe to retry.\n if (canRetry && (r.status === 429 || r.status >= 500)) {\n await sleep(this.#retryDelay(attempt, r.headers), options.signal);\n continue;\n }\n throw errorFromResponse(r.status, r.headers, text);\n }\n // Stop the connect timer but keep forwarding the caller's abort signal.\n t.stopTimer();\n cleanup = t.cleanup;\n res = r;\n }\n options.onResponse?.(responseInfo(res));\n const requestId = res.headers.get(\"x-request-id\") ?? undefined;\n if (!res.body) {\n cleanup();\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response has no body\",\n requestId,\n res.headers,\n );\n }\n // A plain JSON answer (e.g. from a proxy that dropped the stream) becomes results + done.\n if ((res.headers.get(\"content-type\") ?? \"\").includes(\"json\")) {\n let answer: AnswerResponse;\n try {\n answer = JSON.parse(await res.text()) as AnswerResponse;\n } catch (err) {\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response is not valid JSON\",\n requestId,\n res.headers,\n { cause: err },\n );\n } finally {\n cleanup();\n }\n yield { event: \"results\", data: { request: answer.request, results: answer.results } };\n yield {\n event: \"done\",\n data: { answer: answer.answer ?? \"\", citations: answer.citations, meta: answer.meta },\n };\n return;\n }\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n let done = false;\n try {\n while (!done) {\n let chunk: Awaited<ReturnType<typeof reader.read>>;\n try {\n chunk = await reader.read();\n } catch (err) {\n if (options.signal?.aborted) throw err;\n throw new SerpKiteError(\n 0,\n \"connection_error\",\n `stream read failed: ${err instanceof Error ? err.message : String(err)}`,\n requestId,\n res.headers,\n { cause: err },\n );\n }\n if (chunk.done) {\n buf += decoder.decode();\n done = true;\n } else {\n buf += decoder.decode(chunk.value, { stream: true });\n }\n // A CR at the end of a chunk may be the first half of a CRLF split across chunks:\n // keep it until the next chunk shows what follows.\n const held = !done && buf.endsWith(\"\\r\") ? \"\\r\" : \"\";\n if (held) buf = buf.slice(0, -1);\n buf = buf.replace(/\\r\\n?/g, \"\\n\");\n let sep = buf.indexOf(\"\\n\\n\");\n while (sep >= 0 || (done && buf.trim() !== \"\")) {\n const raw = sep >= 0 ? buf.slice(0, sep) : buf;\n buf = sep >= 0 ? buf.slice(sep + 2) : \"\";\n const ev = parseSSE(raw);\n if (ev) {\n if (ev.event === \"error\") throw sseError(ev.data, res.headers, requestId);\n if (ev.event === \"results\" || ev.event === \"delta\" || ev.event === \"done\") {\n yield { event: ev.event, data: ev.data } as AnswerStreamEvent;\n if (ev.event === \"done\") return;\n }\n }\n sep = buf.indexOf(\"\\n\\n\");\n }\n buf += held;\n }\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"the stream ended before the done event\",\n requestId,\n res.headers,\n );\n } finally {\n cleanup();\n reader.cancel().catch(() => {});\n }\n }\n\n /** Low-level request: JSON body in, parsed JSON (or text for non-JSON responses) out. */\n async request<T>(call: Call): Promise<T> {\n const { method, path, body, options = {} } = call;\n const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const headers: Record<string, string> = {\n Accept: \"application/json, text/markdown;q=0.9\",\n Authorization: `Bearer ${this.#apiKey}`,\n ...this.#headers,\n ...options.headers,\n };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n // Browsers would turn a custom User-Agent into a CORS preflight header.\n if (!isBrowser()) headers[\"User-Agent\"] = `serpkite-typescript/${VERSION}`;\n const init: RequestInit = {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n };\n\n for (let attempt = 0; ; attempt++) {\n const canRetry = attempt < maxRetries;\n let res: Response;\n const { signal, cleanup, timedOut } = withTimeout(options.signal, timeoutMs);\n try {\n res = await this.#fetch(this.baseUrl + path, { ...init, signal });\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const msg = timedOut()\n ? `request timed out after ${timeoutMs} ms`\n : `request failed: ${err instanceof Error ? err.message : String(err)}`;\n throw new SerpKiteError(0, code, msg, undefined, undefined, { cause: err });\n }\n\n let text: string;\n try {\n text = await res.text();\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n throw new SerpKiteError(\n 0,\n code,\n \"failed to read the response body\",\n undefined,\n res.headers,\n {\n cause: err,\n },\n );\n }\n cleanup();\n\n if (!res.ok) {\n const retryable = res.status === 429 || (res.status >= 500 && call.retry !== \"rate_limit\");\n if (canRetry && retryable) {\n await sleep(this.#retryDelay(attempt, res.headers), options.signal);\n continue;\n }\n throw errorFromResponse(res.status, res.headers, text);\n }\n\n options.onResponse?.(responseInfo(res));\n const type = res.headers.get(\"content-type\") ?? \"\";\n if (type.includes(\"json\")) {\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response is not valid JSON\",\n res.headers.get(\"x-request-id\") ?? undefined,\n res.headers,\n { cause: err },\n );\n }\n }\n return text as T;\n }\n }\n\n #backoff(attempt: number): number {\n const base = Math.min(this.retryDelayMs * 2 ** attempt, 8_000);\n return base / 2 + Math.random() * (base / 2);\n }\n\n #retryDelay(attempt: number, headers: Headers): number {\n const after = parseRetryAfter(headers.get(\"retry-after\"));\n return after === undefined ? this.#backoff(attempt) : Math.min(after, 60_000);\n }\n}\n\n// ── helpers ─────────────────────────────────────────────────────────────\n\n/** Builds `?a=1&b=2` from the defined values (empty string when there are none). */\nfunction query(params: Record<string, string | number | boolean | undefined>): string {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) q.set(k, String(v));\n }\n const s = q.toString();\n return s ? `?${s}` : \"\";\n}\n\nfunction taskId(idOrTask: string | { id: string }): string {\n const id = typeof idOrTask === \"string\" ? idOrTask : idOrTask?.id;\n if (!id) throw new SerpKiteError(0, \"invalid_request\", \"a task id is required\");\n return id;\n}\n\n/** Parses one SSE block (`event:` / `data:` lines) into its event name and JSON data. */\nfunction parseSSE(block: string): { event: string; data: unknown } | undefined {\n let event = \"message\";\n const data: string[] = [];\n for (const line of block.split(\"\\n\")) {\n if (line === \"\" || line.startsWith(\":\")) continue;\n const i = line.indexOf(\":\");\n const field = i < 0 ? line : line.slice(0, i);\n let value = i < 0 ? \"\" : line.slice(i + 1);\n if (value.startsWith(\" \")) value = value.slice(1);\n if (field === \"event\") event = value;\n else if (field === \"data\") data.push(value);\n }\n if (data.length === 0) return undefined;\n const text = data.join(\"\\n\");\n try {\n return { event, data: JSON.parse(text) };\n } catch {\n return { event, data: text };\n }\n}\n\n/** The `error` event of an answer stream as a SerpKiteError (503, like the JSON error). */\nfunction sseError(data: unknown, headers: Headers, requestId: string | undefined): SerpKiteError {\n const e = (data as { error?: { code?: string; message?: string; request_id?: string } })?.error;\n return new SerpKiteError(\n 503,\n e?.code ?? \"unavailable\",\n e?.message ?? \"the answer stream failed\",\n e?.request_id ?? requestId,\n headers,\n );\n}\n\n/** Parses Retry-After (seconds or an HTTP date) into milliseconds. */\nexport function parseRetryAfter(v: string | null): number | undefined {\n if (!v) return undefined;\n const secs = Number(v);\n if (Number.isFinite(secs)) return Math.max(0, secs * 1000);\n const at = Date.parse(v);\n return Number.isNaN(at) ? undefined : Math.max(0, at - Date.now());\n}\n\nfunction responseInfo(res: Response): ResponseInfo {\n const h = res.headers;\n const num = (name: string) => {\n const v = h.get(name);\n if (v === null || v === \"\") return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n };\n const cache = h.get(\"x-cache\")?.toUpperCase();\n return {\n status: res.status,\n headers: h,\n requestId: h.get(\"x-request-id\") ?? undefined,\n creditsUsed: num(\"x-credits-used\"),\n creditsRemaining: num(\"x-credits-remaining\"),\n costUsd: num(\"x-cost-usd\"),\n cache: cache === \"HIT\" || cache === \"MISS\" ? cache : undefined,\n latencyMs: num(\"x-latency-ms\"),\n tokensEstimate: num(\"x-tokens-estimate\"),\n };\n}\n\nfunction withTimeout(outer: AbortSignal | undefined, ms: number) {\n const ctrl = new AbortController();\n let timedOut = false;\n const onAbort = () => ctrl.abort(outer?.reason);\n if (outer?.aborted) ctrl.abort(outer.reason);\n else outer?.addEventListener(\"abort\", onAbort, { once: true });\n const timer =\n ms > 0 && Number.isFinite(ms)\n ? setTimeout(() => {\n timedOut = true;\n ctrl.abort(new SerpKiteError(0, \"timeout\", `request timed out after ${ms} ms`));\n }, ms)\n : undefined;\n return {\n signal: ctrl.signal,\n timedOut: () => timedOut,\n stopTimer: () => {\n if (timer !== undefined) clearTimeout(timer);\n },\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n outer?.removeEventListener(\"abort\", onAbort);\n },\n };\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(signal?.reason);\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\ntype EnvHost = {\n process?: { env?: Record<string, string | undefined> };\n Deno?: { env?: { get?: (name: string) => string | undefined } };\n document?: unknown;\n};\n\nfunction env(name: string): string | undefined {\n const g = globalThis as EnvHost;\n try {\n const v = g.process?.env?.[name];\n if (v) return v;\n } catch {\n // process.env may be a throwing proxy on some edge runtimes.\n }\n try {\n return g.Deno?.env?.get?.(name) || undefined;\n } catch {\n // Deno without --allow-env.\n return undefined;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof (globalThis as EnvHost).document !== \"undefined\";\n}\n","import type { BatchCreateEntry, BatchCreateError } from \"./types\";\n\n/** True when a `batches.create` entry was rejected instead of queued. */\nexport function isBatchError(entry: BatchCreateEntry): entry is BatchCreateError {\n return !(\"id\" in entry && typeof entry.id === \"string\") && \"error\" in entry && !!entry.error;\n}\n","import { SerpKiteError } from \"./errors\";\nimport type { WebhookEvent } from \"./types\";\n\n/** Options for {@link verifyWebhook}. */\nexport interface VerifyWebhookOptions {\n /** Reject deliveries whose timestamp is further than this from now. Default 300. */\n toleranceSeconds?: number;\n /** Current time in Unix seconds (tests). */\n now?: number;\n}\n\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\nfunction header(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === \"function\") {\n return (headers as Headers).get(name) ?? undefined;\n }\n const rec = headers as Record<string, string | string[] | undefined>;\n const key = Object.keys(rec).find((k) => k.toLowerCase() === name.toLowerCase());\n const v = key === undefined ? undefined : rec[key];\n return Array.isArray(v) ? v[0] : v;\n}\n\n/**\n * Checks a webhook delivery (any event: `batch.completed`, `crawl.completed`, `research.completed`,\n * `monitor.results`): `X-SerpKite-Signature` must be\n * `v1=<hex HMAC-SHA256(secret, \"<X-SerpKite-Timestamp>.<raw body>\")>` and the\n * timestamp recent. Pass the raw request body exactly as received (not\n * re-serialised JSON). Uses Web Crypto (Node 18+, Deno, Bun, browsers).\n *\n * ```ts\n * const ok = await verifyWebhook(process.env.SERPKITE_WEBHOOK_SECRET!, rawBody, req.headers);\n * ```\n */\nexport async function verifyWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<boolean> {\n const signature = header(headers, \"x-serpkite-signature\");\n const ts = header(headers, \"x-serpkite-timestamp\");\n if (!secret || !signature?.startsWith(\"v1=\") || !ts || !/^\\d+$/.test(ts)) return false;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - Number(ts)) > (options.toleranceSeconds ?? 300)) return false;\n\n const enc = new TextEncoder();\n const body = typeof payload === \"string\" ? enc.encode(payload) : payload;\n const prefix = enc.encode(`${ts}.`);\n const msg = new Uint8Array(prefix.length + body.length);\n msg.set(prefix);\n msg.set(body, prefix.length);\n const key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const mac = new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, msg));\n const want = Array.from(mac, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n const got = signature.slice(3).toLowerCase();\n if (got.length !== want.length) return false;\n let diff = 0;\n for (let i = 0; i < want.length; i++) diff |= want.charCodeAt(i) ^ got.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Verifies a delivery (see {@link verifyWebhook}) and returns it typed by its\n * `X-SerpKite-Event` header. `deliveryId` is `X-SerpKite-Delivery` (for\n * `monitor.results` it equals `data.run_id`): use it to deduplicate retries. A\n * `crawl.completed` or `research.completed` result over 4 MB arrives as `result: null` with\n * `result_omitted: true`; fetch it from `data.poll_url`. Throws a\n * `SerpKiteError` with code `invalid_signature` when the signature or timestamp\n * doesn't check out.\n *\n * ```ts\n * const ev = await parseWebhook(secret, rawBody, req.headers);\n * if (ev.type === \"monitor.results\") console.log(ev.data.new_results.length);\n * ```\n */\nexport async function parseWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<WebhookEvent> {\n if (!(await verifyWebhook(secret, payload, headers, options))) {\n throw new SerpKiteError(400, \"invalid_signature\", \"webhook signature or timestamp is invalid\");\n }\n const text = typeof payload === \"string\" ? payload : new TextDecoder().decode(payload);\n let data: { event?: unknown };\n try {\n data = JSON.parse(text);\n } catch (err) {\n throw new SerpKiteError(\n 400,\n \"invalid_payload\",\n \"webhook body is not JSON\",\n undefined,\n undefined,\n {\n cause: err,\n },\n );\n }\n const type =\n header(headers, \"x-serpkite-event\") ??\n (typeof data?.event === \"string\" ? data.event : \"batch.completed\");\n return { type, deliveryId: header(headers, \"x-serpkite-delivery\"), data } as WebhookEvent;\n}\n"]}
|