Google Search Results JSON API

A search feature needs links it can display in an application. A search results JSON API returns search data as fields a program can read.

I’ll distinguish Serpstack from Google’s Custom Search JSON API, then show how to make and parse a Serpstack request.

TL;DR

Use a SERP data API such as Serpstack when your program needs Google search results as JSON. Google’s Custom Search JSON API serves a configured Programmable Search Engine, is closed to new customers and is scheduled to end on January 1, 2027.

  • Serpstack’s current Free plan includes 100 searches a month, HTTPS and JSON output for non-commercial use.
  • Send the query to Serpstack’s HTTPS search endpoint and read ordinary result links from organic_results.
  • The code below reads the vendor’s published sample offline. It does not make a live API request or prove your key works.

What Is a Google Search Results JSON API?

A Google search results JSON API returns search data as fields a program can read. SERP means search engine results page, the page listing links and other features for a query. I compared Google’s current API notice with Serpstack documentation to distinguish the products and their request paths.

Serpstack is a third-party SERP provider with an HTTPS endpoint. A request includes an access key and a search query. Its organic_results array holds ordinary web links for the request.

JSON:API is a resource-oriented API specification. Google’s Custom Search JSON API is a separate product that returns results for a configured Programmable Search Engine and requires its engine ID. Google says new customers cannot sign up and existing users must transition by January 1, 2027.

ChoiceWhat it returnsWhat to check
SerpstackStructured Google SERP data from a provider endpoint.Plan quota, commercial terms, result fields and supported parameters.
Google Custom Search JSON APIJSON results for a configured Programmable Search Engine.Check the configured engine ID and migration status.
Direct HTML parsingPage markup that your code must inspect and maintain.Changes to markup and access controls add maintenance work.

Choose an endpoint by the application’s data needs, not by the word JSON in its name. Check that the provider’s query settings, result groups, required regions and commercial terms match the intended use. Run a representative query with the fields your code expects, then save the response as a fixture and verify optional properties before the caller relies on them.

A response is a snapshot of one query. The position field orders links within that response, so interpret it with the search query that produced it. It cannot predict what the next query will return or its order.

How to Get Google Search Results as JSON With Serpstack

Make an HTTPS GET request to Serpstack’s search endpoint, provide an access key and query, then parse the JSON response. This example uses Node.js 18 or later, where fetch() is available without installing an HTTP package. For more on making a Node.js HTTP request and handling its response, read CodeForGeek’s guide to RESTful APIs in Node.js.

Step 1: Create a key and choose a plan

Create a Serpstack account and obtain its access key from the provider. The key is required before the live endpoint returns a search response.

The access key identifies your Serpstack account. The query is the phrase you want to search. Keep the key in the Node process environment, outside source control and browser code, and redact request URLs in logs.

Set the phrase deliberately, and hold Serpstack’s documented country, language and device parameters constant when comparing requests because those settings can change the results.

Step 2: Save the request and parser

Save this complete program as google_search_results_json.js to run it from Node. It makes a live request by default, using an environment key. The –sample branch reads one saved result and tests parser output without a key or network, but it does not verify live access.

The response has request metadata, search_parameters and an organic_results array. The live branch uses fetch and checks HTTP status before parsing JSON. Build fixtures with valid and malformed rows to test the filter before using live data.

The live code reads SERP_QUERY when set and otherwise searches for mcdonalds. The URL object keeps the fixed endpoint separate from the access key, query, engine and Google domain parameters, and encodes spaces and punctuation in the query. Set SERP_QUERY in the environment that launches Node to search another phrase.

const ENDPOINT = "https://api.serpstack.com/search";

const sampleResponse = {
  request: { success: true },
  search_parameters: {
    engine: "google",
    query: "mcdonalds",
    domain: "google.com",
    device: "desktop",
  },
  organic_results: [
    {
      position: 1,
      title: "McDonald's: Burgers, Fries & More. Quality Ingredients.",
      url: "https://www.mcdonalds.com/us/en-us.html",
    },
  ],
};

function organicLinks(payload) {
  const rows = Array.isArray(payload.organic_results)
    ? payload.organic_results
    : [];
  return rows.filter(
    (item) => Number.isInteger(item.position)
      && typeof item.title === "string"
      && typeof item.url === "string",
  );
}

function printLinks(payload) {
  const links = organicLinks(payload);
  console.log(`Organic links: ${links.length}`);
  for (const item of links) {
    console.log(`${item.position}\t${item.title}\t${item.url}`);
  }
}

async function fetchLive() {
  const accessKey = process.env.SERPSTACK_ACCESS_KEY;
  if (!accessKey) {
    throw new Error("Set SERPSTACK_ACCESS_KEY before a live request.");
  }

  const url = new URL(ENDPOINT);
  url.searchParams.set("access_key", accessKey);
  url.searchParams.set("query", process.env.SERP_QUERY || "mcdonalds");
  url.searchParams.set("engine", "google");
  url.searchParams.set("google_domain", "google.com");

  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Serpstack returned HTTP ${response.status}.`);
  }

  const payload = await response.json();
  if (payload.request?.success !== true) {
    throw new Error(payload.error?.info || "Serpstack reported a request error.");
  }
  return payload;
}

async function main() {
  const payload = process.argv.includes("--sample")
    ? sampleResponse
    : await fetchLive();
  printLinks(payload);
}

main().catch((error) => {
  console.error(error.message);
  process.exitCode = 1;
});

The filter keeps a result only when its position is an integer and its title and URL are strings, which makes the ordinary-link output predictable. It skips objects without those link fields, including results that may represent another feature type. Add separate normalizers for features your application uses, and test missing optional values such as a snippet instead of rejecting a whole response.

Keep provider parsing inside organicLinks() so the rest of the feature can consume a stable object. Test that adapter when the response schema changes.

Step 3: Run the parser on the published sample

I ran the sample option from the named JavaScript file. The terminal output shows the position, title and URL selected by the parser.

Run the same command from the directory where you saved the file:

node google_search_results_json.js --sample
Terminal capture of the sample command and its output
A terminal capture of the local sample run with its complete output.
pankaj@codeforgeek:~$ node google_search_results_json.js --sample
Organic links: 1
1	McDonald's: Burgers, Fries & More. Quality Ingredients.	https://www.mcdonalds.com/us/en-us.html

[exit 0]

The captured line contains position, title and URL separated by tabs. The screenshot and text show this exact execution and offer selectable output for copying. Position preserves returned order, and the count equals the sample’s organic_results array length.

Step 4: Make a live request and use its fields

To request current data, set SERPSTACK_ACCESS_KEY for the Node process and run the file without –sample. Set SERP_QUERY when you want a phrase other than the default. The program adds the key and query using URL and searchParams, which encode characters such as spaces for the request.

The live path checks response.ok before parsing JSON, then reads Serpstack’s request.success field.

Step 5: Handle the result type your feature needs

The sample parser passes ordinary links to its terminal formatter. Other SERP features may use different fields, so check the provider schema and keep each feature type in a separate parser path. For a second provider’s implementation, compare CodeForGeek’s Zenserp SERP data tutorial.

What Can Go Wrong With a Search Results JSON Request?

I built the table around the request stage that failed. A missing organic_results array signals an unexpected shape, while an empty array means the provider returned no ordinary links. Keep those states separate when the program displays a zero count.

CaseWhat you seeWhat to check
Missing keyThe program stops before making a request.Set SERPSTACK_ACCESS_KEY in the same environment that launches Node. Do not replace it with a value in source control.
HTTP errorThe code reports a non-success status and stops before JSON parsing.Check the endpoint, account access and returned status before parsing a body.
Provider reports a failed searchThe HTTP response arrives, but request.success is false.Read the provider’s error field and compare request parameters with the account settings.
JSON parsing failsThe response body cannot be read as JSON.Check whether the endpoint returned an error page or another unexpected body before treating it as a result object.
No organic result arrayThe parser prints zero links.Inspect the top-level response and confirm organic_results is present as an array for this query.
Quota or usage errorThe provider rejects a request after the account reaches its plan limit.Review account usage and the plan’s request rules before retrying.
An optional snippet is missingThe result has a title and URL but no snippet.Check whether the feature requires snippet text. Keep it optional if the provider response omits it.
Results differ by locationThe same words return different results or ordering.Compare the submitted country, language, Google domain and device before comparing positions.

For transient errors, check provider rate limits and plan quota before using a delayed, finite retry, and correct invalid keys or malformed queries before sending again.

As checked on October 9, 2026, the Free plan lists 100 searches per month for non-commercial use, with HTTPS and JSON output, while Basic lists 5,000 searches per month at $29.99 monthly or $26.99 with annual billing. Estimate query volume from user actions and recurring jobs, then confirm current quota and commercial terms before purchase.

If search runs as a user types, sending a request on every keystroke can multiply query volume. Trigger the request on submit or an intentional pause, and consider caching only when provider terms and freshness requirements allow it.

If an integration still depends on Google’s endpoint, consult the official migration notice and confirm the account’s replacement path before changing code. Inventory the request location and downstream consumers before switching providers.

For diagnostics, retain the status and safe search context, and check provider guidance before saving a full response that may include request metadata.

Conclusion

For current Google SERP data in an application, use a provider endpoint such as Serpstack and parse the fields your task needs. Verify account terms and run one representative query in a secure environment before connecting the response to a user-facing feature. The example holds engine and Google domain constant while letting SERP_QUERY select the phrase.

For the provider’s request parameters and plan terms, read the Serpstack API documentation and current Serpstack pricing. For Google’s product end date and request format, use the Custom Search JSON API overview and its REST request guide.

FAQ

These answers cover access to Google’s own API and the fields used by the Serpstack example.

Does Google provide a JSON API for its search results?

Google’s Custom Search JSON API returns results for a configured Programmable Search Engine. Google says it is closed to new customers and existing users must transition by January 1, 2027. A third-party SERP API such as Serpstack is a separate service for querying Google SERP data.

Which Serpstack JSON fields contain ordinary search links?

The published response sample places ordinary links in the organic_results array. A link object can include position, title and url. Check the current response for the query you send and filter out feature objects that do not have those fields. If you need another value such as a snippet, validate it separately before using it.

How many free Serpstack searches can I make?

The Serpstack pricing page currently lists 100 searches per month on the Free plan and labels it for non-commercial use. Check the plan page before you rely on a quota or price.

Does the –sample command test a live Serpstack request?

No. It reads the saved response in google_search_results_json.js and makes no network request. To check credentials or current endpoint behavior, run the live path separately from the server process that holds SERPSTACK_ACCESS_KEY.

What does request.success mean in the example?

The request.success field reports Serpstack’s search outcome after the response body is parsed. Read it alongside response.ok to identify which stage returned an error.

Can I use the Serpstack Free plan for commercial searches?

The current pricing page labels the Free plan for non-commercial use. It lists 100 searches per month. Review the current plan terms before sending commercial traffic.

Why might the same search phrase return different results?

Country, language, Google domain and device settings can affect the response. Compare those request values alongside the phrase before interpreting a changed result list.

What is the difference between JSON:API and a search results JSON API?

JSON:API is a convention for resource-oriented HTTP APIs. A search results JSON API returns search data in a provider-defined response structure.

What does search_parameters tell me?

In the sample payload, search_parameters records query settings such as engine, query, domain and device. Read it when confirming the request context for a result.

What should I check if organic_results is empty or missing?

An empty array means no ordinary links were returned in that payload. A missing array means the parser did not find the expected field, so inspect the response schema before changing the query.

How should I estimate API request volume?

Count calls triggered by the search workflow, scheduled jobs and intentional retries. A monthly website visitor count alone may not describe API usage.

Should a search box request data on every keystroke?

No. Wait for a submit or deliberate pause to avoid multiplying requests, then consider caching only if provider rules and data freshness permit it.

What does the position field mean?

The position field gives an entry’s place in the current response. Subsequent searches can return a different ordering, so treat it as response-specific.

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