Skip to main content
Glama

Your agent calls google_search and gets Google's actual results page as typed JSON: every link resolved to its real URL, plus the AI Overview, People also ask and related searches. A real Firefox (Camoufox) runs Google's page as it is, on your own IP or through any proxy you already have.

Why: plain HTTP gets a JavaScript wall, automated Chrome got a CAPTCHA 2 out of 2 times in our test, and Google's official Custom Search API closes on 1 January 2027. Other search MCP servers either aren't Google's index (Brave, Tavily, Exa, DuckDuckGo) or charge per query (SerpApi, Bright Data).

๐Ÿš€ Quick start

uv tool install gsearch-agent                         # or: pipx install gsearch-agent
gsearch setup                                         # downloads the browser once (~1 GB), prints your MCP config
claude mcp add gsearch --scope user -- gsearch-mcp    # or any MCP client, below

Then just ask: "Search Google for what asyncio is and give me the top three links", "What do people also ask about asyncio?", "Find asyncio pages on python.org from the past year."

Without a proxy, gsearch uses your own IP, at most 20 searches an hour. For more, add a proxy in any format:

claude mcp add gsearch --scope user -e GSEARCH_PROXY="gate.example.com:7000:user-sid-{sid}:password" -e GSEARCH_DATA_LIMIT_MB=1000 -- gsearch-mcp

gsearch setup prints this block with the full path of gsearch-mcp filled in. Use the full path if your client doesn't find the command.

{
  "mcpServers": {
    "gsearch": {
      "command": "gsearch-mcp",
      "env": { "GSEARCH_PROXY": "optional, any format", "GSEARCH_DATA_LIMIT_MB": "1000" }
    }
  }
}

Claude Desktop: claude_desktop_config.json. Cursor: ~/.cursor/mcp.json, or .cursor/mcp.json in a project.

No install, with uv: use "command": "uvx", "args": ["gsearch-agent"], after fetching the browser once with uvx --from gsearch-agent gsearch setup.

Settings in a file: put them in a .env (see .env.example) and set GSEARCH_ENV_FILE to its absolute path instead.

NOTE

Automated access to Google Search may breach Google's Terms of Service. ReadLegal and responsible use before running live searches.

Related MCP server: gemini-search-mcp

๐Ÿ†š How it compares

gsearch

SerpApi, Bright Data

Brave, Tavily, Exa

DuckDuckGo, SearXNG MCPs

Playwright + Chromium Google MCPs

Google's own results

โœ…

โœ…

โŒ their own index

โŒ / depends on the instance

โœ…

API key or account

none

required

required

none

none

Cost

free (+ your proxy, optional)

per query

usage-based plans

free

free

Runs on your machine

โœ…

โŒ hosted

โŒ hosted

โœ…

โœ…

AI Overview, People also ask

โœ…

โœ… (SerpApi)

โŒ

โŒ

varies

Google's redirect links resolved to real URLs

โœ…

โœ…

n/a

n/a

varies

Blocks reported as errors with a retry time

โœ…

n/a

n/a

varies

varies

Terms-of-Service risk is yours

yes (Legal)

the provider's

no

low

yes

"Varies": we did not test those projects. Our only Chromium measurement is F-0001: Playwright Chrome was sent to a CAPTCHA 2 of 2 times on an IP where gsearch was served. Read Legal before running live searches.

โœจ Why gsearch

Google's official search API is closed to new customers and shuts down on 1 January 2027 (F-0005). gsearch is built for AI agents instead:

  • ๐ŸฆŠ A real browser, not a scraper. Camoufox, a hardened Firefox, runs Google's page as it is, with a real-device fingerprint and a locale that matches its IP.

  • ๐Ÿ”— Real URLs. Google hides result links behind /goto redirects. gsearch resolves every one to the page it points to, without loading that page.

  • ๐Ÿค– Built for agents. One MCP server, two tools, typed JSON. Blocks come back as clear errors with retry_after_s, never as empty results, and every string is flagged as untrusted web content.

  • ๐ŸŒ Any proxy, any format. Rotating residential, static VPS or datacenter lists, HTTP, HTTPS or SOCKS5, in any common layout. Every byte is counted, with a hard data limit.

  • ๐ŸŽฏ Nothing to tune. Two settings. Everything else is fixed to what worked best in testing.

  • ๐Ÿ“Š Honest numbers. Every claim in this README links to a write-up of how it was measured.

๐Ÿ“ฆ What your agent gets

Real output from a saved results page (offline demo mode, where url stays null; a live search fills it):

A block is never an empty list. It comes back as an error that says what happened and how long to wait:

Field

What it holds

organic[].url

The real destination of each result, resolved from Google's /goto redirect in the same session.

organic[].title, snippet, date, domain, display_url

What the result shows, as text. link keeps the exact link Google served.

organic[].sitelinks, source_meta

A result's sub-links, and what Google shows instead of a URL on forum and video results (2 years ago).

ai_overview

The AI Overview text and its sources, when Google shows one.

people_also_ask, related_searches

The questions and related queries on the page.

ads

Text ads, when the market serves them.

pagination

Current page and next page. Ask for more results with page.

meta.untrusted_content

Always true: every string is third-party web text (see prompt injection).

Error codes: captcha, soft_block, hard_block, consent_wall, js_gate, rate_limited, parse_drift, network, proxy, backend_unavailable, invalid_request. The full JSON Schema is in serp.schema.json.

๐Ÿ” How it works

  • Like a person. A visible window, because headless sessions were challenged far more often on proxy IPs (F-0015). The query is typed into the address bar. WebRTC is off, so your own IP can't leak around the proxy.

  • Never forged. gsearch never generates or alters Google's tokens: the browser runs the page as it is.

  • Cheap links. Links are resolved on the same IP with the same cookies and a TLS fingerprint matching the browser's (F-0011, F-0013).

  • Runs on servers. On a Linux machine without a screen, the browser uses a virtual display automatically (sudo apt install xvfb).

๐ŸŒ Proxies

# rotating residential: {sid}, or your provider's sid-โ€ฆ, session-โ€ฆ, sessid-โ€ฆ part, gives a new IP per search
GSEARCH_PROXY=gate.example.com:7000:user-country-us-sid-{sid}:password

# static proxies (VPS, datacenter, ISP): one or many, separated by commas, spaces or new lines
GSEARCH_PROXY=socks5://user:password@203.0.113.10:1080, http://203.0.113.11:3128

# or a file with one proxy per line
GSEARCH_PROXY=/path/to/proxies.txt
  • Rotating: a new IP and a new browser for every search, up to 3 at once. A blocked IP is replaced, up to 4 IPs per search. country-xx or cc-xx in the login is checked, and exits elsewhere are dropped.

  • Static: each IP keeps one warm session, and the IPs take turns. Each IP has its own budget (20 searches an hour, 150 a day) and rests until midnight after a CAPTCHA, so ten IPs allow up to about 200 searches an hour.

  • Data: every byte goes through a local relay that counts it, and GSEARCH_DATA_LIMIT_MB stops everything at your limit, across restarts.

TIP

Check your proxy provider's acceptable-use policy. Many forbid using their network on sites whose terms prohibit automated access, and forbid solving CAPTCHAs.

๐Ÿ“Š Performance

Proxy quality decides speed and cost. Many residential IPs are already flagged by Google. Each flagged IP costs a browser launch (about 0.7โ€“1 MB and 20 s) before the next one is tried.

Expected with a high-quality rotating proxy

Measured on a low-quality pool (F-0015)

Fresh IPs served by Google

~85% or more (assumed)

1 in 3 to 1 in 5

Time per search

~15โ€“20 s (14 s measured on a clean IP)

40โ€“75 s

Proxy data per search

~0.6โ€“1 MB (0.6 MB measured on a clean IP)

4.7โ€“6.3 MB

Searches per hour (3 browsers)

~500โ€“600

~72

1,000 searches

~0.6โ€“1 GB, under 2 hours

~5โ€“6.5 GB, ~14 hours

Without a proxy, a warm session on your own IP takes ~1.2 s per search, with every link resolved and 0 CAPTCHAs in testing (F-0014), capped at 20 searches an hour to protect your IP. Static proxies have not been benchmarked yet.

Measure your own proxy within your data limit:

python tools/benchmark.py -n 10

๐Ÿ›  Reference

Setting

What it does

GSEARCH_PROXY

Your proxies, in any format above, or a file listing them. Unset: your own IP.

GSEARCH_DATA_LIMIT_MB

Stop all proxy traffic at this many MB, counted across restarts. Unset: no limit.

GSEARCH_ENV_FILE

Absolute path of your .env. Needed in MCP client configs. If set, the file must exist.

GSEARCH_FIXTURES_DIR

Answer from saved pages instead of Google (demos and tests).

GSEARCH_STATE_DIR

Where budgets, the data meter and saved block pages live. Default: %LOCALAPPDATA%\gsearch or ~/.local/share/gsearch.

Everything else is fixed to what worked best in testing: a new IP per search with rotating proxies, a visible browser, address-bar search, real-device fingerprints, 4 attempts per search, always resolving URLs, and a 10-minute cache for repeated queries.

MCP tool

Arguments

Returns

google_search

query, and optionally page (1โ€“50), lang (en, fr, โ€ฆ), country (us, โ€ฆ), time_range (h, d, w, m, y), site (a domain)

The JSON above plus a short text summary, or an error result on a block.

google_search_status

none

Proxy data used against the limit, IPs used and blocked, and the cache. Never contacts Google.

gsearch search "query" [--page N] [--lang xx] [--country xx] [--time-range h|d|w|m|y] [--site domain]
gsearch search "query" --fixtures fixtures/gecko     answer from saved pages (offline, from a clone of this repo)
gsearch parse page.html                              parse any saved Google results page
gsearch schema                                       print the JSON Schema
gsearch identity check                               check the browser's fingerprint matches its IP (no Google traffic)
gsearch setup                                        download the browser once and print the MCP config

Exit codes: 0 results, 2 usage or settings error, 3 blocked or other error, 4 Google changed its layout.

import asyncio
from gsearch_core.client import SearchClient
from gsearch_core.config import fetcher_from_env
from gsearch_parse.models import SearchRequest

async def main() -> None:
    fetcher, _live = fetcher_from_env()      # reads the settings and .env
    result = await SearchClient(fetcher).search(SearchRequest(query="what is asyncio in python"))
    print(result.model_dump_json(indent=2))  # SearchResponse or SearchError
    await fetcher.aclose()

asyncio.run(main())

To call it over MCP from your own code, the way an agent does, see examples/mcp_client.py.

You see

What it means

What to do

captcha on most searches

Your proxy's IPs are flagged by Google.

Try another pool or country. Measure with tools/benchmark.py.

captcha with hours to wait, no proxy

Your own IP was challenged and rests until midnight.

Wait, or add a proxy.

rate_limited

Every IP is at its hourly or daily budget (own IP or static proxies).

Wait retry_after_s, or add proxies.

proxy: no usable exit in 4 draws

Four IPs in a row were dead or in the wrong country.

Check the country part of the login, and the provider.

proxy: proxy data limit reached

GSEARCH_DATA_LIMIT_MB is used up.

Raise it, or delete proxy_bytes.json in the state folder to count from zero.

parse_drift

Google changed its page layout.

Update gsearch, or see CONTRIBUTING.md.

GSEARCH_ENV_FILE=โ€ฆ does not exist

The settings path is wrong.

Use an absolute path. gsearch refuses to start rather than fall back to your own IP.

the Camoufox browser is not installed

The browser has not been downloaded yet.

Run gsearch setup once.

Browser windows open

Expected with a proxy: visible sessions are challenged far less.

On a server without a screen, install Xvfb and they run on a virtual display.

When the browser hits a block or an error, the page is saved with a screenshot in the state folder's evidence/, and the error's evidence_ref points to it.

โ“ FAQ

Do I need a proxy? No. Without one, gsearch uses your own IP with a warm session (~1.2 s per search), capped at 20 searches an hour and 150 a day to keep your IP clean. For volume, use a proxy.

Which proxy should I buy? A rotating residential proxy with sticky sessions (a session id in the username). Quality matters more than price: run tools/benchmark.py on a small plan before buying more.

How much proxy data does a search use? About 0.6 MB on a clean IP. Flagged IPs cost more, because each one is a wasted browser launch. Set GSEARCH_DATA_LIMIT_MB so you can never overspend.

Does it solve CAPTCHAs? No. A blocked IP is dropped and a fresh one is tried. gsearch ships no CAPTCHA solver and never forges Google's tokens.

Can it run on a server? Yes. On Linux without a screen, install Xvfb and the browser runs on a virtual display.

Which agents work with it? Any MCP client: Claude Code, Claude Desktop, Cursor and others. You can also use the CLI or call it from Python.

  • Google's Terms of Service restrict automated access to Google Search. Running this tool against Google may breach those terms, whatever this project's license says.

  • Litigation risk is real and current. In Google v. SerpApi (N.D. Cal., filed 2025-12-19), Google argues that solving its SearchGuard JavaScript challenge by automated means is circumvention under DMCA ยง1201. gsearch runs that challenge in a real browser and does not forge or alter tokens, but the complaint also targets automated browsers. See docs/DESIGN.md (D-004).

  • No CAPTCHA solver is shipped, wired in or recommended. gsearch_core.captcha defines an interface only; anything you plug into it, and its legality, is your responsibility.

  • Get legal advice for your jurisdiction and use case before running live searches. The parser (gsearch_parse) only reads HTML you already have.

  • Limits are on by default and cannot be bypassed from the CLI or MCP. Don't use proxies to hammer Google.

  • No personal-data harvesting. Don't profile people, scrape people-search results or collect personal data at scale. Keep caches short-lived and don't republish raw results.

  • On a block, back off. CAPTCHAs and rate limits come back as errors with retry_after_s. gsearch never tries to defeat the challenge.

Prompt injection

Every title, snippet, URL and question in a result is untrusted third-party text from the open web, and may contain instructions aimed at your agent. The tool descriptions say so, every response carries meta.untrusted_content: true and an MCP untrusted_content flag, and the text summary ends with a warning. Treat results strictly as data.

๐Ÿค Contributing

Tests never contact Google: the parser is tested against saved pages, and the browser against a local fake Google. Pull requests are welcome: pick a good first issue, see the roadmap, and start with CONTRIBUTING.md and docs/DESIGN.md.

License

MIT. Camoufox (MPL-2.0) is installed from PyPI as a dependency and is never vendored.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    F
    maintenance
    MCP server for web search powered by Google AI Mode (Gemini). Enables any AI agent to search the web in real-time for free and without rate limits.
    2
    184
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for Google Search using a logged-in browser profile, delivering personalized, ad-free results with advanced operators and pagination. It also fetches web pages as markdown through the same browser session.
    5
    MIT