OpenSERP Cloud Updated Quickstart →

Megasearch & Ranking Diff

Run one query across several engines and either merge the results into a single ranked list, or line the rankings up side by side to see where a page sits on...

Run one query across several engines and either merge the results into a single ranked list, or line the rankings up side by side to see where a page sits on Google versus Bing versus Yandex. This page covers both: megaSearch for merged coverage, and a small ranking-diff pattern for comparing positions across engines.

Examples run against OpenSERP Cloud at https://api.openserp.org. The same code works on a self-hosted open-source OpenSERP server - swap the apiKey for a local baseUrl.

export OPENSERP_API_KEY="osk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

megaSearch queries the engines you pass, deduplicates overlapping URLs, and returns one ranked list. Use it when you want broader coverage than a single engine gives.

JavaScript / TypeScript

npm install @openserp/sdk
import { OpenSERP } from "@openserp/sdk";

const client = new OpenSERP({ apiKey: process.env.OPENSERP_API_KEY });

const mega = await client.megaSearch({
  text: "vector database",
  engines: ["google", "bing", "duckduckgo"],
  mode: "balanced",
  limit: 20,
});

console.log(mega.results.length, "merged results");
console.log("credits used:", client.lastResponse?.credits?.used);

Python

pip install openserp
import os
from openserp import OpenSERP

with OpenSERP(api_key=os.environ["OPENSERP_API_KEY"]) as client:
    response = client.mega_search(
        text="vector database",
        engines=["google", "bing", "duckduckgo"],
        mode="balanced",
        limit=20,
    )
    print(len(response.results), "merged results")

curl

curl "https://api.openserp.org/v1/mega/search?text=vector+database&mode=balanced&engines=google,bing,duckduckgo&limit=20" \
  -H "Authorization: Bearer $OPENSERP_API_KEY"

Merged search bills only for the engines that successfully return data. Read X-Credits-Used (or client.lastResponse?.credits?.used) as the source of truth.

Want to eyeball the merge first? Run a megasearch in the Search Playground and inspect the combined result list.

Compare where a page ranks on each engine

Merged search is great for coverage, but for a ranking comparison you want each engine’s list kept separate, then the target domain’s position pulled from each. Query the engines in parallel and diff the ranks:

import { OpenSERP } from "@openserp/sdk";

const client = new OpenSERP({ apiKey: process.env.OPENSERP_API_KEY });

const target = "openserp.org";
const query = "open source serp api";
const engines = ["google", "bing", "duckduckgo", "yandex"] as const;

function rankOf(results: { rank: number; domain?: string }[], domain: string) {
  const hit = results.find((r) => r.domain?.replace(/^www\./, "") === domain);
  return hit ? hit.rank : null;
}

const diff = await Promise.all(
  engines.map(async (engine) => {
    const { results } = await client.search({ engine, text: query, limit: 20, region: "US" });
    return { engine, rank: rankOf(results, target) };
  }),
);

for (const row of diff) {
  console.log(row.engine.padEnd(12), row.rank ?? "not in top 20");
}
google      3
bing        1
duckduckgo  2
yandex      not in top 20

The same shape in Python:

import os
from openserp import OpenSERP

target = "openserp.org"
query = "open source serp api"
engines = ["google", "bing", "duckduckgo", "yandex"]

def rank_of(results, domain):
    hit = next((r for r in results if r.domain and r.domain.removeprefix("www.") == domain), None)
    return hit.rank if hit else None

with OpenSERP(api_key=os.environ["OPENSERP_API_KEY"]) as client:
    for engine in engines:
        response = client.search(engine=engine, text=query, limit=20, region="US")
        print(engine.ljust(12), rank_of(response.results, target) or "not in top 20")

Each engine is a separate billable request, so a four-engine diff costs four single-engine searches. Pin region and lang so the comparison is apples to apples - the same query ranks differently by market.

Resilient routing: any and fast

If you just want one good answer and do not need the merge, any and fast modes return the first successful engine and bill a predictable 1 credit:

const fast = await client.fastSearch({
  text: "vector database",
  engines: ["google", "bing", "duckduckgo"],
  limit: 10,
});
console.log("served by:", client.lastResponse?.engineUsed);
  • fast - picks a low-latency, healthy engine.
  • any - tries engines in your requested order and returns the first success.

Next steps