Migration guide · LangChain

Move LangChain's Google Search tools off the Custom Search JSON API

LangChain's Google Search tools call the Custom Search JSON API, which Google discontinues on January 1, 2027, and neither has a setting for the address. In Python, replace GoogleSearchAPIWrapper's client with one that points at Searlo; the wrapper, GoogleSearchRun and GoogleSearchResults then work unchanged. In JavaScript, swap GoogleCustomSearch for a ten-line tool with the same name and output.

Where LangChain keeps the URL and key

Python: GoogleSearchAPIWrapper, in langchain-google-community, reads google_api_key and google_cse_id (or GOOGLE_API_KEY and GOOGLE_CSE_ID) and builds a google-api-python-client service with build("customsearch", "v1", developerKey=…). The host comes from that client, and the wrapper has no option for it. It builds the client while it validates its fields, so a search_engine passed to the constructor is replaced; set it after the wrapper exists.

JavaScript: GoogleCustomSearch, in @langchain/community, reads apiKey and googleCSEId (or GOOGLE_API_KEY and GOOGLE_CSE_ID) and fetches https://www.googleapis.com/customsearch/v1 directly, returning a JSON array of title, link and snippet. The URL is written in its code.

The change in Python

Two added lines after the wrapper is created. Everything that uses the wrapper keeps working: run(), results(), GoogleSearchRun and GoogleSearchResults, and siterestrict=True, which Searlo answers too.

  search = GoogleSearchAPIWrapper(google_api_key=SEARLO_API_KEY, google_cse_id="YOUR_CX")
+ search.search_engine = build("customsearch", "v1", developerKey=SEARLO_API_KEY,
+                              client_options={"api_endpoint": "https://api.searlo.tech"})

Switch in three steps

  1. 01Put your Searlo key where the Google key wasgoogle_api_key (or GOOGLE_API_KEY) becomes your Searlo API key. google_cse_id must still be set because the wrapper requires it; your existing cx or any short text works, and Searlo ignores it.
  2. 02Python: replace the wrapper's clientAfter creating the wrapper, set search.search_engine to build("customsearch", "v1", developerKey=…, client_options={"api_endpoint": "https://api.searlo.tech"}).
  3. 03JavaScript: swap the toolReplace new GoogleCustomSearch() with the DynamicTool below. It keeps the name google-custom-search, takes a string and returns the same JSON array.

Python: GoogleSearchAPIWrapper

A complete script. Pass tool to your agent as before.

agent_search.py

import os

from googleapiclient.discovery import build
from langchain_google_community import GoogleSearchAPIWrapper, GoogleSearchRun

SEARLO_API_KEY = os.environ["SEARLO_API_KEY"]

search = GoogleSearchAPIWrapper(
    google_api_key=SEARLO_API_KEY,
    google_cse_id="YOUR_CX",  # still required by the wrapper; Searlo ignores it
    k=5,
)
# The wrapper builds a client for Google's host when it is created. Replace it
# after creating the wrapper: a search_engine passed to the constructor is overwritten.
search.search_engine = build(
    "customsearch",
    "v1",
    developerKey=SEARLO_API_KEY,
    client_options={"api_endpoint": "https://api.searlo.tech"},
)

print(search.results("searlo api", 3))  # [{'title': ..., 'link': ..., 'snippet': ...}, ...]

tool = GoogleSearchRun(api_wrapper=search)  # the agent tool, unchanged
print(tool.invoke("searlo api"))

JavaScript: replacing GoogleCustomSearch

GoogleCustomSearch cannot be pointed elsewhere, so this tool takes its place: same name, same description, same string input, same JSON output. It needs only @langchain/core.

googleSearch.mjs

import { DynamicTool } from "@langchain/core/tools";

const API_URL = "https://api.searlo.tech/customsearch/v1";

// Replaces GoogleCustomSearch from @langchain/community, whose URL is fixed in its code.
// The same name, the same string input and the same JSON output: [{ title, link, snippet }].
export const googleSearch = new DynamicTool({
  name: "google-custom-search",
  description:
    "a custom search engine. useful for when you need to answer questions about current events. input should be a search query. outputs a JSON array of results.",
  func: async (input) => {
    const params = new URLSearchParams({ key: process.env.SEARLO_API_KEY, q: input });
    const res = await fetch(`${API_URL}?${params}`);
    const data = await res.json();
    if (!res.ok) throw new Error(`Searlo returned ${res.status}: ${data.error?.message}`);
    return JSON.stringify(
      (data.items ?? []).map((item) => ({ title: item.title, link: item.link, snippet: item.snippet })),
    );
  },
});

console.log(await googleSearch.invoke("searlo api"));

Notes

Each search is 1 credit, and a failed search is not charged. An agent can search several times for one question, and each search counts.

Errors keep Google's envelope. In Python they surface as googleapiclient's HttpError, as before; the JavaScript tool throws with Searlo's message, for example when the account is out of credits.

Results come from the whole web. If your engine was limited to a few sites, pass search_params={"siteSearch": "example.com"} to results(), or put site: in the query.

Other tools build on these classes. Langflow's Google Search API component, for example, creates the same Python wrapper in its code, with fields for the key and the engine ID but none for the address.

We ran both on 2026-10-25 with langchain-google-community 5.1.1 and @langchain/core 1.3.0.

FAQ

Questions

Can I pass search_engine to GoogleSearchAPIWrapper's constructor?

No. The wrapper's validator builds its own client for Google and overwrites the field. Assign search.search_engine after the wrapper is created, as in the code above.

Does GoogleSearchResults still return title, link and snippet?

Yes. It calls the wrapper's results(), which reads items from the Custom Search JSON that Searlo returns. It adds a thumbnail when the result has one (pagemap.cse_thumbnail); Searlo does not return cse_image, so image is not set.

Is there a LangChain package for Searlo's own API?

Not on PyPI yet. Until there is, the drop-in above is the shortest path, and Searlo's native web search API works as a tool you define over its REST endpoint.

Run your agent on Searlo

Create a key, add two lines, run it.