// migration guide
Migrate from Serper.dev to Litescrape in one line
Move supported Serper /search calls to Litescrape with one configuration line for your base URL and API key. Pay $0.15 per 1,000 successful requests.
If your client reads SERPER_BASE_URL and SERPER_API_KEY, you can move supported individual /search calls to Litescrape with one configuration line. Set the base URL and a Litescrape-issued key. The endpoint accepts the same X-API-KEY header and supported query fields, then returns searchParameters, organic and mapped rich results. Litescrape charges $0.15 per 1,000 successful requests.
01 / configuration
Change the base URL and key
If your product reads SERPER_BASE_URL and appends /search, set the base URL to https://api.litescrape.com. Put your Litescrape key in SERPER_API_KEY. Keep /search out of the base URL so the client does not append it twice.
export SERPER_BASE_URL="https://api.litescrape.com" SERPER_API_KEY="ls_live_your_litescrape_key" An environment variable only works when your client reads it. If your integration hardcodes https://google.serper.dev/search, change that full URL to https://api.litescrape.com/search. If it exposes a full endpoint setting, use the full URL there too. A Serper-issued key will not work on Litescrape.
curl 'https://google.serper.dev/search' \ --header "X-API-KEY: $SERPER_API_KEY" \ --header 'Content-Type: application/json' \ --data '{"q":"coffee grinders","num":5,"gl":"us","hl":"en"}' The route also accepts Authorization: Bearer with a Litescrape key. When both authentication headers are present, X-API-KEY takes precedence. Keep keys in headers; keys in the URL or JSON body are not supported.
02 / runnable client
Keep the code that reads organic results
Save this file as serper_migration.py. It needs Python 3.10 or newer and httpx, reads the two settings above, and raises an HTTP error for unsuccessful responses. The server handles the field conversion.
"""Call Litescrape's Serper-compatible web-search route.
Requires Python 3.10+ and httpx. Set SERPER_BASE_URL and SERPER_API_KEY.
The key must be issued by Litescrape when using api.litescrape.com.
python serper_migration.py "coffee grinders" --num 5 --gl us --hl en
"""
from __future__ import annotations
import argparse
import json
import os
from typing import Any
import httpx
def search(query: str, **parameters: Any) -> dict[str, Any]:
base_url = os.environ["SERPER_BASE_URL"].rstrip("/")
response = httpx.post(
f"{base_url}/search",
headers={"X-API-KEY": os.environ["SERPER_API_KEY"]},
json={"q": query, **parameters},
timeout=90.0,
)
response.raise_for_status()
return response.json()
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("query")
parser.add_argument("--num", type=int, default=10)
parser.add_argument("--page", type=int, default=1)
parser.add_argument("--gl", default="us")
parser.add_argument("--hl", default="en")
parser.add_argument("--location")
args = parser.parse_args()
parameters = {
name: value
for name, value in vars(args).items()
if name != "query" and value is not None
}
print(json.dumps(search(args.query, **parameters), indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()python -m pip install httpxpython serper_migration.py "coffee grinders" --num 5 --gl us --hl en Your existing loop over result["organic"] can stay in place for the mapped fields. This is an illustrative response showing the JSON structure, not a captured search result or a guarantee of how many rows Google will return.
{ "searchParameters": { "q": "coffee grinders", "gl": "us", "hl": "en", "num": 5, "page": 1, "autocorrect": true, "type": "search", "engine": "google" }, "organic": [ { "title": "Example coffee grinder guide", "link": "https://example.com/coffee-grinders", "snippet": "An illustrative result showing the supported field names.", "position": 1 } ], "credits": 1}03 / parameters
The supported request fields
POST /search accepts one JSON object, or query-string parameters with an empty body. GET /search accepts the same query-string fields. When a field appears in both the query string and JSON body, the JSON value wins.
| Serper field | Default | Litescrape behavior |
|---|---|---|
q | Required | Non-empty query, up to 2,048 characters. |
gl | us | Lowercase two-letter country code. |
hl | en | Google language code, such as en or fr. |
location | Unset | Named search origin, using the native Google Search location rules. |
num | 10 | Integer from 1 to 100. Caps organic rows; Google may supply fewer. |
page | 1 | Positive integer. Translates to start = (page - 1) * num. |
autocorrect | true | false disables spelling correction through nfpr=1. |
tbs | Unset | Native Google filter, such as qdr:w for the past week. |
safe | Unset | active or off. |
type | search | Only search is accepted. |
engine | google | Only google is accepted. |
Use JSON numbers for num and page, and a JSON boolean for autocorrect. Query strings use decimal numbers and true or false. Optional null values are treated as omitted. Unknown fields, repeated query-string fields and malformed JSON are rejected before billing. The JSON body limit is 16 KiB.
04 / response
What your parser receives
Successful responses always contain searchParameters, organic and credits: 1. The parameters show the effective values, including defaults. organic can be empty. Other modules depend on what Google supplies and which fields are mapped into the response.
| Serper-compatible field | Source in native Litescrape Search |
|---|---|
organic | organic_results, capped at num. Maps title, link, snippet, date and attributes when present. |
organic[].position | One-based rank within the returned page. |
organic[].sitelinks | Inline and expanded sitelinks flattened into title/link objects. |
organic[].imageUrl | The organic result's thumbnail. |
organic[].rating, ratingCount | Detected rich-snippet rating and review count. |
answerBox | answer_box, including mapped answer, snippet and highlighted words. |
knowledgeGraph | knowledge_graph, with mapped image, description source/link and attributes. |
peopleAlsoAsk | related_questions: question, snippet, title and link when present. |
relatedSearches | related_searches, keeping the query field. |
topStories | top_stories, with title, link, source, date and thumbnail as imageUrl. |
Read optional modules defensively, for example result.get("peopleAlsoAsk", []). Native search_metadata, native pagination and modules outside this map are not part of the compatibility response. Use the native API reference if your application needs Litescrape's full response format.
05 / limits
Keep page size fixed while paging
With num=10, pages 1, 2 and 3 request native offsets 0, 10 and 20. With num=5, page 2 requests offset 5. Changing num midway changes the offset calculation and can introduce gaps or overlap.
from serper_migration import search # page=2, num=10 maps to Google's start=10.result = search("coffee grinders", num=10, page=2, gl="us", hl="en")for item in result["organic"]: print(item["position"], item["title"], item["link"]) Positions restart at 1 on each returned page. Google can return fewer rows than num; a short page alone is not a reliable end-of-results signal. Set a page budget and deduplicate links if you collect several pages. The endpoint does not return a next-page token or promise access to an unlimited result set.
Send individual requests for multiple queries. JSON array batches are rejected, and changing type does not turn this route into another Serper endpoint. Maps, reviews, shopping and other Litescrape products have their own native routes and response contracts.
06 / operations
Check the HTTP status before parsing organic
Errors use {"message":"The API key is invalid.","statusCode":401}. The HTTP status is authoritative. X-Request-ID and applicable Retry-After headers are preserved.
| Status | Meaning | Client action |
|---|---|---|
400 | Invalid or unsupported input | Fix the request before retrying. |
401 | Missing or invalid key | Check that the header contains a Litescrape key. |
402 | No calls remaining | Check the key balance and top up. |
429 | Key concurrency limit reached | Reduce in-flight requests and honor Retry-After. |
500, 503 | Internal failure, upstream unavailability or request deadline | Use bounded retries with backoff; keep the request ID for diagnosis. |
Each successful request consumes one Litescrape call, including a successful response with an empty organic array. Authentication and validation failures consume none; worker, rendering and deadline failures refund the reservation. The response's credits: 1 is the cost of that successful request, not your remaining balance.
These requests share your key's balance and concurrency allowance with native endpoints. Check GET /api/keys/status with Authorization: Bearer for remaining_calls and concurrency_limit. That status request consumes no call. The default configured concurrency is 25; this counts requests in flight, not requests per second.
07 / pricing
$0.15 per 1,000 successful searches
Serper's published prepaid rates, checked September 19, 2026, depend on the pack size. The table compares unit rates; the pack price is the upfront Serper purchase needed for that rate.
| Serper pack | Serper upfront purchase | Serper per 1,000 | Litescrape per 1,000 |
|---|---|---|---|
| Starter | $50 for 50,000 credits | $1.00 | $0.15 |
| Standard | $375 for 500,000 credits | $0.75 | $0.15 |
| Scale | $1,250 for 2.5 million credits | $0.50 | $0.15 |
| Ultimate | $3,750 for 12.5 million credits | $0.30 | $0.15 |
At the Starter unit rate, Litescrape costs 85% less per search. Against Ultimate's unit rate, it costs 50% less. One million successful Litescrape requests consume $150 of credits. These comparisons exclude taxes and free trial allowances.
Litescrape starts with 10 free calls and paid top-ups from $10. Credits last six months from each top-up. See Litescrape pricing for the purchase terms and current rates.
If you are also replacing Google's older client, the Custom Search JSON API migration guide covers its separate parameter and response mapping.
08 / questions
FAQ
- Can I keep my existing Serper API key?
- Keep the setting name, but replace its value with a Litescrape-issued key. A Serper-issued key cannot authenticate with Litescrape.
- Is changing SERPER_BASE_URL enough?
- Only if your client reads that setting and appends /search. Set it to https://api.litescrape.com and replace the key. Clients with a hardcoded URL need that URL changed explicitly.
- Will my organic result parser still work?
- The response includes organic with title, link, snippet and page-local position when the source supplies those fields. Check any rich modules or extra fields your application uses against the response map.
- Does this endpoint support Serper batches, Images, News or Places?
- No. It accepts one Google web-search query per request on /search. Array batches and other Serper paths are outside its scope. Other Litescrape endpoints use their native request and response formats.
- Does Litescrape forward my request to Serper?
- No. The compatibility route uses Litescrape's Google Search worker and translates the response into the supported Serper fields.
- Are the rankings and rich results identical?
- No identical-ranking guarantee is made. Google chooses the results and modules for each request. This endpoint returns the fields listed in the response map.