# Custom Search (CSE-compatible)

`GET https://api.serpkite.com/customsearch/v1` · Credits: 1 per page

For apps moving off the Google Custom Search JSON API: same query params (key, cx, q, start, num…), same items[] and searchInformation shape.

For apps moving off the Google Custom Search JSON API, which Google shuts down on January 1, 2027. Same query parameters, same `items[]` and `searchInformation` shape. Change the host and the key, keep your parser.

`cx` is accepted and ignored: results come from the whole web, not a programmable search engine. Use `siteSearch` or a `site:` operator to restrict to a domain. This endpoint keeps Google's own parameter names (`gl`, `hl`, `lr`…) and is not under `/v1`.

## Query parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `q` **required** | string |  | The search query. Required. |
| `key` | string |  | Your SerpKite API key (or send `Authorization: Bearer`). |
| `cx` | string |  | Accepted and ignored. |
| `start` | integer | `1` | Index of the first result, 1–91 (1, 11, 21…). |
| `num` | integer | `10` | Results per call, 1–10. |
| `gl` | string |  | Country code (Google CSE name). |
| `hl` | string |  | Interface language (Google CSE name). |
| `lr` | string |  | Language restrict, e.g. `lang_de`. |
| `safe` | string |  | SafeSearch. One of: `active`, `off`. |
| `dateRestrict` | string |  | Recency, e.g. `d7`, `w2`, `m6`, `y1`. |
| `siteSearch` | string |  | Restrict to a site. |
| `searchType` | string |  | `image` for image results. One of: `image`. |

## Example request

cURL:

```bash
curl "https://api.serpkite.com/customsearch/v1?q=site%3Adocs.python.org+asyncio&cx=any" \
  -H "Authorization: Bearer $SERPKITE_API_KEY"
```

Python:

```python
import os
import requests

res = requests.get(
    "https://api.serpkite.com/customsearch/v1",
    params={"q": "site:docs.python.org asyncio", "cx": "any"},
    headers={"Authorization": f"Bearer {os.environ['SERPKITE_API_KEY']}"},
    timeout=30,
)
res.raise_for_status()
print(res.json())
```

Node.js:

```javascript
const res = await fetch("https://api.serpkite.com/customsearch/v1?q=site%3Adocs.python.org+asyncio&cx=any", {
  headers: { Authorization: `Bearer ${process.env.SERPKITE_API_KEY}` },
});
if (!res.ok) throw new Error((await res.json()).error.message);
console.log(await res.json());
```

## Response fields

| Field | Type | Description |
| --- | --- | --- |
| `kind` | string | `customsearch#search`. |
| `searchInformation` | object | `searchTime`, `formattedSearchTime`, `totalResults`, `formattedTotalResults`. |
| `items[]` | array | `kind`, `title`, `htmlTitle`, `link`, `displayLink`, `snippet`, `htmlSnippet`, `formattedUrl`, `htmlFormattedUrl`, `pagemap`, `image`. |
| `queries` | object | `request[]` and `nextPage[]` as in the CSE API. |

## Example response

```json
{
  "kind": "customsearch#search",
  "searchInformation": {
    "searchTime": 0.94,
    "formattedSearchTime": "0.94",
    "totalResults": "1240000",
    "formattedTotalResults": "1,240,000"
  },
  "items": [
    {
      "kind": "customsearch#result",
      "title": "asyncio — Asynchronous I/O",
      "htmlTitle": "<b>asyncio</b> — Asynchronous I/O",
      "link": "https://docs.python.org/3/library/asyncio.html",
      "displayLink": "docs.python.org",
      "snippet": "asyncio is a library to write concurrent code using the async/await syntax."
    }
  ],
  "queries": {
    "request": [
      {
        "searchTerms": "site:docs.python.org asyncio",
        "count": 10,
        "startIndex": 1
      }
    ]
  }
}
```

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_request` | A parameter is missing or invalid. |
| 401 | `unauthorized` | The API key is missing, invalid or revoked. |
| 402 | `insufficient_credits` | Your balance is too low. Buy a pack or wait for the monthly free grant. |
| 429 | `rate_limited` | Too many requests per second for your plan. Retry after the Retry-After header. |
| 503 | `upstream_error` | Google could not be fetched or parsed. Not billed; retry after Retry-After. |

All errors: https://serpkite.com/docs/errors

## Notes

- Errors use SerpKite's error shape (`{"error":{"code","message","request_id"}}`), not Google's.

## Related

- [Migrate from Google CSE](https://serpkite.com/docs/guides/migrate-from-google-cse)
- [CSE shutdown and alternatives](https://serpkite.com/migrate/google-custom-search-api)