Metadata-Version: 2.4
Name: reserp
Version: 0.2.1
Summary: Official minimal Python SDK for the Reserp Google Search API and SERP API
Project-URL: Homepage, https://reserp.ai
Project-URL: Documentation, https://reserp.ai/docs
Project-URL: Repository, https://github.com/reserp-ai/reserp-python
Project-URL: Issues, https://github.com/reserp-ai/reserp-python/issues
Project-URL: Changelog, https://github.com/reserp-ai/reserp-python/blob/main/CHANGELOG.md
Author-email: Reserp <no-reply@reserp.ai>
License-Expression: MIT
License-File: LICENSE
Keywords: api-client,google-search,google-search-api,google-serp-api,python,reserp,sdk,search-api,search-results-api,serp,serp-api,serp-data
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: mypy<2,>=1.13; extra == 'dev'
Requires-Dist: pytest<10,>=8.3; extra == 'dev'
Requires-Dist: ruff<1,>=0.8; extra == 'dev'
Requires-Dist: twine<7,>=5.1; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center">
  <a href="https://reserp.ai">
    <img src="https://reserp.ai/icon-512.png" alt="Reserp Google Search API" width="112" height="112">
  </a>
</p>

# Reserp Python SDK

[![PyPI version](https://img.shields.io/pypi/v/reserp.svg)](https://pypi.org/project/reserp/)
[![Python versions](https://img.shields.io/pypi/pyversions/reserp.svg)](https://pypi.org/project/reserp/)
[![CI](https://github.com/reserp-ai/reserp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/reserp-ai/reserp-python/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Google Search data, structured for scale.**

The official minimal Python client for [Reserp](https://reserp.ai), a high-yield Google Search API and SERP API for high-volume, recurring production search workloads.

Reserp returns visible Google Search result blocks as structured JSON, including organic listings, news, carousels, sitelinks, pagination, and nested results in Google's response order. Start for free with no credit card required.

[Website](https://reserp.ai) · [API documentation](https://reserp.ai/docs) · [OpenAPI 3.1](https://reserp.ai/openapi.json) · [Postman](https://www.postman.com/reserp-ai/reserp-google-search-api/overview) · [Pricing](https://reserp.ai/pricing)

## Design

This package is a transparent wrapper over `POST /v1/serp`:

- One client call sends exactly one API request.
- The request dictionary is the public API request body.
- The return value is the native `httpx.Response`.
- Status codes, response headers, success payloads, and error payloads remain unchanged.
- Native HTTPX request options and caller-configured sync or async clients pass through.
- Typed dictionaries describe the public API contract without changing it at runtime.

The client does not retry, back off, impose its own timeouts, build or validate Google URLs, follow pagination, transform responses, cache data, batch work, or control concurrency. Those decisions remain with the caller and the configured HTTPX transport.

## Installation

```bash
pip install reserp
```

Python 3.10 or later is required.

## Quick start

```python
import os

from reserp import Reserp

with Reserp(api_key=os.environ["RESERP_API_KEY"]) as reserp:
    response = reserp.search(
        {"url": "https://www.google.com/search?q=best+pizza+in+dubai&gl=ae&hl=en"}
    )
    data = response.json()

    if not data["ok"]:
        print(response.status_code, data["error"], data["retryable"], data["billed"])
    else:
        for result in data["results"]:
            print(result.get("text"), result.get("url"))
```

Create an API key in the [Reserp dashboard](https://reserp.ai/dashboard). Keep API keys on your server; never embed one in browser or mobile code.

## Production workloads at scale

For bulk Google Search, recurring SERP collection, SEO monitoring, market intelligence, competitive research, and other business-critical data pipelines, place the API behind infrastructure that owns durability and throughput:

```text
producer -> durable queue -> workers with controlled concurrency -> Reserp API
```

Use Cloud Tasks, SQS, BullMQ, Celery, or an equivalent durable queue. Let one layer own retries and backoff, bound worker concurrency, respect `Retry-After`, persist job state and results, and design for possible duplicate queue delivery. These practices are identical whether a worker uses this transparent client or direct HTTP.

## Native transport control

Use an HTTPX client to control transport behavior without an SDK policy layer:

```python
import httpx

limits = httpx.Limits(max_connections=50, max_keepalive_connections=20)
timeout = httpx.Timeout(20.0)

with httpx.Client(limits=limits, timeout=timeout) as transport:
    reserp = Reserp(api_key=os.environ["RESERP_API_KEY"], client=transport)
    response = reserp.search(
        {
            "url": "https://www.google.com/search?q=semiconductor+manufacturing&gl=us&hl=en&tbs=qdr:w"
        },
        headers={"x-request-id": "your-job-id"},
        follow_redirects=False,
    )
```

Additional non-conflicting keyword arguments are passed to `httpx.Client.post` or `httpx.AsyncClient.post` after the SDK supplies the endpoint, authorization header, content type, and JSON body. If you do not inject a client, normal HTTPX transport defaults apply.

Transport and timeout failures remain native HTTPX exceptions. HTTP error responses do not become SDK exceptions; inspect the native status, headers, and API JSON body.

## Async client

```python
import asyncio
import os

import httpx
from reserp import AsyncReserp


async def main() -> None:
    async with httpx.AsyncClient() as transport:
        reserp = AsyncReserp(
            api_key=os.environ["RESERP_API_KEY"],
            client=transport,
        )
        response = await reserp.search(
            {"url": "https://www.google.com/search?q=photonic+computing&gl=us&hl=en"}
        )
        print(response.status_code, response.json())


asyncio.run(main())
```

## Direct HTTP equivalent

The client call is equivalent to this direct API request:

```bash
curl https://api.reserp.ai/v1/serp \
  --request POST \
  --header "Authorization: Bearer $RESERP_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"url":"https://www.google.com/search?q=photonic+computing&gl=us&hl=en"}'
```

Use either interface according to your application. Both expose the same Google Search API contract and leave workload behavior under your control.

## Results and pagination

Pagination uses Google's organic-result offset, not the number of URLs in `results`. A response can contain URLs from many visible result types—including organic listings, news, carousels, sitelinks, and nested result blocks—so never derive the next offset from `len(data["results"])`.

The `start` parameter selects the page by organic-result offset. Omit it or use `0` for the first page, `10` for the second, `20` for the third, and continue in increments of 10. Any other value returns a non-billable `400 invalid_request` response.

Clients fetching pages independently or asynchronously can set `start` directly in each submitted Google Search URL:

```python
with Reserp(api_key=os.environ["RESERP_API_KEY"]) as reserp:
    third_page_response = reserp.search(
        {"url": "https://www.google.com/search?q=photonic+computing&gl=us&hl=en&start=20"}
    )
    third_page = third_page_response.json()
```

`pagination.nextUrl` is provided as a convenience for clients advancing sequentially from a completed response:

```python
with Reserp(api_key=os.environ["RESERP_API_KEY"]) as reserp:
    first_response = reserp.search(
        {"url": "https://www.google.com/search?q=photonic+computing&gl=us&hl=en"}
    )
    first_page = first_response.json()

    if first_page["ok"]:
        next_response = reserp.search({"url": first_page["pagination"]["nextUrl"]})
        next_page = next_response.json()
```

Standard Google Search parameters such as `q`, `gl`, `hl`, `tbm`, and `tbs` belong in the submitted Google URL. See the [API documentation](https://reserp.ai/docs) for the authoritative request contract.

## Errors and billing signals

API errors use stable JSON fields:

```json
{
  "ok": false,
  "error": "rate_limited",
  "retryable": true,
  "billed": false
}
```

The API response is authoritative. Automatically retry only when `retryable` is `true` and `billed` is `false`, respect `Retry-After` when present, and avoid blindly retrying an ambiguous transport failure whose billing outcome is unknown. The client does not make those decisions.

## API resources

- [Google Search API documentation](https://reserp.ai/docs)
- [OpenAPI 3.1 document](https://reserp.ai/openapi.json)
- [Postman workspace](https://www.postman.com/reserp-ai/reserp-google-search-api/overview)
- [JavaScript and TypeScript client on GitHub](https://github.com/reserp-ai/reserp-js)
- [JavaScript and TypeScript package on npm](https://www.npmjs.com/package/@reserp/sdk)
- [Plans and pricing](https://reserp.ai/pricing)

## License

MIT
