Skip to content

Serper Integration

Official Serper API Documentation

Serper provides a fast and cost-effective way to get search results from Google. Use KeyPool to call Serper with your team token and a stable base URL.

KeyPool Endpoint for Serper

To interact with Serper via KeyPool, you will use the following base URL:

{YOUR_KEYPOOL_BASE_URL}/v1/serper

Authentication

Use your KeyPool team token as a bearer token when making requests to the KeyPool Serper endpoint.

Do not send a personal Serper API key when using KeyPool.

Key Features and Usage

KeyPool currently supports Serper's ten JSON POST endpoint families: /search, /images, /news, /maps, /places, /videos, /shopping, /scholar, /patents, and /autocomplete. The examples below show the same request pattern for web and image search.

The proxy does not impose an endpoint allowlist. For any Serper operation served by google.serper.dev, use the same path under {YOUR_KEYPOOL_BASE_URL}/v1/serper and send the provider-documented JSON body; KeyPool injects x-api-key and strips any client-supplied provider key. The public spec documents only the verified core families. Operations hosted on another provider host, such as Webpage on scrape.serper.dev, require a separate routing integration.

Perform a standard Google web search.

Python Example (using requests):

import os
import requests
import json

KEYPOOL_BASE_URL = os.environ.get("KEYPOOL_BASE_URL")
KEYPOOL_TOKEN = os.environ.get("KEYPOOL_TOKEN")

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {KEYPOOL_TOKEN}",
}

payload = {
    "q": "latest AI news",
    "num": 5, # Number of results
}

response = requests.post(f"{KEYPOOL_BASE_URL}/v1/serper/search", headers=headers, data=json.dumps(payload))
response.raise_for_status() # Raise an exception for HTTP errors

search_results = response.json()
print("Web Search Results:")
for result in search_results.get("organic", []):
    print(f"- {result.get('title')} ({result.get('link')})")

TypeScript Example (using fetch):

const KEYPOOL_BASE_URL = process.env.KEYPOOL_BASE_URL;
const KEYPOOL_TOKEN = process.env.KEYPOOL_TOKEN;

async function serperWebSearch() {
  const headers = {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${KEYPOOL_TOKEN}`,
  };

  const payload = {
    q: "latest AI news",
    num: 5,
  };

  const response = await fetch(`${KEYPOOL_BASE_URL}/v1/serper/search`, {
    method: "POST",
    headers: headers,
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const searchResults = await response.json();
  console.log("Web Search Results:");
  searchResults.organic.forEach((result: any) => {
    console.log(`- ${result.title} (${result.link})`);
  });
}

serperWebSearch();

Perform a Google image search.

Python Example (using requests):

import os
import requests
import json

KEYPOOL_BASE_URL = os.environ.get("KEYPOOL_BASE_URL")
KEYPOOL_TOKEN = os.environ.get("KEYPOOL_TOKEN")

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {KEYPOOL_TOKEN}",
}

payload = {
    "q": "cute puppies",
    "num": 3,
}

response = requests.post(f"{KEYPOOL_BASE_URL}/v1/serper/images", headers=headers, data=json.dumps(payload))
response.raise_for_status()

image_results = response.json()
print("Image Search Results:")
for result in image_results.get("images", []):
    print(f"- {result.get('title')} ({result.get('imageUrl')})")

TypeScript Example (using fetch):

const KEYPOOL_BASE_URL = process.env.KEYPOOL_BASE_URL;
const KEYPOOL_TOKEN = process.env.KEYPOOL_TOKEN;

async function serperImageSearch() {
  const headers = {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${KEYPOOL_TOKEN}`,
  };

  const payload = {
    q: "cute puppies",
    num: 3,
  };

  const response = await fetch(`${KEYPOOL_BASE_URL}/v1/serper/images`, {
    method: "POST",
    headers: headers,
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const imageResults = await response.json();
  console.log("Image Search Results:");
  imageResults.images.forEach((result: any) => {
    console.log(`- ${result.title} (${result.imageUrl})`);
  });
}

serperImageSearch();

Provider quotas and newly published endpoints

Serper's official homepage currently advertises 2,500 free queries. Paid plans advertise 50, 100, 200, or 300 queries per second depending on the plan. KeyPool's configured 60 requests/minute per key is an internal routing policy, not a vendor quota.

The first-party Playground also lists /reviews and /lens. They are not in KeyPool's current public contract yet. Its /webpage operation uses the separate scrape.serper.dev host and is not routed by this service.

Error Handling

KeyPool handles various error conditions gracefully, providing informative responses. Common error scenarios include:

  • 401 Unauthorized: Invalid or missing KeyPool Team Token.
  • 403 Forbidden: Your Team Token does not have permission to access the Serper service.
  • 429 Quota Exceeded: Your team's rate limit for Serper has been reached.
  • 503 No Available Keys: Serper is temporarily unavailable through your KeyPool workspace.
  • 502 Upstream Failed: The request to the Serper API failed (e.g., network error, Serper API down).

Interactive API Reference

For all Serper endpoints and in-browser testing, see API Reference → Serper.