OpenSERP Cloud Quickstart →

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.

  1. In n8n, open Settings → Community Nodes → Install and enter the package name:

    @openserp/n8n-nodes-openserp
    
  2. 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/me to confirm the key works.

  3. 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&region=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