Skip to main content

Python SDK for the OpenSERP self-hosted server and OpenSERP Cloud.

Project description

openserp

PyPI version Python versions License

pip install openserp

Cloud:

import os
from openserp import OpenSERP

client = OpenSERP(api_key=os.environ["OPENSERP_KEY"])
resp = client.search(engine="google", text="openserp")

print(resp.results[0].title, resp.results[0].url)

Self-hosted:

from openserp import OpenSERP

client = OpenSERP(base_url="http://localhost:7000")
resp = client.search(engine="bing", text="openserp")

print(resp.results[0].title, resp.results[0].url)

Python 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 and against OpenSERP Cloud with the same code.

Use it for AI grounding, RAG pipelines, LLM tool use, agent tool use, LangChain / LlamaIndex integrations, SEO rank tracking, competitor analysis, and search-powered automations. Open-source alternative to SerpAPI, DataForSEO, ScrapingBee, Bright Data SERP, Oxylabs SERP, and Zenserp.

Also available for TypeScript / JavaScript: @openserp/sdk (source).

Alpha — the API may change before 1.0.0. Pin a version in production.

Contents

Install

pip install openserp

DataFrame export is an optional extra:

pip install "openserp[pandas]"

Requires Python 3.10+.

Quickstart — OSS (self-hosted)

Run the open-source server locally, no API key required:

docker run -p 7000:7000 karust/openserp serve
from openserp import OpenSERP

client = OpenSERP(base_url="http://localhost:7000")

resp = client.search(
    engine="google",
    text="openserp",
    limit=10,
    region="US",
)

print(resp.results[0].title, resp.results[0].url)

If you pass no options, the client defaults to http://localhost:7000.

Quickstart — Cloud

Get an API key at the dashboard. When api_key is set, the SDK defaults base_url to https://api.openserp.org/v1 and sends Authorization: Bearer ... for you.

import os
from openserp import OpenSERP

client = OpenSERP(api_key=os.environ["OPENSERP_KEY"])

resp = client.search(engine="google", text="openserp")

print(resp.results[0].title)
print(client.last_response.credits)  # CreditInfo(used=..., remaining=...)

If both base_url and api_key are set, base_url wins and the key is still sent. Use this for an authenticated self-hosted deployment.

Why two backends?

OpenSERP Cloud is the same HTTP contract as the OSS server, with a /v1/ prefix and bearer auth. The same SDK call works on both — you only change base_url / api_key. 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 for the full comparison.

Search

single = client.search(engine="bing", text="golang", limit=10, region="US")

mega = client.mega_search(
    text="golang",
    engines=["google", "bing", "yandex"],
    mode="balanced",
    limit=20,
)

fast = client.fast_search(text="golang", engines=["google", "bing"])
any_ = client.any_search(text="golang", engines=["google", "yandex"])

mega_search aggregates multiple engines. mode is "balanced" (default, merged and deduplicated), "any" (first successful engine wins), or "fast" (engines reordered by recent health). fast_search / any_search are sugar for the matching mode.

Images

images = client.image(engine="bing", text="golang logo", limit=20)

mega_images = client.mega_image(text="golang logo", engines=["bing", "google"])

Async

import asyncio, os
from openserp import AsyncOpenSERP


async def main() -> None:
    async with AsyncOpenSERP(api_key=os.environ["OPENSERP_KEY"]) as client:
        resp = await client.search(engine="google", text="openserp")
        print(resp.results[0].title)


asyncio.run(main())

Run hundreds of queries concurrently with a semaphore:

import asyncio
from openserp import AsyncOpenSERP


async def main() -> None:
    sem = asyncio.Semaphore(20)
    queries = [f"keyword {i}" for i in range(500)]

    async with AsyncOpenSERP() as client:
        async def run(query: str):
            async with sem:
                return await client.search(engine="google", text=query, limit=10)

        responses = await asyncio.gather(*(run(q) for q in queries))
        print(len(responses))


asyncio.run(main())

Endpoint availability

OSS-only operational methods raise OssOnlyError when the client is configured for Cloud:

client.parse_google(html="<html>...</html>")
client.stats()
client.health()

Cloud-only account methods raise CloudOnlyError when the client is configured for OSS:

client.me()
client.pricing()
client.engines_status()
client.engines_capabilities()

The backend is inferred from base_url and api_key. Pass backend="oss" or backend="cloud" to the constructor to override.

Telemetry

client.last_response is updated after every HTTP response:

client.last_response.credits          # Cloud — CreditInfo(used, remaining)
client.last_response.engine_used      # both — X-Engine-Used
client.last_response.fallback_engine  # OSS only
client.last_response.cache            # OSS only
client.last_response.headers          # raw response headers (lower-cased)

fallback_engine and cache are deliberately hidden by Cloud, so expect them to be None against api.openserp.org. credits is the inverse: only present when talking to Cloud.

Error handling

from openserp import OpenSERP, RateLimitError, CaptchaError, SERPError

client = OpenSERP(api_key="...")

try:
    client.search(engine="google", text="openserp")
except RateLimitError:
    # slow down or queue the request
    ...
except CaptchaError:
    # OSS may surface upstream captcha challenges; on Cloud this is handled for you
    ...
except SERPError as err:
    print(err.status, err.code, err.reason, err.request_id)

Retry hook

The SDK does not apply a retry policy. Provide a hook when you want one:

import os, random, time
from openserp import OpenSERP, SERPError

RETRYABLE = {408, 429, 500, 502, 503}
client: OpenSERP


def should_retry(err: Exception, attempt: int) -> bool:
    if attempt >= 3 or not isinstance(err, SERPError) or err.status not in RETRYABLE:
        return False
    headers = client.last_response.headers if client.last_response else {}
    retry_after = float(headers.get("retry-after", 0) or 0)
    wait = retry_after or min(2 ** attempt * 0.25, 8.0)
    time.sleep(wait + random.random() * 0.25)
    return True


client = OpenSERP(api_key=os.environ["OPENSERP_KEY"], retry=should_retry)
client.search(engine="google", text="openserp")

Use cases

  • AI grounding / RAG — feed top-N results into an LLM prompt (OpenAI, Anthropic, Ollama) for up-to-date answers.
  • LLM tool use — expose client.search as a tool to your agent.
  • SEO monitoring — daily rank tracking across multiple engines and regions, export to a DataFrame or Sheets.
  • Competitor analysis — weekly diff of top-10 results for a keyword set.
  • Data pipelines — stream SERPs to ClickHouse, BigQuery, or a DataFrame for NLP on snippets.

Quick SEO rank report with pandas:

import pandas as pd
from openserp import OpenSERP

client = OpenSERP()
keywords = ["openserp", "serp api", "google search api"]
frames = []

for keyword in keywords:
    resp = client.search(engine="google", text=keyword, region="US", limit=10)
    frame = resp.to_pandas()
    frame["keyword"] = keyword
    frames.append(frame)

pd.concat(frames, ignore_index=True).to_csv("rank-report.csv", index=False)

Development

python -m pip install -e ".[dev,pandas]"
pytest
ruff check .
mypy src
python -m build

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openserp-0.1.3.tar.gz (14.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openserp-0.1.3-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file openserp-0.1.3.tar.gz.

File metadata

  • Download URL: openserp-0.1.3.tar.gz
  • Upload date:
  • Size: 14.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openserp-0.1.3.tar.gz
Algorithm Hash digest
SHA256 09965cee4b989b58b360b489b745e786e1c15f8167dbe285ad4674ca1573d710
MD5 e42ddc5ab814c88e43ef58e9cbf0b502
BLAKE2b-256 9e2cbe6b20968c674322927b7599d4b504176c9b5eb7852a346bfbf02f8c9762

See more details on using hashes here.

Provenance

The following attestation bundles were made for openserp-0.1.3.tar.gz:

Publisher: sdk-python.yml on karust/openserp_project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openserp-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: openserp-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openserp-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 084fcf1de8f985c6ec042bbcb4fee534183afb4ac7274fa9f80757500069b308
MD5 9e9d9fed1bcdaa413404ce0c72b59bba
BLAKE2b-256 8f290440a1c4f7b21c9fc8a659a83de588a4a905809647d9fb31dbbce4f51c6a

See more details on using hashes here.

Provenance

The following attestation bundles were made for openserp-0.1.3-py3-none-any.whl:

Publisher: sdk-python.yml on karust/openserp_project

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page