Every Cloud endpoint is a JSON API under:
https://api.openserp.org
Search endpoints are GET requests and require the bearer auth from Authentication. The official SDKs call these same endpoints for you — when an example is shown for both, pick whichever fits your codebase.
Endpoint map
| Endpoint | Use when |
|---|---|
GET /v1/{engine}/search |
You want web results from one engine. |
GET /v1/{engine}/image |
You want image results from one engine. |
GET /v1/mega/search?mode=balanced |
You want merged, deduplicated web results from multiple engines. |
GET /v1/mega/image?mode=balanced |
You want merged image results from multiple engines. |
GET /v1/mega/search?mode=any |
You want one good web response, engines tried in your order. |
GET /v1/mega/search?mode=fast |
You want a low-latency web response from a healthy engine. |
GET /v1/mega/image?mode=any | ?mode=fast |
First-successful or low-latency image routing. |
GET /v1/extract |
You want clean, LLM-ready content from one URL. |
POST /v1/extract/batch |
You want clean content from up to 20 URLs in one request. |
GET /v1/me |
Validate a key or read the account balance. |
GET /v1/engines/status |
User-facing engine health and latency. |
GET /v1/engines/capabilities |
Supported web/image capability matrix. |
Supported engines: google, bing, yandex, baidu, duckduckgo (alias duck), ecosia.
Single-engine web search
Use this when you know which engine you want.
JavaScript SDK
const response = await client.search({
engine: "google",
text: "golang",
limit: 10,
region: "US",
lang: "EN",
});
for (const r of response.results) {
console.log(r.rank, r.title, r.url);
}
Python SDK
response = client.search(engine="google", text="golang", limit=10, region="US", lang="EN")
for r in response.results:
print(r.rank, r.title, r.url)
HTTP
curl "https://api.openserp.org/v1/google/search?text=golang&limit=10®ion=US&lang=EN" -H "Authorization: Bearer $OPENSERP_API_KEY"
Common search parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
string | required | Search query. |
limit |
integer | 10 |
Maximum results, 1 to 100. A limit over 10 can return multiple SERP pages where available; single-engine web search bills ceil(returned web results / 10), not query count. |
start |
integer | 0 |
Nonnegative result offset. Support and step size depend on the engine. See Pagination. |
lang |
string | optional | Language code such as EN, DE, RU, ES, FR, ZH. |
region |
string | US |
Country/region code such as US, GB, DE, RU. |
date |
string | optional | Date range YYYYMMDD..YYYYMMDD, e.g. 20250101..20251231. Google and Ecosia only. |
site |
string | optional | Restrict to a domain, e.g. github.com. |
file |
string | optional | Restrict to a file type, e.g. pdf. |
features |
boolean | true |
Return serp_features (AI Overview, answer boxes, people also ask, related searches) when the page has them. false returns an empty array. Same flag and default as OpenSERP OSS. |
Not every engine supports every filter — treat lang, site, and file as best-effort outside Google. start and date are strict: if the engine can’t serve them, the request fails with 400 invalid_request (for example “date is not supported for this engine.”) and isn’t charged.
| Engine | start |
date |
|---|---|---|
| Yes, multiples of 10 | Yes | |
| Bing | Yes, multiples of 10 | No |
| Yandex | Yes, multiples of 10 | No |
| Baidu | First few pages | No |
| Ecosia | Yes | Yes |
| DuckDuckGo | No | No |
Response envelope
Search responses always use the same query, meta, results, pagination shape:
{
"query": { "text": "golang", "engines_requested": ["google"] },
"meta": {
"request_id": "019dc6c1-da45-706e-a57c-d671fa2862ee",
"requested_at": "2026-05-12T18:30:00Z",
"took_ms": 642
},
"results": [
{
"id": "google-1",
"rank": 1,
"type": "organic",
"title": "The Go Programming Language",
"url": "https://go.dev/",
"display_url": "go.dev",
"snippet": "Go is an open source programming language.",
"domain": "go.dev",
"position": { "absolute": 1 },
"engine": "google"
}
],
"pagination": { "page": 1, "has_more": true, "next_start": 10 }
}
Web result fields include title, url, snippet, domain, type, position, and engine. Image results use the same envelope and add image-specific fields described below.
Multi-engine web search
Use mode=balanced for broader coverage with one deduplicated list.
JavaScript SDK
const response = await client.megaSearch({
text: "openserp api",
engines: ["google", "bing", "duckduckgo"],
limit: 20,
region: "US",
});
console.log(response.results.length, "merged results");
console.log("credits used:", client.lastResponse?.credits?.used);
HTTP
curl "https://api.openserp.org/v1/mega/search?text=openserp+api&mode=balanced&engines=google,bing,duckduckgo&limit=20®ion=US" -H "Authorization: Bearer $OPENSERP_API_KEY"
Mega parameters
| Parameter | Type | Description |
|---|---|---|
engines |
comma-separated list | Engines to query, e.g. google,bing,yandex. Omit to use the default engine set. |
mode |
enum | balanced, any, or fast. Defaults to balanced. |
features |
boolean | Return merged serp_features. Defaults to true; false returns an empty array, as in OpenSERP OSS. |
Mega endpoints are billed dynamically from the engines that successfully return data. Image endpoints are billed by returned image volume. Use X-Credits-Used (or client.lastResponse?.credits?.used) as the source of truth.
Any and Fast routing
These modes return one successful response and bill a predictable 1 credit. Use them when you want resilience without merged coverage.
| Mode | Behavior |
|---|---|
any |
Tries engines in your order. If one is slow, the next starts a few seconds later, and the first good result wins. |
fast |
Same race, with engines ordered by recent health and latency. |
Both answer within about 15 seconds. start is passed through on any and fast; balanced mega rejects start above 0 with 400.
JavaScript SDK
const fast = await client.fastSearch({
text: "golang",
engines: ["google", "bing", "duckduckgo"],
limit: 10,
});
console.log("served by:", client.lastResponse?.engineUsed);
const any = await client.anySearch({
text: "golang",
engines: ["google", "bing", "duckduckgo"],
limit: 10,
});
console.log("served by:", client.lastResponse?.engineUsed);
HTTP
curl "https://api.openserp.org/v1/mega/search?text=golang&mode=fast&engines=google,bing,duckduckgo&limit=10" -H "Authorization: Bearer $OPENSERP_API_KEY"
Any/Fast success responses include meta.engine_used and the X-Engine-Used header. engines_tried and engines_skipped may be absent. If every requested engine fails, the response is 502 and isn’t charged. If every engine rejects the requested paging or date filter, the response is 400 invalid_request without charge. See Errors & rate limits.
Image search
Image endpoints take text, limit, and region (plus engines and mode on mega).
Single-engine
const images = await client.image({
engine: "bing",
text: "golang logo",
limit: 10,
});
for (const img of images.results) {
console.log(img.title, img.image_url, `${img.width}x${img.height}`);
}
curl "https://api.openserp.org/v1/bing/image?text=golang+logo&limit=10" -H "Authorization: Bearer $OPENSERP_API_KEY"
Multi-engine
const merged = await client.megaImage({
text: "golang logo",
engines: ["bing", "google"],
limit: 20,
});
const fastImg = await client.fastImage({
text: "golang logo",
engines: ["bing", "google"],
limit: 10,
});
const anyImg = await client.anyImage({
text: "golang logo",
engines: ["bing", "google"],
limit: 10,
});
Image result fields
Image results extend the standard envelope with:
| Field | Description |
|---|---|
image_url |
Direct URL to the full-size image. |
thumbnail_url |
URL for a smaller preview. |
source_url |
Page where the image is hosted. |
width, height |
Image dimensions in pixels (when known). |
title, snippet |
Caption and surrounding context. |
URL extraction
GET /v1/extract fetches one URL and returns clean, LLM-ready markdown, text, and page metadata.
JavaScript SDK
const page = await client.extract({
url: "https://openserp.org/docs",
mode: "auto",
});
console.log(page.markdown);
cURL
curl -H "Authorization: Bearer $OPENSERP_API_KEY" "https://api.openserp.org/v1/extract?url=https://openserp.org/docs&mode=auto"
Many URLs at once
POST /v1/extract/batch extracts up to 20 URLs concurrently and returns one item per URL. A URL that fails becomes an item with an error rather than failing the request, so one dead link never costs you the other results:
curl -X POST -H "Authorization: Bearer $OPENSERP_API_KEY" -H "Content-Type: application/json" -d '{"urls":["https://openserp.org/docs","https://openserp.org/blog"],"mode":"auto"}' "https://api.openserp.org/v1/extract/batch"
const batch = await client.batchExtract({
urls: ["https://openserp.org/docs", "https://openserp.org/blog"],
mode: "auto",
});
for (const item of batch.results ?? []) {
if (item.error) console.warn(item.url, "failed:", item.error);
else console.log(item.url, item.page_content?.slice(0, 120));
}
Billing is per URL and matches calling /v1/extract that many times: successful extractions bill their mode, while failed and empty ones are free. A batch reserves its worst case up front and returns the unused remainder when it finishes, so a large batch can be refused for insufficient credits even when the real charge would be smaller.
Extracting from a specific region
Pass region (a two-letter country code) to extract as a visitor from that country - useful for geo-fenced or localized pages. It applies to every URL in a batch and adds 1 credit per successfully extracted URL:
curl -H "Authorization: Bearer $OPENSERP_API_KEY" "https://api.openserp.org/v1/extract?url=https://example.com/pricing®ion=DE"
Omit region to extract at the base rate. Unlike search, extraction accepts country codes only - not city names or numeric region IDs.
Pagination
Use start to request later result pages. For Google, Bing, and Yandex, keep limit at 10 (or a multiple of 10) and pass the previous response’s pagination.next_start:
async function paginate(text: string, pages = 3) {
const all = [];
let start = 0;
for (let i = 0; i < pages; i += 1) {
const page = await client.search({ engine: "google", text, limit: 10, start });
all.push(...page.results);
if (!page.pagination?.has_more || page.results.length === 0) break;
start = page.pagination.next_start;
}
return all;
}
const results = await paginate("openserp");
console.log(results.length);
pagination is computed from your request: page is floor(start / limit) + 1, has_more is true while a page returns results (unless the engine reports the last page), and next_start is start + limit when has_more is true, otherwise 0. has_more is a continuation hint; it does not guarantee a nonempty next page. An offset the engine can’t serve (see the table under Common search parameters) returns 400 invalid_request and isn’t charged.
Each page is a separate billable request, and a later page costs the same as the first. Watch X-Credits-Used and X-Credits-Remaining on long pagination jobs.
Account and capability endpoints
These endpoints are free to call.
GET /v1/me
The account associated with the API key and the current credit balance.
const account = await client.me();
console.log(account.email, account.credits_remaining);
{ "email": "[email protected]", "credits_remaining": 4250 }
GET /v1/pricing
Live credit pricing. Fetch it from your own app when you want to display expected cost without hardcoding values.
const pricing = await client.pricing();
console.log(pricing.credit_price_usd, pricing.search.credits);
Use GET /v1/pricing as the source of truth for current per-call credit costs — values may change while pricing is being finalized.
GET /v1/engines/status
User-facing operational status and recent latency signals.
const status = await client.enginesStatus();
console.log(status);
{
"engines": {
"google": { "status": "operational", "latency_ms": 420 },
"yandex": { "status": "loaded", "latency_ms": 710 }
}
}
Status values:
operational- engine is delivering results.loaded- engine is delivering results under load.down- currently unavailable.
Idle engines retain their last known status across restarts.
Without an API key, the response contains only { "overall": "operational" }, with overall one of operational, degraded, or down. Per-engine details require Authorization: Bearer authentication.
GET /v1/engines/capabilities
Currently supported web and image capabilities.
const caps = await client.enginesCapabilities();
console.log(caps.engines?.google);
{
"engines": {
"google": { "web": true, "image": true }
},
"modes": {
"any": { "web": true, "image": true },
"fast": { "web": true, "image": true },
"mega": { "web": true, "image": true }
}
}
Response telemetry
Every billable response includes:
| Header | SDK accessor | Meaning |
|---|---|---|
X-Request-Id |
client.lastResponse?.requestId |
Unique request ID — quote in support tickets. |
X-Credits-Used |
client.lastResponse?.credits?.used |
Credits charged for this request. |
X-Credits-Remaining |
client.lastResponse?.credits?.remaining |
Account balance after the request. |
X-Engine-Used |
client.lastResponse?.engineUsed |
Engine used by Any/Fast routing. |
Next
- Usage examples — full SDK, MCP, n8n, and curl snippets plus recipes.
- Errors & rate limits — retry behavior and failure handling.
- OSS vs Cloud — how Cloud differs from the self-hosted server.