Copy-pasteable examples for using OpenSERP Cloud from the official SDKs, the MCP server, n8n, and raw HTTP. Every example authenticates with a Cloud API key - create one on the API keys page first.
Store the secret in an environment variable and never commit it:
export OPENSERP_API_KEY="osk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
All of these run the same Cloud API at https://api.openserp.org. The SDKs add the base URL and the Authorization: Bearer header for you when you pass an API key.
JavaScript / TypeScript SDK
Install the @openserp/sdk package:
npm install @openserp/sdk
Pass apiKey and the SDK targets Cloud automatically (it defaults baseUrl to https://api.openserp.org/v1):
import { OpenSERP } from "@openserp/sdk";
const client = new OpenSERP({ apiKey: process.env.OPENSERP_API_KEY });
const { results } = await client.search({
engine: "google",
text: "openserp",
limit: 5,
region: "US",
});
for (const r of results) {
console.log(r.rank, r.title, r.url);
}
console.log("credits remaining:", client.lastResponse?.credits?.remaining);
Works in Node 18+, Bun, Deno, Cloudflare Workers, Vercel Edge, and any runtime with global fetch.
Multi-engine merged search, with first-success routing as a cheaper alternative:
const mega = await client.megaSearch({
text: "openserp api",
engines: ["google", "bing", "duckduckgo"],
mode: "balanced",
limit: 20,
});
console.log(mega.results.length, "merged results");
const fast = await client.fastSearch({
text: "openserp api",
engines: ["google", "bing", "duckduckgo"],
});
console.log("served by:", client.lastResponse?.engineUsed);
Image search:
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}`);
}
Typed error handling - the SDK applies no retry policy of its own, so handle failures explicitly:
import { CaptchaError, RateLimitError, SERPError } from "@openserp/sdk";
try {
await client.search({ engine: "google", text: "openserp" });
} catch (err) {
if (err instanceof RateLimitError) {
// back off and retry later
} else if (err instanceof CaptchaError) {
// engine challenged the request; retry later or switch engines
} else if (err instanceof SERPError) {
console.error(err.status, err.code, err.reason, err.requestId);
}
}
Python SDK
Install the openserp package:
pip install openserp
The client is a context manager. Pass api_key to target Cloud:
import os
from openserp import OpenSERP
with OpenSERP(api_key=os.environ["OPENSERP_API_KEY"]) as client:
response = client.search(engine="google", text="openserp", limit=5, region="US")
for item in response.results:
print(item.rank, item.title, item.url)
print("credits remaining:", client.last_response.credits.remaining)
Multi-engine merged search:
with OpenSERP(api_key=os.environ["OPENSERP_API_KEY"]) as client:
response = client.mega_search(
text="openserp api",
engines=["google", "bing", "duckduckgo"],
mode="balanced",
limit=20,
)
print(len(response.results), "merged results")
MCP server
Give Claude, Cursor, and other MCP clients live search and URL extraction as tools with @openserp/mcp. Set OPENSERP_API_KEY and the server runs in Cloud mode against https://api.openserp.org.
Claude Desktop
Add this to claude_desktop_config.json:
{
"mcpServers": {
"openserp": {
"command": "npx",
"args": ["-y", "@openserp/mcp"],
"env": {
"OPENSERP_API_KEY": "osk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
}
Claude Code
claude mcp add openserp --env OPENSERP_API_KEY=$OPENSERP_API_KEY -- npx -y @openserp/mcp
Cursor
Use the same JSON entry as Claude Desktop. The server exposes search, mega_search, fast_search, any_search, image_search, mega_image, extract, get_usage, and list_engines tools.
Set OPENSERP_BASE_URL only if you need to point at a different compatible OpenSERP base URL.
n8n community node
Drop search, image, and extract steps into n8n automations with @openserp/n8n-nodes-openserp.
-
In n8n, open Settings → Community Nodes → Install and enter the package name:
@openserp/n8n-nodes-openserp -
Create an OpenSERP API credential for Cloud:
API Key: osk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Base URL: leave empty (defaults to https://api.openserp.org/v1)The credential test calls
/v1/meto confirm the key works. -
Add an OpenSERP node and pick an operation, for example a single-engine web search:
Resource: Search Operation: Single Engine: Google Query: openserp Limit: 10
Search and image operations emit one n8n item per result, plus an openserp_meta object on the first item with the request ID, credits, and engine used.
curl / raw REST
Search endpoints are GET requests. Authenticate with a bearer header.
Single-engine web search:
curl "https://api.openserp.org/v1/google/search?text=openserp&limit=5®ion=US" \
-H "Authorization: Bearer $OPENSERP_API_KEY"
Multi-engine merged search:
curl "https://api.openserp.org/v1/mega/search?text=openserp+api&mode=balanced&engines=google,bing,duckduckgo&limit=20" \
-H "Authorization: Bearer $OPENSERP_API_KEY"
Image search:
curl "https://api.openserp.org/v1/bing/image?text=golang+logo&limit=10" \
-H "Authorization: Bearer $OPENSERP_API_KEY"
See the Endpoints reference for every parameter and the full response envelope.
Recipes
Short, practical patterns. They run against Cloud as written; the same code works on a self-hosted server by swapping apiKey for baseUrl. More runnable versions live in the open-source examples directory.
Ground an LLM answer with fresh results
Turn the top results into citable context for a prompt:
import { OpenSERP } from "@openserp/sdk";
const client = new OpenSERP({ apiKey: process.env.OPENSERP_API_KEY });
const question = "What is retrieval augmented generation?";
const { results } = await client.search({ engine: "google", text: question, limit: 10 });
const context = results
.map((r, i) => `[${i + 1}] ${r.title}\n${r.url}\n${r.snippet ?? ""}`)
.join("\n\n");
const prompt = `Answer the question using only the search results below. Cite sources as [n].
Question: ${question}
Search results:
${context}`;
Expose search as an agent tool
A plain function the model can call, returning JSON-serializable results:
import os
from openserp import OpenSERP
def web_search(query: str, limit: int = 10) -> list[dict]:
"""Search the web and return a compact list of results."""
with OpenSERP(api_key=os.environ["OPENSERP_API_KEY"]) as client:
response = client.search(engine="google", text=query, limit=limit)
return [
{"rank": r.rank, "title": r.title, "url": r.url, "snippet": r.snippet}
for r in response.results
]
Track a domain’s rank for a keyword set
A minimal SEO rank check across several keywords:
import os
from openserp import OpenSERP
target = "go.dev"
keywords = ["golang tutorial", "go programming language", "golang documentation"]
def matches(domain, target):
if not domain:
return False
domain = domain.lower().removeprefix("www.")
return domain == target or domain.endswith(f".{target}")
with OpenSERP(api_key=os.environ["OPENSERP_API_KEY"]) as client:
for keyword in keywords:
response = client.search(engine="google", text=keyword, region="US", limit=20)
hit = next((r for r in response.results if matches(r.domain, target)), None)
print(f'"{keyword}": {hit.rank if hit else "not in top 20"}')
Search and pull clean page content in one call
Pass the extraction flags to enrich the top results with cleaned page content (extractTop is capped at 5):
import { OpenSERP } from "@openserp/sdk";
const client = new OpenSERP({ apiKey: process.env.OPENSERP_API_KEY });
const { results } = await client.search({
engine: "google",
text: "what is a serp api",
extract: true,
extractTop: 2,
extractMode: "auto",
});
for (const r of results) {
const content = r.extracted?.content;
console.log(r.rank, r.title, content?.slice(0, 300).trim());
}
Need just one page as Markdown instead? Use extract:
const page = await client.extract({ url: "https://openserp.org/docs", mode: "auto", clean: true });
console.log(page.markdown);
Next steps
- Quickstart - create a key and run your first request.
- Endpoints reference - every endpoint, parameter, and the response envelope.
- Authentication - key storage and rotation.
- Errors & rate limits - retry behavior and failure handling.