serpix

google-search-api

Google Search API

One request returns a live Google results page as structured JSON: what ranked, in what order, from where. One credit per page.

Endpoint

GET/v1/search
bash
curl "https://api.serpix.io/v1/search?q=best+espresso+machine&hl=en" \
  -H "Authorization: Bearer $SERPIX_API_KEY"

The same parameters are accepted as a JSON body on POST /v1/search:

post variant
curl -X POST "https://api.serpix.io/v1/search" \
  -H "Authorization: Bearer $SERPIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "best espresso machine", "hl": "en"}'

Parameters

Query and localization
fieldtypedescription
qrequiredstringThe search query.
glstringCountry to search as, two letters. Defaults to the country of location when one is set, otherwise us.
hlstringInterface language, two letters. Changes the labels Google renders.
devicedesktop | mobileWhich layout Google serves. Mobile results differ from desktop.
crstringRestrict results to pages from these countries. Comma-separated two-letter codes, like fr,de.
lrstringRestrict results to pages in these languages. Comma-separated two-letter codes, like fr,de.

Search origin

Where the search is made from. A place name covers most work; coordinates are there for when a city is too coarse, and Google measures distance from them. They are two ways to write one thing, so a request carries one or the other.

Search origin
fieldtypedescription
locationstringCity-level place the search is made from, like Austin or Berlin, Germany. The canonical name it resolves to is echoed back as search_parameters.location_used.
latnumberLatitude of the search origin, for when a city is too coarse. Send with lon, and not with location.
lonnumberLongitude of the search origin. Send with lat.
radiusintegerDistance bias in metres around the origin. A bias, not a fence: prominent results outside it still rank.

Results and filtering

Results and filtering
fieldtypedescription
pageinteger1-based page number. Page 2 is the next ten results.
time_rangeh | d | w | m | yOnly pages from the past hour, day, week, month, or year. Mutually exclusive with after/before.
afterstringOnly pages published on or after this date, ISO YYYY-MM-DD.
beforestringOnly pages published on or before this date, ISO YYYY-MM-DD.
sortrelevance | datedate orders by recency instead of relevance.
verbatimbooleanSearch the words exactly: no synonyms, no stemming, no query expansion.
autocorrectbooleanfalse keeps results for the query as typed, even when Google would correct it. See spelling_fix in the response.
safeoff | activeTurn Google's explicit-results filter on.
filterbooleanfalse disables Google's similar-results dedup, so near-duplicate pages that are normally folded away still rank.

Rejected parameters

Unknown parameters are never silently ignored. A request carrying one is rejected with 422 before any search runs, so a typo can't bill you for results you didn't ask for. Common parameters from raw Google URLs and other SERP APIs get a pointed message naming the equivalent here:

Rejected aliases
fieldtypedescription
num422One page of about ten results per request. Paginate with page.
start422Use page, a 1-based page number, instead of a result offset.
tbs422Use time_range, after/before, sort, or verbatim.
nfpr422Use autocorrect, a boolean.
uule422Use location with a city name, or lat/lon. The encoding is handled for you.
google_domain422Searches run on google.com. Set the country and language with gl and hl, and the origin with location or lat/lon.
api_key422Authenticate with the Authorization: Bearer header.
engine422Not needed. This endpoint is Google web search.
422 · rejected parameter
HTTP/1.1 422 Unprocessable Entity

{
  "detail": "`num` is not supported: one page of ~10 results per request; paginate with `page`"
}

Response

A search returns one JSON object: three envelope objects that are always present, then the results themselves. A field Google didn't serve is omitted entirely, never null, never [].

Top level
fieldtypedescription
search_metadataobjectRequest id, status, timing, parser version, and the exact Google URL fetched.
search_parametersobjectThe vantage point this capture was taken from, so it can be run again the same way.
search_informationobjectThe query as Google echoed it, and how it handled a misspelling.
organic_resultsarrayThe ranked web results, in the order Google served them.
people_also_askstring[]The follow-up questions Google suggests, as plain strings.
related_searchesstring[]The refined queries at the bottom of the page, as plain strings.
paginationobjectThe current page and the URL of the next one.
GET /v1/search?q=best espresso machine
{
  "search_metadata": {
    "id": "srx_6-Z531iRST1L67tZ",
    "status": "success",
    "created_at": "2026-08-13T14:22:07Z",
    "took_ms": 812,
    "parser_version": "2026.08.18",
    "google_url": "https://www.google.com/search?q=best+espresso+machine&..."
  },
  "search_parameters": {
    "q": "best espresso machine",
    "gl": "us",
    "hl": "en",
    "device": "desktop",
    "page": 1
  },
  "search_information": {
    "query_displayed": "best espresso machine"
  },
  "organic_results": [
    {
      "position": 1,
      "title": "The 5 Best Espresso Machines [Tested 2026]",
      "link": "https://coffeechronicler.com/gear/espresso-machines/best/",
      "snippet": "We tested 14 machines. The Gaggia Classic Pro wins on build quality, while the Breville Bambino Plus is the better pick for beginners.",
      "date": "Jul 3, 2026"
    },
    {
      "position": 2,
      "title": "Best Espresso Machine: Our Top 5 Picks for 2026",
      "link": "https://www.coffeeness.de/en/best-espresso-machine/",
      "snippet": "Our top pick is the Gaggia Classic Evo, with a commercial steam wand and 58mm portafilter.",
      "sitelinks": [
        {
          "title": "Gaggia Classic Evo Review",
          "link": "https://www.coffeeness.de/en/gaggia-classic-evo-review/"
        }
      ]
    }
  ],
  "people_also_ask": [
    "Is a $500 espresso machine worth it?",
    "What is the best espresso machine for a beginner?",
    "Do I need a separate grinder?"
  ],
  "related_searches": [
    "best espresso machine under 500",
    "gaggia classic pro review",
    "best espresso machine with grinder"
  ],
  "pagination": {
    "current": 1,
    "next": "https://www.google.com/search?q=best+espresso+machine&start=10&..."
  }
}

Organic results

One entry per ranked page, in served order. position is the rank a rank tracker stores; date is split off the front of the snippet rather than left glued to it.

OrganicResult
fieldtypedescription
positionintegerRank on this page, starting at 1.
titlestringThe result's headline, as displayed.
linkstringThe destination URL. Google's opaque redirect is resolved, so this is the real page, never a /goto token.
snippetstringThe description text Google quoted from the page.
datestringPublication date as displayed, when Google shows one. Split off the snippet so the prose stays prose.
sitelinkslistExtra links Google nested under this result.

Search information

Google silently rewrites misspelled queries. When it does, the ranks you get belong to a different search than the one you sent — so it is stated explicitly rather than left for you to notice.

SearchInformation
fieldtypedescription
query_displayedstringThe query as Google echoed it in the search box.
spelling_fixobjectPresent only when Google corrected or suggested a spelling.
SpellingFix
fieldtypedescription
typestringcorrected means Google searched something else and these ranks are for that query. suggested means it only offered, and the ranks are for the query you sent.
originalstringThe query as typed, when Google replaced it.
corrected_tostringThe query Google corrected or suggested.
a corrected query
{
  "search_information": {
    "query_displayed": "portugual",
    "spelling_fix": {
      "type": "corrected",
      "original": "portugual",
      "corrected_to": "portugal"
    }
  }
}

Pagination

Ask for page 2 with page=2. next is absent on the last page, which is how you know to stop.

Pagination
fieldtypedescription
currentintegerThe page this response is for.
nextstringGoogle's URL for the following page. Absent on the last page.

Envelope

SearchMetadata
fieldtypedescription
idstringRequest id. Quote it in support.
statusstringAlways success on a 200.
created_atstringWhen the search ran, ISO 8601 UTC.
took_msintegerTotal time for the fetch and parse.
parser_versionstringThe parser build that produced this response.
google_urlstringThe exact URL fetched, including the encoded origin.
html_urlstringArchived raw HTML for this search, when archiving is on.
json_urlstringArchived JSON for this search, when archiving is on.
SearchParameters
fieldtypedescription
qstringThe query that was searched.
glstringThe country actually used, after defaults were applied.
hlstringThe interface language used.
devicestringThe layout requested.
pageintegerThe page requested.
location_requestedstringThe place name as you sent it.
location_usedstringThe canonical place it resolved to.
latnumberLatitude of the origin, when coordinates were sent.
lonnumberLongitude of the origin, when coordinates were sent.
radiusintegerDistance bias in metres, when one was sent.

Errors

You are charged for results, not for failures. A blocked page, a transport error, or a page we captured but could not read all cost nothing — the credit header on the response says so either way.

Status codes
fieldtypedescription
200chargedResults returned.
402freeNot enough credits. Nothing was fetched.
422freeThe request was rejected before any search ran.
502freeThe page was captured but could not be read. This is our problem, not yours, and it is logged as one.
503freeGoogle blocked the capture. Retry — a different session is used.
502 · charged nothing
HTTP/1.1 502 Bad Gateway
X-Serpix-Credits-Charged: 0

{
  "error": { "code": "search_failed", "message": "the page could not be parsed" }
}