Google SERP API: How to Get Google Search Results by API in 2026

Google SERP API: How to Get Google Search Results by API in 2026

TLDR: There is no open, official Google SERP API in 2026. Google's Custom Search JSON API is closed to new customers and shuts down on January 1, 2027, so live Google rankings now come from third-party SERP APIs such as SerpApi, Serper, SearchApi, and DataForSEO. Since Google dropped num=100, a top-100 check costs ten requests. If your application needs relevant pages rather than Google positions, a web search API can be the better fit.

This guide compares the routes, their September 2026 prices, and the num=100 cost math, with a tested adapter for four providers. For the SERP API concept and how providers work under the hood, see What Is a SERP API?

Is there an official Google SERP API?

Not one you can sign up for. Google's Custom Search JSON API overview states that the API is closed to new customers and that existing customers have until January 1, 2027 to move to an alternative. Google suggests Vertex AI Search for searching up to 50 domains and asks anyone who needs full-web search to contact it about a separate solution. The page lists no price for that option.

For existing customers, the same page lists the terms as of September 2026: 100 queries per day free, then $5 per 1,000 queries, up to 10,000 queries per day. The cse.list reference caps num at 10 results per request and says the API never returns more than 100 results for a query, so a start plus num above 100 returns an error.

The engine behind the API is narrowing as well. Google's Programmable Search Engine help says that as of January 20, 2026, new engines must use Sites to search, which is limited to 50 domains. Existing engines set to Search the entire web keep that option until January 1, 2027, and switching it off cannot be undone.

Even while it lasts, Custom Search is not a SERP feed. Its response schema returns items with a title, link, snippet, and PageMap data, plus spelling corrections and promotions. It has no fields for ads, People Also Ask, local packs, or AI Overviews. If you are moving an existing integration off it, the Google CSE alternative guide covers the inventory and pagination mapping.

What do third-party Google SERP APIs return and cost?

Third-party SERP APIs fetch Google's results page for you and return it as JSON. They exist because doing it yourself runs into Google's defenses. The google.com robots.txt disallows /search under User-agent: *, and Google says its SearchGuard system, launched in January 2025, sends a JavaScript challenge that large-scale automated clients typically cannot solve, as the court's July 2026 order summarizes it. Here is how four providers compare as of September 2026.

ProviderRequest shapeWhat comes backListed price, September 2026
SerpApiGET https://serpapi.com/search with engine=google, q, and api_key. Paginate with start=0, 10, 20.search_metadata plus organic_results (position, title, link, snippet), with local results, ads, Knowledge Graph, answer boxes, shopping, and video sections.Free: 250 searches a month. Starter $25 for 1,000, Developer $75 for 5,000, Production $150 for 15,000, Big Data $275 for 30,000, Searcher $725 for 100,000, Volume $1,475 for 250,000, Infrastructure $2,750 for 500,000, and Cloud plans from $3,750 for 1 million.
SerperPOST https://google.serper.dev/search with an X-API-KEY header and a JSON body such as {"q": "..."}.organic (title, link, snippet, position), knowledgeGraph, peopleAlsoAsk, relatedSearches.2,500 free queries. Prepaid credits from $1.00 per 1,000 (50,000 for $50) to $0.30 per 1,000 (12.5 million for $3,750), valid six months.
SearchApiGET https://www.searchapi.io/api/v1/search with engine=google and q. Key as api_key or a Bearer header. Paginate with page.organic_results (position, title, link, domain, snippet, date) and other SERP sections.$40 a month for 10,000 searches ($4 per 1,000), stepping down to $5,000 a month for 5 million ($1 per 1,000). Billed per successful search.
DataForSEOPOST https://api.dataforseo.com/v3/serp/google/organic/live/advanced with Basic auth and a JSON array of tasks.tasks[].result[].items[], each typed (organic, people_also_ask, local_pack, featured_snippet, ai_overview, and more) with rank_group and rank_absolute.Per 10-result page: $0.0006 in the Standard queue, $0.0012 in the Priority queue, $0.002 in Live mode.

Sources: SerpApi pricing and Google Search API docs, Serper pricing and playground, SearchApi pricing and Google Search API docs, DataForSEO pricing and Live Advanced docs.

The headline prices hide the constraints that matter in production. SerpApi's plans list a guaranteed hourly throughput, from 50 searches on Free to 20,000 on Searcher and 100,000 on Infrastructure, and count only successful searches. DataForSEO's Standard queue averages about five minutes per task against a 45-minute target, while Live mode averages up to six seconds and accepts up to 2,000 calls per minute. Serper says it queries Google in real time without caching and usually answers in one to two seconds. SearchApi also sells a separate Google Rank Tracking API that returns up to 100 organic results per request, which matters for the next section.

What did the num=100 change do to cost?

For years, adding &num=100 to a Google search URL returned 100 results on one page, and rank trackers used it to pull the top 100 in a single request. In mid-September 2025, Google stopped honoring it and told Search Engine Land: "The use of this URL parameter is not something that we formally support." SearchApi's Google Search API reference now describes num as phased out by Google in September 2025 and fixed at 10.

The cost effect is mechanical: a top-100 check now takes ten 10-result pages. DataForSEO documented the change most precisely. Its September 2025 update says its earlier price basis was fixed per 100 results, and its pricing FAQ now bills every 10-result page at the base rate. One hundred results cost $0.006 in the Standard queue, $0.012 in the Priority queue, and $0.02 in Live mode. Under the old basis, the same base prices ($0.0006, $0.0012, and $0.002) covered 100 results, so a deep check now costs ten times as much.

Here is what that means for a common workload: 1,000 keywords checked once a day, or 30,000 checks a month, at list prices as of September 2026.

RouteTop 10 (1 page per check)Top 100 (10 pages per check)
DataForSEO, Standard queue$18$180
DataForSEO, Live mode$60$600
SearchApi, Google engine$100 Production plan (35,000 searches)$900 Octo 500K plan (500,000 searches)
SerpApi$275 Big Data plan (30,000 searches)$2,750 Infrastructure plan (500,000 searches)
SerperAbout $30 of credits at the $1.00 per 1,000 rate (packs start at $50)The pricing page does not say how deeper pages are billed; test it
Google Custom Search (existing customers only, until January 1, 2027)About $135 after the daily free 100About $1,485, right at the 10,000-per-day cap

Depth is now the main cost lever. DataForSEO's own advice is to check less often or cut depth from 100 to 50, and a tool that reports only the top 20 should budget two pages, not ten. Plan shape matters too: credits and per-page queues scale in a straight line, while monthly plans jump at tier boundaries.

For contrast, You.com's Web Search API bills $5 per 1,000 calls with up to 100 results per call as of September 2026, so 30,000 calls cost $150 a month at either depth. That is more than DataForSEO's Standard queue at top-10 depth and less than every listed top-100 price above, but it is not like for like: You.com returns its own ranking, not Google positions. The num=100 migration note covers switching a SERP endpoint to You.com.

How do you keep your code provider-neutral?

Whichever vendor you pick, keep its response shape out of your application. The script below normalizes SerpApi, Serper, DataForSEO, and You.com responses into one list of position, URL, title, and snippet, and measures top-10 URL overlap between two providers on the same query. It uses only the Python standard library. Run it with no arguments and it executes fixture checks with zero network calls. Live calls require --live and a key in the named environment variable.

"""Normalize Google SERP API responses and You.com results into one shape.

Offline by default: `python3 serp_compare.py` runs fixture checks, no network.
Live is opt-in:      `python3 serp_compare.py --live you --against serpapi --query "crm software"`
"""
import argparse
import base64
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request

ENV = {"serpapi": "SERPAPI_API_KEY", "serper": "SERPER_API_KEY",
       "dataforseo": "DATAFORSEO_AUTH", "you": "YDC_API_KEY"}  # DATAFORSEO_AUTH is "login:password"


def post_json(url, headers, body):
    headers = dict(headers, **{"Content-Type": "application/json"})
    data = json.dumps(body).encode("utf-8")
    req = urllib.request.Request(url, data=data, headers=headers, method="POST")
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.load(resp)


def fetch(provider, query, key):
    if provider == "serpapi":
        qs = urllib.parse.urlencode({"engine": "google", "q": query, "api_key": key})
        with urllib.request.urlopen("https://serpapi.com/search?" + qs, timeout=30) as resp:
            return json.load(resp)
    if provider == "serper":
        return post_json("https://google.serper.dev/search", {"X-API-KEY": key}, {"q": query})
    if provider == "dataforseo":
        token = base64.b64encode(key.encode("utf-8")).decode("ascii")
        task = {"keyword": query, "location_code": 2840, "language_code": "en", "depth": 10}
        return post_json("https://api.dataforseo.com/v3/serp/google/organic/live/advanced",
                         {"Authorization": "Basic " + token}, [task])
    if provider == "you":
        return post_json("https://ydc-index.io/v1/search", {"X-API-Key": key},
                         {"query": query, "count": 10})
    raise ValueError("unknown provider: " + provider)


def row(position, url, title, snippet):
    return {"position": position, "url": url, "title": title, "snippet": snippet}


def normalize(provider, data):
    """Return organic results as [{position, url, title, snippet}] in rank order."""
    if provider == "serpapi":
        if data.get("error"):
            raise RuntimeError("SerpApi error: " + str(data["error"]))
        hits = [h for h in data.get("organic_results") or [] if h.get("link")]
        return [row(h.get("position"), h["link"], h.get("title"), h.get("snippet")) for h in hits]
    if provider == "serper":
        hits = [h for h in data.get("organic") or [] if h.get("link")]
        return [row(h.get("position"), h["link"], h.get("title"), h.get("snippet")) for h in hits]
    if provider == "dataforseo":
        items = []
        for task in data.get("tasks") or []:
            for result in task.get("result") or []:
                items += [i for i in result.get("items") or []
                          if i.get("type") == "organic" and i.get("url")]
        return [row(i.get("rank_group"), i["url"], i.get("title"), i.get("description")) for i in items]
    if provider == "you":
        web = (data.get("results") or {}).get("web") or []
        out = []
        for hit in (h for h in web if h.get("url")):
            snippets = hit.get("snippets") or []
            snippet = snippets[0] if snippets else hit.get("description")
            # You.com has no Google rank; position here is list order only.
            out.append(row(len(out) + 1, hit["url"], hit.get("title"), snippet))
        return out
    raise ValueError("unknown provider: " + provider)


def url_key(url):
    parts = urllib.parse.urlsplit(url)
    host = parts.netloc.lower()
    host = host[4:] if host.startswith("www.") else host
    return host + (parts.path.rstrip("/") or "/")


def overlap_at_k(a, b, k=10):
    """Share of the top-k URLs two result lists have in common, 0.0 to 1.0."""
    top_a = {url_key(r["url"]) for r in a[:k]}
    top_b = {url_key(r["url"]) for r in b[:k]}
    return len(top_a & top_b) / float(k)


def monthly_cost(keywords, checks_per_month, pages_per_check, price_per_1k):
    """Cost when every 10-result page is a billed request (the post-num=100 norm)."""
    return keywords * checks_per_month * pages_per_check * price_per_1k / 1000.0


FIXTURES = {
    "serpapi": {"search_metadata": {"status": "Success"}, "organic_results": [
        {"position": 1, "title": "A", "link": "https://www.example.com/a/", "snippet": "a"},
        {"position": 2, "title": "B", "link": "https://example.org/b", "snippet": "b"}]},
    "serper": {"searchParameters": {"q": "x"}, "organic": [
        {"position": 1, "title": "A", "link": "https://example.com/a", "snippet": "a"}]},
    "dataforseo": {"tasks": [{"result": [{"items": [
        {"type": "people_also_ask", "rank_group": 1, "rank_absolute": 1},
        {"type": "organic", "rank_group": 1, "rank_absolute": 2, "title": "A",
         "url": "https://example.com/a", "description": "a"}]}]}]},
    "you": {"results": {"web": [
        {"url": "https://example.com/a", "title": "A", "description": "static",
         "snippets": ["query-relevant a"]},
        {"url": "https://example.net/c", "title": "C", "description": "fallback"}]},
        "metadata": {"query": "x"}},
}


def offline_checks():
    google = normalize("serpapi", FIXTURES["serpapi"])
    you = normalize("you", FIXTURES["you"])
    assert [r["position"] for r in google] == [1, 2]
    assert normalize("serper", FIXTURES["serper"])[0]["url"] == "https://example.com/a"
    dfs = normalize("dataforseo", FIXTURES["dataforseo"])
    assert len(dfs) == 1 and dfs[0]["position"] == 1 and dfs[0]["snippet"] == "a"
    assert you[0]["snippet"] == "query-relevant a" and you[1]["snippet"] == "fallback"
    assert overlap_at_k(google, you, k=2) == 0.5  # www. and trailing slash are ignored
    assert normalize("you", {"results": {}}) == [] and normalize("serper", {}) == []
    assert monthly_cost(1000, 30, 1, 0.60) == 18.0 and monthly_cost(1000, 30, 10, 0.60) == 180.0
    assert round(monthly_cost(1000, 30, 10, 2.00), 2) == 600.0
    try:
        normalize("serpapi", {"error": "Invalid API key."})
    except RuntimeError:
        pass
    else:
        raise AssertionError("SerpApi error payload was accepted")


def search(provider, query):
    key = os.environ.get(ENV[provider], "").strip()
    if not key:
        raise SystemExit("Set %s before using --live %s" % (ENV[provider], provider))
    try:
        return normalize(provider, fetch(provider, query, key))
    except urllib.error.HTTPError as err:
        raise SystemExit("%s returned HTTP %d" % (provider, err.code))


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--live", choices=sorted(ENV))
    parser.add_argument("--against", choices=sorted(ENV))
    parser.add_argument("--query", default="google serp api")
    args = parser.parse_args()
    if not args.live:
        offline_checks()
        print("offline checks passed, 0 network calls")
        return 0
    rows = search(args.live, args.query)
    for r in rows:
        print(r["position"], r["url"])
    if args.against:
        other = search(args.against, args.query)
        print("overlap@10 vs %s: %.1f" % (args.against, overlap_at_k(rows, other)))
    return 0


if __name__ == "__main__":
    sys.exit(main())

The request shapes follow each provider's documentation as fetched in September 2026: SerpApi's GET endpoint with engine=google and api_key, Serper's POST with an X-API-KEY header, DataForSEO's Live Advanced endpoint with Basic authentication and an array of tasks, and You.com's POST to ydc-index.io/v1/search with an X-API-Key header, reading results.web as the search reference specifies. Two choices are deliberate. DataForSEO items are filtered to type organic and ranked by rank_group, which counts only elements of the same type, rather than rank_absolute, which counts every element on the page. You.com results carry no Google rank, so the adapter numbers them in list order and reads the snippets array before falling back to description.

The overlap score divides by k, so a provider that returns fewer than ten results scores lower; treat it as a screening signal, not a quality verdict. We checked the script with py_compile, its fixture checks, and a mocked-HTTP test of each provider's method, URL, headers, and body. No live requests were made, so relevance, latency, and billing still need your own test.

Where does You.com's Web Search API fit, and where does it not?

You.com is not a Google SERP API, and this guide does not treat it as one. Its documentation describes an independent index operated by You.com, not resold from a third party. Each web result carries a URL, title, description, snippets, page_age, and optional extracted contents. There is no Google position, ad, local pack, or AI Overview field.

It fits when:

  • A model or agent is the consumer. An LLM needs relevant pages and text, not Google's layout. One POST returns web results, plus news when the query calls for it, and the extraction parameter can add query-relevant highlights or full-page Markdown to the same response.
  • You need depth without pagination. The billing page lists up to 100 results per call at $5 per 1,000 calls as of September 2026. Full-page extraction adds $1 per 1,000 pages crawled live, and cached pages are free under the default blend source.
  • You want no dependency on Google's results page. Because the index is operated by You.com rather than scraped from google.com, changes like num=100 or new anti-bot measures on Google's side are not a dependency of your integration.

It does not fit when:

  • The metric is your position on Google. Rank tracking and SEO reporting need Google-sourced data. Use a SERP API.
  • You monitor SERP features. Ads, local packs, People Also Ask, and AI Overviews exist only on Google's page, which is why DataForSEO types them as separate items.
  • You run cheap, shallow checks at high volume. At top-10 depth, DataForSEO's Standard queue at $0.60 per 1,000 pages costs less than $5 per 1,000 calls, if a five-minute average turnaround works for you.

Many teams need both: a SERP API for the ranking report and a web search API for the model that reads the pages. The web search API guide covers response anatomy and content tiers.

What should you test before you commit?

  1. Field coverage on your queries. Run a sample of real keywords for each location and device you report, and confirm the elements you depend on appear.
  2. Depth billing. Request page two and a top-100 check, then read the usage log. DataForSEO refunds unused depth automatically when a SERP has fewer results than requested; confirm how other vendors count each page.
  3. Failure accounting. SerpApi does not count cached, errored, or failed searches, Serper deducts credits for successful responses, and SearchApi bills per success. Log the status of every request so a failed page never turns into an empty ranking.
  4. Throughput and latency. Guaranteed hourly throughput at SerpApi, queue turnaround at DataForSEO, and per-second limits at Serper (50 to 300 queries per second, depending on the credit pack) shape your batch windows as much as price does.
  5. Parity with a manual check. Compare a handful of results against a browser search from the target market, since location and device settings change what Google shows.
  6. An exit path. Keep an adapter like the one above between the vendor and your application so a switch is a configuration change.

Vendor risk belongs on the list too. Google sued SerpApi on December 19, 2025, alleging that it circumvented SearchGuard in violation of the DMCA. On July 20, 2026, the court dismissed those claims, part of them permanently and part with leave to amend, as Search Engine Land reported, and Google filed an amended complaint on August 10, 2026. SerpApi and SearchApi both advertise up to $2 million in legal coverage for their collection and parsing of search results, with conditions tied to how customers use the data. This is not legal advice, just a reason to read each vendor's terms and keep a second provider tested.

Which route should you choose?

What you needRouteWhy
Google positions for SEO reportingA SERP API with explicit depth controlOnly Google-sourced data answers where you rank. Use a queue for batch work and Live or real-time plans for on-demand checks.
Ads, local packs, or AI OverviewsA SERP API that returns typed SERP elementsThese features exist only on Google's results page.
An existing Custom Search integrationMigrate before January 1, 2027The API discontinues on that date, and new engines are already limited to 50 domains.
Pages and text for an LLM or agentA web search API such as You.com'sUp to 100 results per call, optional page content, and no dependency on scraping Google.
Both rankings and model contextSplit the two pathsRanking data and model context have different depth, freshness, and cost needs.

Before you sign, run the same keyword set through two candidates and log what each returns and bills. The web search API evaluation guide shows how to keep that comparison fair, and new You.com accounts start with $100 in free API credits.

    Share Article:

  1. LI Test

  2. LI Test

Related resources.

How to Use the You.com Web Search API in TypeScript

How to Use the You.com Web Search API in TypeScript

September 22, 2026

Blog

How to Build a News Search Pipeline With the You.com Web Search API

How to Build a News Search Pipeline With the You.com Web Search API

September 22, 2026

Blog

What Is the You.com Web Search API? A Practical Guide for Developers

What Is the You.com Web Search API? Endpoint, Pricing, and Limits

September 22, 2026

Blog

How Authentication Works in the You.com Web Search API

How Authentication Works in the You.com Web Search API

September 21, 2026

Blog

How to Use Date Filters With the You.com Web Search API

How to Use Date Filters With the You.com Web Search API

September 21, 2026

Blog