@openserp/sdk 0.1.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 ADDED
@@ -0,0 +1,268 @@
1
+ # @openserp/sdk
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@openserp/sdk.svg)](https://www.npmjs.com/package/@openserp/sdk)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@openserp/sdk.svg)](https://www.npmjs.com/package/@openserp/sdk)
5
+ [![license](https://img.shields.io/npm/l/@openserp/sdk.svg)](https://github.com/karust/openserp/blob/master/LICENSE)
6
+
7
+ Universal TypeScript / JavaScript SDK for the **OpenSERP** multi-engine SERP API — Google, Bing, Yandex, Baidu, DuckDuckGo, and Ecosia results in a single call. Works against the self-hosted [open source server](https://github.com/karust/openserp) and against [OpenSERP Cloud](https://openserp.org/cloud) with the same code.
8
+
9
+ Use it for AI grounding, RAG pipelines, LLM tool use, SEO monitoring, competitor analysis, and search-powered automations. Open-source alternative to SerpAPI and DataForSEO.
10
+
11
+ > **Alpha — the API may change before `1.0.0`.** Pin a version in production.
12
+
13
+ ## Contents
14
+
15
+ - [Install](#install)
16
+ - [Quickstart — OSS (self-hosted)](#quickstart--oss-self-hosted)
17
+ - [Quickstart — Cloud](#quickstart--cloud)
18
+ - [Why two backends?](#why-two-backends)
19
+ - [Search](#search)
20
+ - [Images](#images)
21
+ - [Endpoint availability](#endpoint-availability)
22
+ - [Telemetry](#telemetry)
23
+ - [Error handling](#error-handling)
24
+ - [Retry hook](#retry-hook)
25
+ - [Edge runtimes](#edge-runtimes)
26
+ - [Use cases](#use-cases)
27
+ - [Development](#development)
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install @openserp/sdk
33
+ ```
34
+
35
+ ```bash
36
+ yarn add @openserp/sdk
37
+ ```
38
+
39
+ ```bash
40
+ pnpm add @openserp/sdk
41
+ ```
42
+
43
+ ```bash
44
+ bun add @openserp/sdk
45
+ ```
46
+
47
+ ESM and CommonJS builds are both shipped. Requires Node 18+ and a global `fetch` (any modern runtime).
48
+
49
+ ## Quickstart — OSS (self-hosted)
50
+
51
+ Run the open source server locally, no API key required:
52
+
53
+ ```bash
54
+ docker run -p 7000:7000 karust/openserp serve
55
+ ```
56
+
57
+ ```ts
58
+ import { OpenSERP } from "@openserp/sdk";
59
+
60
+ const client = new OpenSERP({
61
+ baseUrl: "http://localhost:7000",
62
+ });
63
+
64
+ const results = await client.search({
65
+ engine: "google",
66
+ text: "openserp",
67
+ limit: 10,
68
+ region: "US",
69
+ lang: "EN",
70
+ });
71
+
72
+ console.log(results.results[0]?.title);
73
+ ```
74
+
75
+ ## Quickstart — Cloud
76
+
77
+ Get an API key at [dashboard](https://openserp.org/dashboard/keys). When `apiKey` is set, the SDK defaults `baseUrl` to `https://api.openserp.org/v1` and sends `Authorization: Bearer ...` for you.
78
+
79
+ ```ts
80
+ import { OpenSERP } from "@openserp/sdk";
81
+
82
+ const client = new OpenSERP({
83
+ apiKey: process.env.OPENSERP_KEY,
84
+ timeoutMs: 30_000,
85
+ });
86
+
87
+ const results = await client.search({
88
+ engine: "google",
89
+ text: "openserp",
90
+ });
91
+
92
+ console.log(client.lastResponse?.credits); // { used, remaining }
93
+ ```
94
+
95
+ If both `baseUrl` and `apiKey` are set, `baseUrl` wins and the key is still sent. Use this for an authenticated self-hosted deployment.
96
+
97
+ ## Why two backends?
98
+
99
+ OpenSERP Cloud is the same HTTP contract as the OSS server, with `/v1/` prefix and bearer auth. The same SDK call works on both — you only change `baseUrl` / `apiKey`. Start with OSS for free, move to Cloud when you need managed proxies, captcha handling, and pre-warmed routing without the operational work. See [openserp.org/docs/oss-vs-cloud](https://openserp.org/docs/oss-vs-cloud) for the full comparison and an honest take on alternatives like SerpAPI and DataForSEO.
100
+
101
+ ## Search
102
+
103
+ ```ts
104
+ const single = await client.search({
105
+ engine: "bing",
106
+ text: "golang",
107
+ limit: 10,
108
+ region: "US",
109
+ });
110
+
111
+ const mega = await client.megaSearch({
112
+ text: "golang",
113
+ engines: ["google", "bing", "yandex"],
114
+ mode: "balanced",
115
+ limit: 20,
116
+ });
117
+
118
+ const fast = await client.fastSearch({
119
+ text: "golang",
120
+ engines: ["google", "bing"],
121
+ });
122
+
123
+ const any = await client.anySearch({
124
+ text: "golang",
125
+ engines: ["google", "yandex"],
126
+ });
127
+ ```
128
+
129
+ `megaSearch` aggregates multiple engines. `mode` is `"balanced"` (default), `"any"` (first to respond wins), or `"fast"` (race with a tight cutoff). `fastSearch` / `anySearch` are sugar for the matching mode.
130
+
131
+ ## Images
132
+
133
+ ```ts
134
+ const images = await client.image({
135
+ engine: "bing",
136
+ text: "golang logo",
137
+ limit: 20,
138
+ });
139
+
140
+ const megaImages = await client.megaImage({
141
+ text: "golang logo",
142
+ engines: ["bing", "google"],
143
+ });
144
+ ```
145
+
146
+ ## Endpoint availability
147
+
148
+ OSS-only operational methods throw `OssOnlyError` when the client is configured for Cloud:
149
+
150
+ ```ts
151
+ await client.parseGoogle({ html: "<html>...</html>" });
152
+ await client.stats();
153
+ await client.health();
154
+ ```
155
+
156
+ Cloud-only account methods throw `CloudOnlyError` when the client is configured for OSS:
157
+
158
+ ```ts
159
+ await client.me();
160
+ await client.pricing();
161
+ await client.enginesStatus();
162
+ await client.enginesCapabilities();
163
+ ```
164
+
165
+ The backend is inferred from `baseUrl` and `apiKey`. Override explicitly when needed:
166
+
167
+ ```ts
168
+ client.config.backend = "oss";
169
+ ```
170
+
171
+ ## Telemetry
172
+
173
+ `client.lastResponse` is updated after each HTTP response:
174
+
175
+ ```ts
176
+ console.log(client.lastResponse?.credits); // Cloud — { used, remaining }
177
+ console.log(client.lastResponse?.engineUsed); // both — X-Engine-Used
178
+ console.log(client.lastResponse?.fallbackEngine); // OSS only
179
+ console.log(client.lastResponse?.cache); // OSS only
180
+ ```
181
+
182
+ `fallbackEngine` and `cache` are deliberately hidden by Cloud — they reveal internal routing and cache signal — so expect them to be `undefined` against `api.openserp.org`. `credits` is the inverse: only present when talking to Cloud.
183
+
184
+ ## Error handling
185
+
186
+ ```ts
187
+ import { CaptchaError, RateLimitError, SERPError } from "@openserp/sdk";
188
+
189
+ try {
190
+ await client.search({ engine: "google", text: "openserp" });
191
+ } catch (err) {
192
+ if (err instanceof RateLimitError) {
193
+ // slow down or queue the request
194
+ } else if (err instanceof CaptchaError) {
195
+ // OSS can surface upstream captcha challenges; on Cloud this is handled for you
196
+ } else if (err instanceof SERPError) {
197
+ console.error(err.status, err.code, err.reason, err.requestId);
198
+ }
199
+ }
200
+ ```
201
+
202
+ ## Retry hook
203
+
204
+ The SDK does not apply a retry policy. Provide a hook when you want one:
205
+
206
+ ```ts
207
+ const client = new OpenSERP({
208
+ baseUrl: "http://localhost:7000",
209
+ retry: (err, attempt) => {
210
+ return err instanceof SERPError && err.status >= 500 && attempt < 2;
211
+ },
212
+ });
213
+ ```
214
+
215
+ ## Edge runtimes
216
+
217
+ The SDK uses global `fetch` and has no Node-only runtime dependency. It runs on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.
218
+
219
+ Cloudflare Workers:
220
+
221
+ ```ts
222
+ import { OpenSERP } from "@openserp/sdk";
223
+
224
+ export default {
225
+ async fetch(_request: Request, env: { OPENSERP_KEY: string }) {
226
+ const client = new OpenSERP({ apiKey: env.OPENSERP_KEY });
227
+ const results = await client.fastSearch({ text: "openserp" });
228
+ return Response.json(results);
229
+ },
230
+ };
231
+ ```
232
+
233
+ Vercel Edge:
234
+
235
+ ```ts
236
+ import { OpenSERP } from "@openserp/sdk";
237
+
238
+ export const runtime = "edge";
239
+
240
+ export async function GET() {
241
+ const client = new OpenSERP({ apiKey: process.env.OPENSERP_KEY });
242
+ return Response.json(await client.search({ engine: "google", text: "openserp" }));
243
+ }
244
+ ```
245
+
246
+ ## Use cases
247
+
248
+ - **AI grounding / RAG** — feed top-N search results into an LLM prompt (OpenAI, Anthropic, Ollama) for up-to-date answers.
249
+ - **LLM tool use** — expose `client.search` as a tool to your agent, or use the dedicated [`@openserp/mcp`](https://www.npmjs.com/package/@openserp/mcp) server in Claude Desktop, Cursor, Cline, and Windsurf.
250
+ - **SEO monitoring** — daily rank tracking across multiple engines and regions, export to Sheets or Notion.
251
+ - **Competitor analysis** — weekly diff of top-10 results for a keyword set.
252
+ - **Automations** — drop the [`n8n-nodes-openserp`](https://www.npmjs.com/package/n8n-nodes-openserp) node into a workflow.
253
+ - **Data pipelines** — stream SERPs to ClickHouse, BigQuery, or a DataFrame for NLP on snippets.
254
+
255
+ ## Development
256
+
257
+ ```bash
258
+ corepack enable
259
+ corepack pnpm install
260
+ corepack pnpm test
261
+ corepack pnpm build
262
+ ```
263
+
264
+ Regenerate OSS API types after changing `openserp/docs/openapi.yaml`:
265
+
266
+ ```bash
267
+ corepack pnpm generate:types
268
+ ```
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/generated/oss.ts
17
+ var oss_exports = {};
18
+ module.exports = __toCommonJS(oss_exports);
19
+ //# sourceMappingURL=oss.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/generated/oss.ts"],"sourcesContent":["/**\n * This file was auto-generated by openapi-typescript.\n * Do not make direct changes to the file.\n */\n\nexport interface paths {\n \"/{engine}/search\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /**\n * Search web results from a specific engine\n * @description Engine path values are `google`, `yandex`, `baidu`, `bing`, `duck`, and `ecosia` (`duck` maps to DuckDuckGo internally). Use `?format=markdown|text|ndjson` for alternative output formats.\n */\n get: operations[\"searchWeb\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/{engine}/image\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Search image results from a specific engine */\n get: operations[\"searchImages\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/google/parse\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n get?: never;\n put?: never;\n /**\n * Parse a Google SERP HTML document into structured results\n * @description Accepts raw Google SERP HTML in the request body and returns a standard search envelope. Useful when an upstream provider delivers raw HTML rather than JSON. No browser is used; parsing is done with goquery. The body size limit is 10 MB.\n */\n post: operations[\"parseGoogleHTML\"];\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/bing/parse\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n get?: never;\n put?: never;\n /**\n * Parse a Bing SERP HTML document into structured results\n * @description Accepts raw Bing SERP HTML in the request body and returns a standard search envelope. Useful when an upstream provider delivers raw HTML rather than JSON. No browser is used; parsing is done with goquery. The body size limit is 10 MB.\n */\n post: operations[\"parseBingHTML\"];\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/mega/search\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /**\n * Search across multiple engines with selectable execution mode\n * @description Mode controls engine execution strategy: `balanced` (default) queries all selected engines in parallel, `any` runs engines sequentially in requested order until first success, and `fast` queries only the fastest engine based on circuit-breaker average response time stats. In `balanced` mode, `dedupe` and `merge` tune aggregation behavior. Partial failures are surfaced in `meta.engines_failed` and `meta.engine_errors`. If all selected engines fail, the endpoint returns a 502 with per-engine error details. Use `?format=markdown|text|ndjson` for alternative output formats.\n */\n get: operations[\"megaSearch\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/mega/image\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Image search across multiple engines with selectable execution mode */\n get: operations[\"megaImageSearch\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/mega/engines\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** List available engines and runtime state */\n get: operations[\"listMegaEngines\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/health\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Service health status */\n get: operations[\"healthCheck\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/ready\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Service readiness status */\n get: operations[\"readinessCheck\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/stats\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Combined cache, proxy, and circuit-breaker stats */\n get: operations[\"getStats\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/stats/cache\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Cache statistics only */\n get: operations[\"getCacheStats\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/stats/proxy\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Proxy pool and per-engine proxy policy statistics */\n get: operations[\"getProxyStats\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/stats/cb\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Circuit breaker state per engine */\n get: operations[\"getCircuitBreakerStats\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/openapi.yaml\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Get raw OpenAPI YAML */\n get: operations[\"getOpenAPISpec\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n \"/docs\": {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n /** Swagger UI for interactive API docs */\n get: operations[\"getSwaggerUI\"];\n put?: never;\n post?: never;\n delete?: never;\n options?: never;\n head?: never;\n patch?: never;\n trace?: never;\n };\n}\nexport type webhooks = Record<string, never>;\nexport interface components {\n schemas: {\n QueryEcho: {\n /** @example golang */\n text: string;\n /** @example EN */\n lang?: string;\n /** @example US */\n region?: string;\n /**\n * @example [\n * \"google\"\n * ]\n */\n engines_requested: string[];\n };\n ResponseMeta: {\n /** @example 01HXYZ... */\n request_id: string;\n /**\n * Format: date-time\n * @example 2026-04-24T12:00:00Z\n */\n requested_at: string;\n /** @example 842 */\n took_ms: number;\n /** @example [] */\n engines_failed: string[];\n /** @description Sanitized per-engine failures for mega endpoints. */\n engine_errors?: components[\"schemas\"][\"EngineErrorDetail\"][];\n /** @example 2.1 */\n version: string;\n };\n EngineErrorDetail: {\n /** @example bing */\n engine: string;\n /** @example blocked */\n error: string;\n /**\n * @description Sanitized detail; proxy credentials are never included.\n * @example blocked: 403\n */\n message?: string;\n };\n Pagination: {\n /** @example 1 */\n page: number;\n /** @example true */\n has_more: boolean;\n /** @example 25 */\n next_start: number;\n };\n Position: {\n /**\n * @description 1-based rank in the mixed SERP stream, across both organic and ad blocks. Always present so SEO callers can plot rank vs. on-page position without inferring it from result order.\n * @example 2\n */\n absolute: number;\n };\n DomainInfo: {\n /** @example org */\n tld?: string;\n /** @example wikipedia */\n sld?: string;\n /**\n * @description Empty string when the domain matches no known category.\n * @enum {string}\n */\n category: \"\" | \"gov\" | \"edu\" | \"mil\" | \"news\" | \"forum\" | \"marketplace\" | \"social\";\n };\n Classification: {\n /**\n * @example article\n * @enum {string}\n */\n content_type?: \"article\" | \"document\" | \"video\" | \"forum_thread\" | \"webpage\";\n /** @example encyclopedia */\n source_hint?: string;\n };\n /**\n * @description SERP block type. New values require a minor version bump (`meta.version: \"2.1\"`). In v2.1, engines may emit specialized modules such as `people_also_ask` when a parser can classify them reliably.\n * @enum {string}\n */\n ResultType: \"organic\" | \"ad\" | \"featured_snippet\" | \"knowledge_panel\" | \"people_also_ask\" | \"video\" | \"image\" | \"news\" | \"shopping\" | \"local\" | \"answer_box\";\n Result: {\n /**\n * @description Stable identifier: `s_` + hex(first 8 bytes of MD5(engine|normalized_url)). Normalized URL: lowercase scheme+host, trailing slash stripped, utm_*\\/fbclid/gclid tracking params removed. Same URL → same ID across requests.\n * @example s_a1b2c3d4e5f6a1b2\n */\n id: string;\n /** @example 1 */\n rank: number;\n type: components[\"schemas\"][\"ResultType\"];\n /** @example The Go Programming Language */\n title: string;\n /** @example https://go.dev/ */\n url: string;\n /**\n * @description Breadcrumb form of the URL (e.g. `go.dev › doc › install`).\n * @example go.dev\n */\n display_url: string;\n /** @example Go is an open source programming language... */\n snippet: string;\n /**\n * @description Registrable domain with leading `www.` stripped.\n * @example go.dev\n */\n domain: string;\n /**\n * @description Constructed as `https://{domain}/favicon.ico`. Not probed.\n * @example https://go.dev/favicon.ico\n */\n favicon: string;\n position: components[\"schemas\"][\"Position\"];\n /** @example google */\n engine: string;\n domain_info?: components[\"schemas\"][\"DomainInfo\"];\n classification?: components[\"schemas\"][\"Classification\"];\n };\n ImageData: {\n /** @example https://example.com/gopher.png */\n url: string;\n /** @example https://example.com/gopher-thumb.png */\n thumbnail?: string;\n /** @example 1200 */\n width?: number;\n /** @example 800 */\n height?: number;\n };\n ImageSource: {\n /** @example https://example.com/article-about-gophers */\n page_url: string;\n /** @example example.com */\n domain: string;\n };\n ImageResult: {\n /**\n * @description Stable identifier prefixed with `i_`.\n * @example i_a1b2c3d4e5f6a1b2\n */\n id: string;\n /** @example 1 */\n rank: number;\n /** @enum {string} */\n type: \"image\";\n title: string;\n image: components[\"schemas\"][\"ImageData\"];\n source: components[\"schemas\"][\"ImageSource\"];\n engine: string;\n };\n ClusterOccurrence: {\n /** @example google */\n engine: string;\n /** @example 1 */\n rank: number;\n /** @example s_a1b2c3d4e5f6a1b2 */\n result_id: string;\n };\n Cluster: {\n /**\n * @description Stable identifier: `c_` + hex(first 8 bytes of MD5(normalized_url)).\n * @example c_a1b2c3d4e5f6a1b2\n */\n id: string;\n /** @example https://go.dev/ */\n canonical_url: string;\n /** @example go.dev */\n domain: string;\n /** @example The Go Programming Language */\n title: string;\n occurrences: components[\"schemas\"][\"ClusterOccurrence\"][];\n /**\n * @description Number of engines this URL appeared in.\n * @example 3\n */\n engines_count: number;\n /**\n * @description Lowest (best) rank seen across all engines.\n * @example 1\n */\n best_rank: number;\n /**\n * Format: float\n * @description Cross-engine agreement score: sum(1/rank for each occurrence) / engines_queried, capped at 1.0. Higher is better.\n * @example 0.92\n */\n score: number;\n };\n SearchEnvelope: {\n query: components[\"schemas\"][\"QueryEcho\"];\n meta: components[\"schemas\"][\"ResponseMeta\"];\n results: components[\"schemas\"][\"Result\"][];\n pagination: components[\"schemas\"][\"Pagination\"];\n };\n MegaSearchEnvelope: components[\"schemas\"][\"SearchEnvelope\"] & {\n /** @description Cross-engine clusters, sorted by score descending. Only present on /mega/search responses. Absent (not null) on single-engine endpoints. */\n clusters?: components[\"schemas\"][\"Cluster\"][] | null;\n };\n ImageEnvelope: {\n query: components[\"schemas\"][\"QueryEcho\"];\n meta: components[\"schemas\"][\"ResponseMeta\"];\n results: components[\"schemas\"][\"ImageResult\"][];\n pagination: components[\"schemas\"][\"Pagination\"];\n };\n ErrorResponse: {\n /**\n * @description Stable machine-readable error class. Search-pipeline failures use the following codes: `captcha_detected`, `blocked`, `rate_limited`, `search_timeout`, `proxy_connect`, `proxy_auth`, `proxy_timeout`, `proxy_unavailable`, `parser_failure`, `engine_internal`, `all_engines_failed`, `circuit_open`, `request_timeout`, `request_canceled`. Validation errors use `bad_request`. Other generic codes (`not_found`, `service_unavailable`, `server_error`, `client_error`, `error`) may appear for non-search routes.\n * @example bad_request\n * @enum {string}\n */\n error: \"bad_request\" | \"not_found\" | \"rate_limited\" | \"service_unavailable\" | \"server_error\" | \"client_error\" | \"error\" | \"captcha_detected\" | \"blocked\" | \"search_timeout\" | \"proxy_connect\" | \"proxy_auth\" | \"proxy_timeout\" | \"proxy_unavailable\" | \"parser_failure\" | \"engine_internal\" | \"all_engines_failed\" | \"circuit_open\" | \"request_timeout\" | \"request_canceled\";\n /** @example 400 */\n code: number;\n /**\n * @description Matches the `X-Request-ID` response header.\n * @example 01HXYZ...\n */\n request_id?: string;\n /** @example INVALID_LIMIT: limit must be between 1 and 100 */\n message?: string;\n /**\n * @description Stable client-actionable reason code. Present on 400 errors. Known values: INVALID_LIMIT, INVALID_START, INVALID_PARAM, EMPTY_QUERY, NO_ENGINES, UNKNOWN_FORMAT, REQUEST_PROXY_URL_DISABLED, UNSUPPORTED_PROXY_SCHEME.\n * @example INVALID_LIMIT\n */\n reason?: string;\n /** @description Sanitized context for search-pipeline errors. Credentials are never included. */\n meta?: {\n /** @example google */\n engine?: string;\n /**\n * @description Masked `scheme://host:port`; never includes credentials.\n * @example http://proxy.example:8080\n */\n proxy_used?: string;\n /** @example us */\n proxy_country?: string;\n /** @example residential */\n proxy_class?: string;\n /** @example webshare */\n proxy_provider?: string;\n /** @example sid-123 */\n proxy_session_id?: string;\n /**\n * @description Sanitized underlying error detail when available.\n * @example proxy_connect: dial tcp proxy.example:8080: connection refused\n */\n error_detail?: string;\n engine_errors?: components[\"schemas\"][\"EngineErrorDetail\"][];\n } & {\n [key: string]: unknown;\n };\n };\n EngineHealth: {\n /** @example google */\n name: string;\n /** @example true */\n initialized: boolean;\n /** @enum {string} */\n status: \"ready\" | \"not_initialized\" | \"circuit_open\";\n };\n HealthStatus: {\n /** @enum {string} */\n status: \"healthy\" | \"degraded\" | \"unhealthy\";\n /** @example 1h12m3s */\n uptime: string;\n engines: components[\"schemas\"][\"EngineHealth\"][];\n system: {\n [key: string]: unknown;\n };\n };\n ReadinessStatus: {\n /** @enum {string} */\n status: \"ready\" | \"draining\";\n };\n CacheStatsEnabled: {\n /** @enum {boolean} */\n status: true;\n entries: number;\n hits: number;\n misses: number;\n bypasses: number;\n evictions: number;\n ttl_seconds: number;\n max_size: number;\n };\n CacheStatsDisabled: {\n /** @enum {boolean} */\n status: false;\n };\n CacheStats: components[\"schemas\"][\"CacheStatsEnabled\"] | components[\"schemas\"][\"CacheStatsDisabled\"];\n ProxyTagSummary: {\n configured: number;\n healthy: number;\n };\n ProxyStatsEntry: {\n proxy: string;\n tags: string[];\n healthy: boolean;\n failures: number;\n disabled: boolean;\n };\n ProxyEngineStats: {\n tag?: string;\n selected_proxy: string;\n };\n /** @description Sticky proxy lane state observed by this worker. */\n LaneStats: {\n /**\n * @description Number of lanes currently held by the worker.\n * @example 12\n */\n active: number;\n /**\n * @description Lanes evicted by the LRU bound since worker start.\n * @example 7\n */\n evicted_lru: number;\n /**\n * @description Lane cookie drops triggered by captcha/challenge responses.\n * @example 20\n */\n cookies_dropped: number;\n };\n /** @description Live state of the per-process Chrome pool. Each authenticated upstream proxy identity (scheme+host+port+username) gets a dedicated Chrome so Chrome can answer 407 challenges natively. Direct and unauthenticated proxies share one Chrome with per-BrowserContext proxy override. */\n BrowserPoolStats: {\n /**\n * @description Number of Chrome processes currently held by the pool.\n * @example 3\n */\n active: number;\n /**\n * @description Configured `app.max_processes` LRU cap.\n * @example 4\n */\n max: number;\n /**\n * @description Chrome processes closed because the LRU cap was exceeded.\n * @example 12\n */\n evicted_lru: number;\n /**\n * @description Chrome processes closed by the idle sweeper after `app.idle_ttl`.\n * @example 5\n */\n evicted_idle: number;\n };\n ProxyStats: {\n configured_count: number;\n healthy_count: number;\n unhealthy_count: number;\n /** @description Whether `proxies.allow_request_proxy_url` is enabled on this worker. */\n request_proxy_url_enabled: boolean;\n lanes: components[\"schemas\"][\"LaneStats\"];\n browser_processes: components[\"schemas\"][\"BrowserPoolStats\"];\n tags: {\n [key: string]: components[\"schemas\"][\"ProxyTagSummary\"];\n };\n entries: components[\"schemas\"][\"ProxyStatsEntry\"][];\n engines?: {\n [key: string]: components[\"schemas\"][\"ProxyEngineStats\"];\n };\n };\n CircuitBreakerStat: {\n engine: string;\n /** @enum {string} */\n state: \"closed\" | \"open\" | \"half-open\";\n failure_count: number;\n /** Format: date-time */\n last_changed: string;\n /** @description Seconds until next half-open attempt (present when state is open). */\n retry_in?: number;\n /** @description Average successful engine response time in milliseconds. */\n avg_response_ms?: number;\n };\n CircuitBreakerStatsResponse: {\n circuit_breakers: components[\"schemas\"][\"CircuitBreakerStat\"][];\n };\n StatsResponse: {\n cache: components[\"schemas\"][\"CacheStats\"];\n proxy: components[\"schemas\"][\"ProxyStats\"];\n circuit_breakers: components[\"schemas\"][\"CircuitBreakerStat\"][];\n };\n MegaEngineInfo: {\n name: string;\n initialized: boolean;\n /** @enum {string} */\n circuit_state?: \"closed\" | \"open\" | \"half-open\";\n };\n MegaEnginesResponse: {\n engines: components[\"schemas\"][\"MegaEngineInfo\"][];\n total: number;\n };\n };\n responses: {\n /** @description Invalid request parameters */\n BadRequestError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description The search engine blocked the request */\n ForbiddenError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description Captcha challenge or rate-limit response from the search engine */\n TooManyRequestsError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description Engine internal failure, parser drift, or all-engine failure when fallback is enabled. */\n BadGatewayError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description Search timed out waiting for required SERP elements */\n GatewayTimeoutError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description Proxy-layer failure (no healthy proxy or transport error). The search itself was not produced. */\n ServiceUnavailableError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description Endpoint not found */\n NotFoundError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n /** @description Internal error while handling request */\n InternalServerError: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ErrorResponse\"];\n };\n };\n };\n parameters: {\n /** @description Search engine endpoint alias (`duck` is DuckDuckGo). */\n EnginePath: \"google\" | \"yandex\" | \"baidu\" | \"bing\" | \"duck\" | \"ecosia\";\n /**\n * @description Search query text. At least one of `text`, `site`, or `file` must be non-empty.\n * @example golang\n */\n TextQuery: string;\n /**\n * @description Language code (engine-specific behavior).\n * @example EN\n */\n LangQuery: string;\n /** @description Market/location hint. Yandex accepts numeric `lr` region IDs such as `213`; Google, Bing, and DuckDuckGo use country/locale-style hints such as `RU`, `US`, or `en-GB` where supported. */\n RegionQuery: string;\n /**\n * @description Date interval in `YYYYMMDD..YYYYMMDD` format.\n * @example 20250101..20250131\n */\n DateQuery: string;\n /**\n * @description File extension filter (for engines that support it).\n * @example PDF\n */\n FileQuery: string;\n /**\n * @description Site/domain filter.\n * @example github.com\n */\n SiteQuery: string;\n /**\n * @description Maximum organic results to return (1–100). Ads may be returned in addition.\n * @example 10\n */\n LimitQuery: number;\n /**\n * @description Pagination offset (must be >= 0).\n * @example 20\n */\n StartQuery: number;\n /** @description Duplicate filtering flag (primarily used by Google parser behavior). */\n FilterQuery: boolean;\n /** @description Include answer box style results when supported. */\n AnswersQuery: boolean;\n /**\n * @description Comma-separated engine list for mega endpoints. If omitted, all available engines are used.\n * @example google,bing,duckduckgo\n */\n EnginesQuery: string;\n /** @description Mega execution mode. `balanced` (default) runs all selected engines in parallel. `any` runs selected engines sequentially in request order until first success. `fast` runs only one engine: the fastest by circuit-breaker average response time. */\n MegaModeQuery: \"balanced\" | \"any\" | \"fast\";\n /** @description Enable deduplication by normalized URL. Default `true`. */\n MegaDedupeQuery: boolean;\n /** @description Merge results from all successful engines into one flat list. Default `true`. When `false`, only the first requested engine that returned results is kept. */\n MegaMergeQuery: boolean;\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n FormatQuery: \"json\" | \"markdown\" | \"text\" | \"ndjson\";\n /** @description Request-scoped proxy override. Use `direct` to disable proxy or a tag name to force a specific proxy pool. */\n UseProxyHeader: string;\n /**\n * @description Per-request proxy URL supplied by an upstream balancer. Honored only when `proxies.allow_request_proxy_url: true` is set on the worker; otherwise the request is rejected with `400 bad_request` and `reason=REQUEST_PROXY_URL_DISABLED`. Authenticated SOCKS proxies are rejected in browser mode (`reason=UNSUPPORTED_PROXY_SCHEME`). Credentials are never logged or returned. Precedence: `X-Use-Proxy: direct` > `X-Proxy-URL` > `X-Use-Proxy: <tag>` > per-engine configured tag > `proxies.global` > direct.\n * @example http://user:pass@proxy.example:8080\n */\n ProxyURLHeader: string;\n /**\n * @description Two-letter market country code for the supplied proxy. Used as part of the cache key so different markets do not share results.\n * @example us\n */\n ProxyCountryHeader: string;\n /**\n * @description Proxy class identifier (e.g. `datacenter`, `residential`, `mobile`). Part of the cache key.\n * @example residential\n */\n ProxyClassHeader: string;\n /**\n * @description Upstream proxy provider identifier (e.g. `webshare`, `brightdata`). Part of the cache key.\n * @example webshare\n */\n ProxyProviderHeader: string;\n /**\n * @description Sticky session identifier minted by the balancer. Reusing the same value lets OpenSERP reuse cookies and browser profile for that lane. Lanes are LRU-bounded by `proxies.lanes.max_lanes`. Rotating the session ID gives a clean lane.\n * @example sid-123\n */\n ProxySessionIDHeader: string;\n /** @description Optional tenant scope used to namespace sticky lane state across multi-tenant deployments. When present, lanes are keyed by `tenant + engine + session_id`. */\n TenantHeader: string;\n };\n requestBodies: never;\n headers: {\n /** @description UUID v7 request identifier. Matches `meta.request_id` in the response body and appears in server logs for correlation. */\n XRequestID: string;\n /** @description Cache status when cache is enabled (`HIT`, `MISS`, `BYPASS`). */\n XCache: \"HIT\" | \"MISS\" | \"BYPASS\";\n /** @description Engine name used when dedicated endpoint fallback served the response. */\n XFallbackEngine: string;\n /** @description Effective proxy mode for the request. `request_url` indicates a per-request `X-Proxy-URL` was honored. */\n XProxyMode: \"off\" | \"tag_pool\" | \"request_url\";\n /** @description Effective proxy tag when `X-Proxy-Mode=tag_pool`. Header is omitted when no tag is in effect (i.e. `X-Proxy-Mode` is `request_url` or `off`). */\n XProxyTag: string;\n /** @description Effective proxy target used. Values: `direct`, masked `scheme://host:port` URL, `pooled`, `multiple`, or `mixed`. Credentials are never included. */\n XProxyUsed: string;\n /** @description Aggregate inbound network bytes consumed while executing the search request. Single-engine endpoints report that engine's request bytes; mega endpoints report the sum across selected engines. Cache hits return `0`. */\n XNetworkBytes: number;\n /** @description Browser profile ID selected for browser-mode execution. Mega endpoints may return a comma-separated list when multiple profiles were used. */\n XBrowserProfileID: string;\n };\n pathItems: never;\n}\nexport type $defs = Record<string, never>;\nexport interface operations {\n searchWeb: {\n parameters: {\n query?: {\n /**\n * @description Search query text. At least one of `text`, `site`, or `file` must be non-empty.\n * @example golang\n */\n text?: components[\"parameters\"][\"TextQuery\"];\n /**\n * @description Language code (engine-specific behavior).\n * @example EN\n */\n lang?: components[\"parameters\"][\"LangQuery\"];\n /** @description Market/location hint. Yandex accepts numeric `lr` region IDs such as `213`; Google, Bing, and DuckDuckGo use country/locale-style hints such as `RU`, `US`, or `en-GB` where supported. */\n region?: components[\"parameters\"][\"RegionQuery\"];\n /**\n * @description Date interval in `YYYYMMDD..YYYYMMDD` format.\n * @example 20250101..20250131\n */\n date?: components[\"parameters\"][\"DateQuery\"];\n /**\n * @description File extension filter (for engines that support it).\n * @example PDF\n */\n file?: components[\"parameters\"][\"FileQuery\"];\n /**\n * @description Site/domain filter.\n * @example github.com\n */\n site?: components[\"parameters\"][\"SiteQuery\"];\n /**\n * @description Maximum organic results to return (1–100). Ads may be returned in addition.\n * @example 10\n */\n limit?: components[\"parameters\"][\"LimitQuery\"];\n /**\n * @description Pagination offset (must be >= 0).\n * @example 20\n */\n start?: components[\"parameters\"][\"StartQuery\"];\n /** @description Duplicate filtering flag (primarily used by Google parser behavior). */\n filter?: components[\"parameters\"][\"FilterQuery\"];\n /** @description Include answer box style results when supported. */\n answers?: components[\"parameters\"][\"AnswersQuery\"];\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n format?: components[\"parameters\"][\"FormatQuery\"];\n };\n header?: {\n /** @description Request-scoped proxy override. Use `direct` to disable proxy or a tag name to force a specific proxy pool. */\n \"X-Use-Proxy\"?: components[\"parameters\"][\"UseProxyHeader\"];\n /**\n * @description Per-request proxy URL supplied by an upstream balancer. Honored only when `proxies.allow_request_proxy_url: true` is set on the worker; otherwise the request is rejected with `400 bad_request` and `reason=REQUEST_PROXY_URL_DISABLED`. Authenticated SOCKS proxies are rejected in browser mode (`reason=UNSUPPORTED_PROXY_SCHEME`). Credentials are never logged or returned. Precedence: `X-Use-Proxy: direct` > `X-Proxy-URL` > `X-Use-Proxy: <tag>` > per-engine configured tag > `proxies.global` > direct.\n * @example http://user:pass@proxy.example:8080\n */\n \"X-Proxy-URL\"?: components[\"parameters\"][\"ProxyURLHeader\"];\n /**\n * @description Two-letter market country code for the supplied proxy. Used as part of the cache key so different markets do not share results.\n * @example us\n */\n \"X-Proxy-Country\"?: components[\"parameters\"][\"ProxyCountryHeader\"];\n /**\n * @description Proxy class identifier (e.g. `datacenter`, `residential`, `mobile`). Part of the cache key.\n * @example residential\n */\n \"X-Proxy-Class\"?: components[\"parameters\"][\"ProxyClassHeader\"];\n /**\n * @description Upstream proxy provider identifier (e.g. `webshare`, `brightdata`). Part of the cache key.\n * @example webshare\n */\n \"X-Proxy-Provider\"?: components[\"parameters\"][\"ProxyProviderHeader\"];\n /**\n * @description Sticky session identifier minted by the balancer. Reusing the same value lets OpenSERP reuse cookies and browser profile for that lane. Lanes are LRU-bounded by `proxies.lanes.max_lanes`. Rotating the session ID gives a clean lane.\n * @example sid-123\n */\n \"X-Proxy-Session-ID\"?: components[\"parameters\"][\"ProxySessionIDHeader\"];\n /** @description Optional tenant scope used to namespace sticky lane state across multi-tenant deployments. When present, lanes are keyed by `tenant + engine + session_id`. */\n \"X-Tenant\"?: components[\"parameters\"][\"TenantHeader\"];\n };\n path: {\n /** @description Search engine endpoint alias (`duck` is DuckDuckGo). */\n engine: components[\"parameters\"][\"EnginePath\"];\n };\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Search results envelope */\n 200: {\n headers: {\n \"X-Request-ID\": components[\"headers\"][\"XRequestID\"];\n \"X-Cache\": components[\"headers\"][\"XCache\"];\n \"X-Fallback-Engine\": components[\"headers\"][\"XFallbackEngine\"];\n \"X-Proxy-Mode\": components[\"headers\"][\"XProxyMode\"];\n \"X-Proxy-Tag\": components[\"headers\"][\"XProxyTag\"];\n \"X-Proxy-Used\": components[\"headers\"][\"XProxyUsed\"];\n \"X-Network-Bytes\": components[\"headers\"][\"XNetworkBytes\"];\n \"X-Browser-Profile-Id\": components[\"headers\"][\"XBrowserProfileID\"];\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"SearchEnvelope\"];\n \"text/markdown\": string;\n \"text/plain\": string;\n \"application/x-ndjson\": string;\n };\n };\n 400: components[\"responses\"][\"BadRequestError\"];\n 403: components[\"responses\"][\"ForbiddenError\"];\n 404: components[\"responses\"][\"NotFoundError\"];\n 429: components[\"responses\"][\"TooManyRequestsError\"];\n 500: components[\"responses\"][\"InternalServerError\"];\n 502: components[\"responses\"][\"BadGatewayError\"];\n 503: components[\"responses\"][\"ServiceUnavailableError\"];\n 504: components[\"responses\"][\"GatewayTimeoutError\"];\n };\n };\n searchImages: {\n parameters: {\n query?: {\n /**\n * @description Search query text. At least one of `text`, `site`, or `file` must be non-empty.\n * @example golang\n */\n text?: components[\"parameters\"][\"TextQuery\"];\n /**\n * @description Language code (engine-specific behavior).\n * @example EN\n */\n lang?: components[\"parameters\"][\"LangQuery\"];\n /** @description Market/location hint. Yandex accepts numeric `lr` region IDs such as `213`; Google, Bing, and DuckDuckGo use country/locale-style hints such as `RU`, `US`, or `en-GB` where supported. */\n region?: components[\"parameters\"][\"RegionQuery\"];\n /**\n * @description Date interval in `YYYYMMDD..YYYYMMDD` format.\n * @example 20250101..20250131\n */\n date?: components[\"parameters\"][\"DateQuery\"];\n /**\n * @description File extension filter (for engines that support it).\n * @example PDF\n */\n file?: components[\"parameters\"][\"FileQuery\"];\n /**\n * @description Site/domain filter.\n * @example github.com\n */\n site?: components[\"parameters\"][\"SiteQuery\"];\n /**\n * @description Maximum organic results to return (1–100). Ads may be returned in addition.\n * @example 10\n */\n limit?: components[\"parameters\"][\"LimitQuery\"];\n /**\n * @description Pagination offset (must be >= 0).\n * @example 20\n */\n start?: components[\"parameters\"][\"StartQuery\"];\n /** @description Duplicate filtering flag (primarily used by Google parser behavior). */\n filter?: components[\"parameters\"][\"FilterQuery\"];\n /** @description Include answer box style results when supported. */\n answers?: components[\"parameters\"][\"AnswersQuery\"];\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n format?: components[\"parameters\"][\"FormatQuery\"];\n };\n header?: {\n /** @description Request-scoped proxy override. Use `direct` to disable proxy or a tag name to force a specific proxy pool. */\n \"X-Use-Proxy\"?: components[\"parameters\"][\"UseProxyHeader\"];\n /**\n * @description Per-request proxy URL supplied by an upstream balancer. Honored only when `proxies.allow_request_proxy_url: true` is set on the worker; otherwise the request is rejected with `400 bad_request` and `reason=REQUEST_PROXY_URL_DISABLED`. Authenticated SOCKS proxies are rejected in browser mode (`reason=UNSUPPORTED_PROXY_SCHEME`). Credentials are never logged or returned. Precedence: `X-Use-Proxy: direct` > `X-Proxy-URL` > `X-Use-Proxy: <tag>` > per-engine configured tag > `proxies.global` > direct.\n * @example http://user:pass@proxy.example:8080\n */\n \"X-Proxy-URL\"?: components[\"parameters\"][\"ProxyURLHeader\"];\n /**\n * @description Two-letter market country code for the supplied proxy. Used as part of the cache key so different markets do not share results.\n * @example us\n */\n \"X-Proxy-Country\"?: components[\"parameters\"][\"ProxyCountryHeader\"];\n /**\n * @description Proxy class identifier (e.g. `datacenter`, `residential`, `mobile`). Part of the cache key.\n * @example residential\n */\n \"X-Proxy-Class\"?: components[\"parameters\"][\"ProxyClassHeader\"];\n /**\n * @description Upstream proxy provider identifier (e.g. `webshare`, `brightdata`). Part of the cache key.\n * @example webshare\n */\n \"X-Proxy-Provider\"?: components[\"parameters\"][\"ProxyProviderHeader\"];\n /**\n * @description Sticky session identifier minted by the balancer. Reusing the same value lets OpenSERP reuse cookies and browser profile for that lane. Lanes are LRU-bounded by `proxies.lanes.max_lanes`. Rotating the session ID gives a clean lane.\n * @example sid-123\n */\n \"X-Proxy-Session-ID\"?: components[\"parameters\"][\"ProxySessionIDHeader\"];\n /** @description Optional tenant scope used to namespace sticky lane state across multi-tenant deployments. When present, lanes are keyed by `tenant + engine + session_id`. */\n \"X-Tenant\"?: components[\"parameters\"][\"TenantHeader\"];\n };\n path: {\n /** @description Search engine endpoint alias (`duck` is DuckDuckGo). */\n engine: components[\"parameters\"][\"EnginePath\"];\n };\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Image search results envelope */\n 200: {\n headers: {\n \"X-Request-ID\": components[\"headers\"][\"XRequestID\"];\n \"X-Cache\": components[\"headers\"][\"XCache\"];\n \"X-Fallback-Engine\": components[\"headers\"][\"XFallbackEngine\"];\n \"X-Proxy-Mode\": components[\"headers\"][\"XProxyMode\"];\n \"X-Proxy-Tag\": components[\"headers\"][\"XProxyTag\"];\n \"X-Proxy-Used\": components[\"headers\"][\"XProxyUsed\"];\n \"X-Network-Bytes\": components[\"headers\"][\"XNetworkBytes\"];\n \"X-Browser-Profile-Id\": components[\"headers\"][\"XBrowserProfileID\"];\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ImageEnvelope\"];\n };\n };\n 400: components[\"responses\"][\"BadRequestError\"];\n 403: components[\"responses\"][\"ForbiddenError\"];\n 404: components[\"responses\"][\"NotFoundError\"];\n 429: components[\"responses\"][\"TooManyRequestsError\"];\n 500: components[\"responses\"][\"InternalServerError\"];\n 502: components[\"responses\"][\"BadGatewayError\"];\n 503: components[\"responses\"][\"ServiceUnavailableError\"];\n 504: components[\"responses\"][\"GatewayTimeoutError\"];\n };\n };\n parseGoogleHTML: {\n parameters: {\n query?: {\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n format?: components[\"parameters\"][\"FormatQuery\"];\n };\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody: {\n content: {\n \"text/html\": string;\n };\n };\n responses: {\n /** @description Parsed search results envelope */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"SearchEnvelope\"];\n \"text/markdown\": string;\n \"text/plain\": string;\n \"application/x-ndjson\": string;\n };\n };\n 400: components[\"responses\"][\"BadRequestError\"];\n 500: components[\"responses\"][\"InternalServerError\"];\n };\n };\n parseBingHTML: {\n parameters: {\n query?: {\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n format?: components[\"parameters\"][\"FormatQuery\"];\n };\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody: {\n content: {\n \"text/html\": string;\n };\n };\n responses: {\n /** @description Parsed search results envelope */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"SearchEnvelope\"];\n \"text/markdown\": string;\n \"text/plain\": string;\n \"application/x-ndjson\": string;\n };\n };\n 400: components[\"responses\"][\"BadRequestError\"];\n 500: components[\"responses\"][\"InternalServerError\"];\n };\n };\n megaSearch: {\n parameters: {\n query?: {\n /**\n * @description Search query text. At least one of `text`, `site`, or `file` must be non-empty.\n * @example golang\n */\n text?: components[\"parameters\"][\"TextQuery\"];\n /**\n * @description Language code (engine-specific behavior).\n * @example EN\n */\n lang?: components[\"parameters\"][\"LangQuery\"];\n /** @description Market/location hint. Yandex accepts numeric `lr` region IDs such as `213`; Google, Bing, and DuckDuckGo use country/locale-style hints such as `RU`, `US`, or `en-GB` where supported. */\n region?: components[\"parameters\"][\"RegionQuery\"];\n /**\n * @description Date interval in `YYYYMMDD..YYYYMMDD` format.\n * @example 20250101..20250131\n */\n date?: components[\"parameters\"][\"DateQuery\"];\n /**\n * @description File extension filter (for engines that support it).\n * @example PDF\n */\n file?: components[\"parameters\"][\"FileQuery\"];\n /**\n * @description Site/domain filter.\n * @example github.com\n */\n site?: components[\"parameters\"][\"SiteQuery\"];\n /**\n * @description Maximum organic results to return (1–100). Ads may be returned in addition.\n * @example 10\n */\n limit?: components[\"parameters\"][\"LimitQuery\"];\n /**\n * @description Pagination offset (must be >= 0).\n * @example 20\n */\n start?: components[\"parameters\"][\"StartQuery\"];\n /** @description Duplicate filtering flag (primarily used by Google parser behavior). */\n filter?: components[\"parameters\"][\"FilterQuery\"];\n /** @description Include answer box style results when supported. */\n answers?: components[\"parameters\"][\"AnswersQuery\"];\n /**\n * @description Comma-separated engine list for mega endpoints. If omitted, all available engines are used.\n * @example google,bing,duckduckgo\n */\n engines?: components[\"parameters\"][\"EnginesQuery\"];\n /** @description Mega execution mode. `balanced` (default) runs all selected engines in parallel. `any` runs selected engines sequentially in request order until first success. `fast` runs only one engine: the fastest by circuit-breaker average response time. */\n mode?: components[\"parameters\"][\"MegaModeQuery\"];\n /** @description Enable deduplication by normalized URL. Default `true`. */\n dedupe?: components[\"parameters\"][\"MegaDedupeQuery\"];\n /** @description Merge results from all successful engines into one flat list. Default `true`. When `false`, only the first requested engine that returned results is kept. */\n merge?: components[\"parameters\"][\"MegaMergeQuery\"];\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n format?: components[\"parameters\"][\"FormatQuery\"];\n };\n header?: {\n /** @description Request-scoped proxy override. Use `direct` to disable proxy or a tag name to force a specific proxy pool. */\n \"X-Use-Proxy\"?: components[\"parameters\"][\"UseProxyHeader\"];\n /**\n * @description Per-request proxy URL supplied by an upstream balancer. Honored only when `proxies.allow_request_proxy_url: true` is set on the worker; otherwise the request is rejected with `400 bad_request` and `reason=REQUEST_PROXY_URL_DISABLED`. Authenticated SOCKS proxies are rejected in browser mode (`reason=UNSUPPORTED_PROXY_SCHEME`). Credentials are never logged or returned. Precedence: `X-Use-Proxy: direct` > `X-Proxy-URL` > `X-Use-Proxy: <tag>` > per-engine configured tag > `proxies.global` > direct.\n * @example http://user:pass@proxy.example:8080\n */\n \"X-Proxy-URL\"?: components[\"parameters\"][\"ProxyURLHeader\"];\n /**\n * @description Two-letter market country code for the supplied proxy. Used as part of the cache key so different markets do not share results.\n * @example us\n */\n \"X-Proxy-Country\"?: components[\"parameters\"][\"ProxyCountryHeader\"];\n /**\n * @description Proxy class identifier (e.g. `datacenter`, `residential`, `mobile`). Part of the cache key.\n * @example residential\n */\n \"X-Proxy-Class\"?: components[\"parameters\"][\"ProxyClassHeader\"];\n /**\n * @description Upstream proxy provider identifier (e.g. `webshare`, `brightdata`). Part of the cache key.\n * @example webshare\n */\n \"X-Proxy-Provider\"?: components[\"parameters\"][\"ProxyProviderHeader\"];\n /**\n * @description Sticky session identifier minted by the balancer. Reusing the same value lets OpenSERP reuse cookies and browser profile for that lane. Lanes are LRU-bounded by `proxies.lanes.max_lanes`. Rotating the session ID gives a clean lane.\n * @example sid-123\n */\n \"X-Proxy-Session-ID\"?: components[\"parameters\"][\"ProxySessionIDHeader\"];\n /** @description Optional tenant scope used to namespace sticky lane state across multi-tenant deployments. When present, lanes are keyed by `tenant + engine + session_id`. */\n \"X-Tenant\"?: components[\"parameters\"][\"TenantHeader\"];\n };\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Aggregated envelope with clusters */\n 200: {\n headers: {\n \"X-Request-ID\": components[\"headers\"][\"XRequestID\"];\n \"X-Cache\": components[\"headers\"][\"XCache\"];\n \"X-Proxy-Mode\": components[\"headers\"][\"XProxyMode\"];\n \"X-Proxy-Tag\": components[\"headers\"][\"XProxyTag\"];\n \"X-Proxy-Used\": components[\"headers\"][\"XProxyUsed\"];\n \"X-Network-Bytes\": components[\"headers\"][\"XNetworkBytes\"];\n \"X-Browser-Profile-Id\": components[\"headers\"][\"XBrowserProfileID\"];\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"MegaSearchEnvelope\"];\n \"text/markdown\": string;\n \"text/plain\": string;\n \"application/x-ndjson\": string;\n };\n };\n 400: components[\"responses\"][\"BadRequestError\"];\n 403: components[\"responses\"][\"ForbiddenError\"];\n 429: components[\"responses\"][\"TooManyRequestsError\"];\n 500: components[\"responses\"][\"InternalServerError\"];\n 502: components[\"responses\"][\"BadGatewayError\"];\n 503: components[\"responses\"][\"ServiceUnavailableError\"];\n 504: components[\"responses\"][\"GatewayTimeoutError\"];\n };\n };\n megaImageSearch: {\n parameters: {\n query?: {\n /**\n * @description Search query text. At least one of `text`, `site`, or `file` must be non-empty.\n * @example golang\n */\n text?: components[\"parameters\"][\"TextQuery\"];\n /**\n * @description Language code (engine-specific behavior).\n * @example EN\n */\n lang?: components[\"parameters\"][\"LangQuery\"];\n /** @description Market/location hint. Yandex accepts numeric `lr` region IDs such as `213`; Google, Bing, and DuckDuckGo use country/locale-style hints such as `RU`, `US`, or `en-GB` where supported. */\n region?: components[\"parameters\"][\"RegionQuery\"];\n /**\n * @description Date interval in `YYYYMMDD..YYYYMMDD` format.\n * @example 20250101..20250131\n */\n date?: components[\"parameters\"][\"DateQuery\"];\n /**\n * @description File extension filter (for engines that support it).\n * @example PDF\n */\n file?: components[\"parameters\"][\"FileQuery\"];\n /**\n * @description Site/domain filter.\n * @example github.com\n */\n site?: components[\"parameters\"][\"SiteQuery\"];\n /**\n * @description Maximum organic results to return (1–100). Ads may be returned in addition.\n * @example 10\n */\n limit?: components[\"parameters\"][\"LimitQuery\"];\n /**\n * @description Pagination offset (must be >= 0).\n * @example 20\n */\n start?: components[\"parameters\"][\"StartQuery\"];\n /** @description Duplicate filtering flag (primarily used by Google parser behavior). */\n filter?: components[\"parameters\"][\"FilterQuery\"];\n /** @description Include answer box style results when supported. */\n answers?: components[\"parameters\"][\"AnswersQuery\"];\n /**\n * @description Comma-separated engine list for mega endpoints. If omitted, all available engines are used.\n * @example google,bing,duckduckgo\n */\n engines?: components[\"parameters\"][\"EnginesQuery\"];\n /** @description Mega execution mode. `balanced` (default) runs all selected engines in parallel. `any` runs selected engines sequentially in request order until first success. `fast` runs only one engine: the fastest by circuit-breaker average response time. */\n mode?: components[\"parameters\"][\"MegaModeQuery\"];\n /** @description Enable deduplication by normalized URL. Default `true`. */\n dedupe?: components[\"parameters\"][\"MegaDedupeQuery\"];\n /** @description Merge results from all successful engines into one flat list. Default `true`. When `false`, only the first requested engine that returned results is kept. */\n merge?: components[\"parameters\"][\"MegaMergeQuery\"];\n /** @description Output format. `json` (default) returns the envelope. `markdown` returns a Markdown document suitable for Slack/email. `text` returns a minimal plain-text block optimised for LLM context windows. `ndjson` returns one result object per line with no envelope. The `Accept` header is also checked (`text/markdown`, `text/plain`, `application/x-ndjson`). */\n format?: components[\"parameters\"][\"FormatQuery\"];\n };\n header?: {\n /** @description Request-scoped proxy override. Use `direct` to disable proxy or a tag name to force a specific proxy pool. */\n \"X-Use-Proxy\"?: components[\"parameters\"][\"UseProxyHeader\"];\n /**\n * @description Per-request proxy URL supplied by an upstream balancer. Honored only when `proxies.allow_request_proxy_url: true` is set on the worker; otherwise the request is rejected with `400 bad_request` and `reason=REQUEST_PROXY_URL_DISABLED`. Authenticated SOCKS proxies are rejected in browser mode (`reason=UNSUPPORTED_PROXY_SCHEME`). Credentials are never logged or returned. Precedence: `X-Use-Proxy: direct` > `X-Proxy-URL` > `X-Use-Proxy: <tag>` > per-engine configured tag > `proxies.global` > direct.\n * @example http://user:pass@proxy.example:8080\n */\n \"X-Proxy-URL\"?: components[\"parameters\"][\"ProxyURLHeader\"];\n /**\n * @description Two-letter market country code for the supplied proxy. Used as part of the cache key so different markets do not share results.\n * @example us\n */\n \"X-Proxy-Country\"?: components[\"parameters\"][\"ProxyCountryHeader\"];\n /**\n * @description Proxy class identifier (e.g. `datacenter`, `residential`, `mobile`). Part of the cache key.\n * @example residential\n */\n \"X-Proxy-Class\"?: components[\"parameters\"][\"ProxyClassHeader\"];\n /**\n * @description Upstream proxy provider identifier (e.g. `webshare`, `brightdata`). Part of the cache key.\n * @example webshare\n */\n \"X-Proxy-Provider\"?: components[\"parameters\"][\"ProxyProviderHeader\"];\n /**\n * @description Sticky session identifier minted by the balancer. Reusing the same value lets OpenSERP reuse cookies and browser profile for that lane. Lanes are LRU-bounded by `proxies.lanes.max_lanes`. Rotating the session ID gives a clean lane.\n * @example sid-123\n */\n \"X-Proxy-Session-ID\"?: components[\"parameters\"][\"ProxySessionIDHeader\"];\n /** @description Optional tenant scope used to namespace sticky lane state across multi-tenant deployments. When present, lanes are keyed by `tenant + engine + session_id`. */\n \"X-Tenant\"?: components[\"parameters\"][\"TenantHeader\"];\n };\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Aggregated image results envelope */\n 200: {\n headers: {\n \"X-Request-ID\": components[\"headers\"][\"XRequestID\"];\n \"X-Cache\": components[\"headers\"][\"XCache\"];\n \"X-Proxy-Mode\": components[\"headers\"][\"XProxyMode\"];\n \"X-Proxy-Tag\": components[\"headers\"][\"XProxyTag\"];\n \"X-Proxy-Used\": components[\"headers\"][\"XProxyUsed\"];\n \"X-Network-Bytes\": components[\"headers\"][\"XNetworkBytes\"];\n \"X-Browser-Profile-Id\": components[\"headers\"][\"XBrowserProfileID\"];\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ImageEnvelope\"];\n };\n };\n 400: components[\"responses\"][\"BadRequestError\"];\n 403: components[\"responses\"][\"ForbiddenError\"];\n 429: components[\"responses\"][\"TooManyRequestsError\"];\n 500: components[\"responses\"][\"InternalServerError\"];\n 502: components[\"responses\"][\"BadGatewayError\"];\n 503: components[\"responses\"][\"ServiceUnavailableError\"];\n 504: components[\"responses\"][\"GatewayTimeoutError\"];\n };\n };\n listMegaEngines: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Engine list */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"MegaEnginesResponse\"];\n };\n };\n };\n };\n healthCheck: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Healthy or degraded service */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"HealthStatus\"];\n };\n };\n /** @description Unhealthy service */\n 503: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"HealthStatus\"];\n };\n };\n };\n };\n readinessCheck: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Instance is ready */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ReadinessStatus\"];\n };\n };\n /** @description Instance is draining */\n 503: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ReadinessStatus\"];\n };\n };\n };\n };\n getStats: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Runtime statistics */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"StatsResponse\"];\n };\n };\n };\n };\n getCacheStats: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Cache status */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"CacheStats\"];\n };\n };\n };\n };\n getProxyStats: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Proxy stats */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"ProxyStats\"];\n };\n };\n };\n };\n getCircuitBreakerStats: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description Circuit breaker stats */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/json\": components[\"schemas\"][\"CircuitBreakerStatsResponse\"];\n };\n };\n };\n };\n getOpenAPISpec: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description OpenAPI YAML */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"application/yaml\": string;\n };\n };\n };\n };\n getSwaggerUI: {\n parameters: {\n query?: never;\n header?: never;\n path?: never;\n cookie?: never;\n };\n requestBody?: never;\n responses: {\n /** @description HTML page loading Swagger UI from CDN */\n 200: {\n headers: {\n [name: string]: unknown;\n };\n content: {\n \"text/html\": string;\n };\n };\n };\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}