# Migrate from SerpApi

This guide maps SerpApi's Google Search and Google Maps engines to SerpWiz. For pricing, see [SerpApi vs SerpWiz](/vs/serpapi).

## Requests

| | SerpApi | SerpWiz |
|---|---|---|
| Search endpoint | `GET https://serpapi.com/search?engine=google` | `GET https://api.serpwiz.com/v1/google/search` |
| Maps endpoint | `GET https://serpapi.com/search?engine=google_maps&type=search` | `GET https://api.serpwiz.com/v1/google/maps` |
| API key | `api_key` query parameter | `X-API-Key` header (an `api_key` query parameter also works, but the header keeps keys out of logs) |

### Search parameters

| SerpApi | SerpWiz | Notes |
|---|---|---|
| `q` | `q` | Same. Google operators such as `site:` work. |
| `gl` | `gl` | Same two-letter country code. |
| `hl` | `hl` | Same language code. |
| `start` | `page` | SerpApi uses a result offset (`0`, `10`, `20`…). SerpWiz uses page numbers (`1`, `2`, `3`…), so `page = start / 10 + 1`. |
| `tbs` | `tbs` | Supported: `qdr:h/d/w/m/y`, `li:1` and custom date ranges (`cdr:1,cd_min:…,cd_max:…`). |
| `lr` | `lr` | One value, e.g. `lang_en`. |
| `cr` | `cr` | One value, e.g. `countryUS`. |
| `location` | — | Not available on Search yet. Use `gl` for the country. |
| `device`, `safe`, `num` | — | Not available. Results are desktop, 10 per page. |
| `no_cache` | — | Results may come from a cache up to about an hour old. |
| `output` | — | Always JSON. |

### Maps parameters

| SerpApi (`google_maps`) | SerpWiz (`/v1/google/maps`) | Notes |
|---|---|---|
| `q` | `q` | Same. |
| `ll` (`@lat,lng,zoomz`) | `lat`, `lng`, `zoom` | Split into three parameters. `radius_km` is also available. |
| `place_id` | `place_id` | Look up one place. |
| `start` (steps of 20) | `num` (1–20) | Up to 20 places per request. |
| `hl`, `gl` | `hl`, `gl` | Same. |

## Responses

| SerpApi | SerpWiz |
|---|---|
| `organic_results[]` | `results[]` |
| `organic_results[].position` | `results[].position` |
| `organic_results[].title` | `results[].title` |
| `organic_results[].link` | `results[].url` |
| `organic_results[].snippet` | `results[].description` |
| `related_searches[]` | `related_keywords.keywords[]` (`keyword`) |
| `related_questions[]` (People Also Ask) | `related_keywords.keywords[]`, as question text only |
| `knowledge_graph` | `knowledge_panel` |
| `pagination` / `serpapi_pagination` | `next_page`: pass it as `page` |
| `local_results[]` (Maps) | `results[]` |
| `local_results[].gps_coordinates` | `results[].location` (`lat`, `lng`) |
| `local_results[].reviews` | `results[].reviewsCount` |
| `local_results[].type` | `results[].categoryName` |

See [Google Search](/docs/google-search) and [Google Maps](/docs/google-maps) for every field.

## Code: before and after

```python
# Before: SerpApi
import requests
r = requests.get("https://serpapi.com/search", params={
    "engine": "google", "q": "best crm for startups", "gl": "us", "start": 10,
    "api_key": SERPAPI_KEY,
})
for item in r.json().get("organic_results", []):
    print(item["position"], item["title"], item["link"])
```

```python
# After: SerpWiz
import requests
r = requests.get("https://api.serpwiz.com/v1/google/search", params={
    "q": "best crm for startups", "gl": "us", "page": 2,
}, headers={"X-API-Key": SERPWIZ_KEY}, timeout=60)
r.raise_for_status()
for item in r.json()["results"]:
    print(item["position"], item["title"], item["url"])
```

## Errors

- **SerpApi** returns `{"error": "…"}`.
- **SerpWiz** returns `{"error": {"status", "code", "message", "request_id"}}` with a matching HTTP status. Retry `429` and `503` after the `Retry-After` header. See [Errors](/docs/errors).
- **Charging:** SerpWiz never charges for an error.
