Google Search Engine Data: Query Zenserp API

An app that stores Google results needs a response its code can inspect without parsing a results page. Zenserp returns Google search results as JSON, which Node.js can parse into named fields.

This article explains the request setup and the checks to make when a request fails.

TL;DR

Zenserp returns Google search results as JSON from its search endpoint, so a Node.js app can read fields instead of parsing a results page. Send a GET request with a query and keep the API key in an environment variable.

Use the apikey header instead of placing a secret in the URL because URLs can be copied into logs or history.

  • Use the /api/v2/search endpoint with the apikey request header.
  • Set location and language parameters to match the search you want.
  • Check the HTTP status before parsing the JSON response.

What Is Zenserp and Google Search Data?

A search engine results page, or SERP, is what Google returns for a query. Zenserp provides an API that returns data for a search query and its context.

A single SERP response answers one query in its supplied context. A ranking dataset can collect records across queries and dates. Use an API for a targeted application request or a dataset for broader analysis.

JSON is text organized as named fields and values. Node parses it into an object so an application can read a result title, URL or another field.

Example fieldWhat it representsHow an app can use it
organicAn array of organic search results in the vendor’s exampleLoop through entries after checking the response shape
positionThe displayed position of one resultStore the position with the query and date
titleThe result’s displayed titleShow a label or compare titles
urlThe result destination in the exampleLink to the result or save its destination
descriptionExample summary text for a resultShow a snippet or retain it with the record

I used the vendor homepage’s public response example to build the field table. Those names are illustrative, not live search results. Use it as a parser template because actual fields can vary with the query and search features.

How to Request Google Search Data with Zenserp in Node.js

Build the query URL before adding credentials because it makes the search context explicit before authentication.

Step 1: Check that Node provides fetch

I checked Node v26.7.0 in this workspace. Global fetch is available.

if (typeof fetch !== "function") {
  console.error("This Node runtime does not expose global fetch.");
  process.exit(1);
}

console.log(`Node ${process.version}: global fetch is available.`);

Command: node node_check.js

Node v26.7.0: global fetch is available.
[exit 0]
Terminal showing Node v26.7.0 with global fetch available
I captured and checked this screenshot against the node_check.js output.

Step 2: Build the query URL

I ran the URL builder for the sample query and confirmed it printed an encoded URL without sending a search request. Choose context settings from the table, then hold the query constant when comparing them.

const params = new URLSearchParams({
  q: "Pied Piper",
  location: "New York",
  gl: "us",
  hl: "en",
  device: "desktop",
  num: "10",
});

const url = new URL("https://app.zenserp.com/api/v2/search");
url.search = params.toString();
console.log(url.toString());

Command: node zenserp_query.js

https://app.zenserp.com/api/v2/search?q=Pied+Piper&location=New+York&gl=us&hl=en&device=desktop&num=10
[exit 0]
Terminal showing a Zenserp search URL for Pied Piper
The script prints the encoded URL and does not send an API request.
ParameterPurposeExample
qSearch phrasePied Piper
locationNamed place for localized resultsNew York
glCountry contextus
hlInterface languageen
deviceDevice contextdesktop
numRequested result count, up to 10010
startOffset for a later results pageUse the documented offset

Use start to fetch a later page. Verify the accepted offset in current docs. Keep the query and search context fixed across pages, then store each offset with its response so an application can assemble the requested range.

Step 3: Send the request with the API key

Get an API key from Zenserp and make it available to the Node process as ZENSERP_API_KEY. The current migration guide uses the apikey header, so this program does not put the secret in its source code or request URL.

const apiKey = process.env.ZENSERP_API_KEY;

if (!apiKey) {
  console.log("ZENSERP_API_KEY is not set; no request sent.");
  process.exit(0);
}

const url = new URL("https://app.zenserp.com/api/v2/search");
url.searchParams.set("q", "Pied Piper");

const response = await fetch(url, {
  headers: { apikey: apiKey },
});
const body = await response.text();

if (!response.ok) {
  console.error(`Request failed: HTTP ${response.status}`);
  console.error(body);
  process.exitCode = 1;
} else {
  const data = JSON.parse(body);
  console.log(JSON.stringify(data, null, 2));
}

Command: node zenserp_search.js

ZENSERP_API_KEY is not set; no request sent.
[exit 0]
Terminal showing the missing-key message before a Zenserp request
I captured and checked this screenshot against the missing-key execution.

I ran the request file with ZENSERP_API_KEY unset. It stopped before fetch and printed the message above.

With a key present, the program sends a GET request with the key in the apikey header and reads the body once as text. It checks response.ok. On success it parses JSON, and on an HTTP error it prints the status and body.

What Should You Check When a Zenserp Request Fails?

Use the table to choose the next check because each error points to a different request layer.

What you seeWhat to checkNext action
The program prints a missing-key messageThe environment variable is empty or unavailable to NodeSet the secret in the environment that launches Node, then rerun the file
HTTP error statusRead the returned status and body without printing your keyCheck the credential or request before retrying
No HTTP status appearsFetch threw before a response arrivedCatch the transport exception separately
The response succeeds but a field is missingCompare the actual JSON with the fields your code expectsHandle optional data instead of assuming every result has the same shape
Results use the wrong search contextReview the geographic and language settings in the requestRecord those inputs with the response before comparing results

A blocked Google page is one reason developers look for a results API, but a service can still reject a request when the credentials or query are invalid. Treat this API as a documented provider interface and follow its terms.

  • Confirm the environment variable exists before starting Node.
  • Read the HTTP status and response body without exposing the key.
  • Keep the status code with a retry so its outcome remains visible.

Zenserp’s current pricing page says successful requests are billed. Estimate a recurring workflow by its query, context and page count, then compare expected call volume with the current plan details.

Conclusion: Start with a Reproducible Search Request

Start with one query and explicit context, then add result fields after an authenticated request returns data.

For a broader Node request example, see CodeForGeek’s REST API guide. If your task is scraping page HTML rather than retrieving Google search results, the separate ZenScrape article covers that different workflow.

FAQ

Use the current API documentation for request details and the live pricing page for billing because both can change.

How do I keep the Zenserp API key out of my Node.js file?

Read it from an environment variable and pass it in the apikey request header. Do not commit the key or place it in a shared URL.

Which Zenserp parameter changes the search location?

Use location for a named place. The gl parameter supplies country context, while hl supplies language context. Check the migration guide for current accepted values.

Does Zenserp bill every request?

Zenserp’s current pricing page says successful requests are billed. Review the current plan details before relying on that for a specific error or quota case.

Pankaj Kumar
Pankaj Kumar

Pankaj Kumar is the founder and CEO of CodeForGeek, with more than 14 years in IT. He is an open-source enthusiast who enjoys sharing what he learns through CodeForGeek and YouTube, with a focus on Python, data analytics, machine learning, Angular, Node.js, and Kafka.

Articles: 337