{"slug": "turning-google-search-into-an-api-with-35-lines-of-json", "title": "Turning Google Search into an API with 35 Lines of JSON", "summary": "A developer demonstrated how to turn Google Search into a structured JSON API using Relinos, a programmable browser, with roughly 35 lines of JSON configuration. The setup uses linear browser actions to type a query and submit it, XPath-based extraction to pull titles, URLs, snippets, AI answers and related searches, plus built-in pagination and a one-day cache. The author notes the linear action model is an intentional limitation that keeps routines predictable and debuggable.", "body_md": "Google Search results contain titles, URLs, snippets, AI-generated answers, and related searches.\n\nHere's how to extract them as structured JSON using Relinos, a programmable browser.\n\n```\n{\n    \"url\": \"https://www.google.com\",\n    \"render\": true,\n    \"actions\": [\n        [\"actionWaitForAndSelect\", \"//textarea[@name='q']\", \"1\"],\n        [\"actionType\",\"your search term\",\"\"],\n        [\"actionSleep\", \"1\", \"\"],\n        [\"actionKeyboardEnter\", \"\", \"\"],\n        [\"actionSleep\", \"2\", \"\"]\n    ],\n    \"pagination\": [\"//*[@id='pnnext']\", \"2\"],\n    \"cache\": \"1d\",\n    \"format\": \"json\",\n    \"extraction\": {\n    \"query\": { \"$xpath\": \"//*[@id='ti6dpd']\" },\n    \"result_count\": {\n      \"$xpath\": \"//div[@class='XhWQv qd6zO ApHyTb']\",\n      \"$extract\": \"\\\\D*(\\\\d[,\\\\d]*) .*\"\n    },\n    \"ai\": {\n      \"$xpath\": \"//*[@class='mZJni Dn7Fzd']\", \"$format\": \"md\"\n    },\n    \"results\": {\n      \"$xpath\": \"//div[contains(@class, 'MjjYud')]\",\n      \"$list\": true,\n      \"title\": { \"$xpath\": \".//h3[contains(@class, 'LC20lb MBeuO DKV0Md')]\" },\n      \"url\": { \"$xpath\": \".//a[contains(@class, 'zReHs')]/@href\" },\n      \"site\": { \"$xpath\": \".//span[contains(@class, 'VuuXrf')]\" },\n      \"snippet\": { \"$xpath\": \".//div[contains(@data-snf, 'nke7rc')]\" }\n    },\n    \"related_searches\": {\n      \"$xpath\": \"//*[@class='qR29te']\", \"$list\": true\n    }\n  }\n}\n```\n\nThose lines is all it takes, Here a shortened example output for the search term \"Elon Musk\"\n\n```\n{\n    \"query\": \"Elon Musk\",\n    \"result_count\": \"157,000,000\",\n    \"ai\": null,\n    \"results\": [\n        {\n            \"title\": \"Elon Musk (@elonmusk) / X\",\n            \"url\": \"https://x.com/elonmusk\",\n            \"site\": \"X · elonmusk\",\n            \"snippet\": \"The more I read the news, the more I think that becoming multiplanetary \n            is a necessity. I hope we will have enough time to prepare for life on the Moon and, \n            ...Read more\"\n        },\n        {\n            \"title\": \"Elon Musk\",\n            \"url\": \"https://en.wikipedia.org/wiki/Elon_Musk\",\n            \"site\": \"Wikipedia\",\n            \"snippet\": \"40 minutes ago — Elon Reeve Musk is an industrialist, technologist, and \n            former public official who is the chief executive officer (CEO) and largest shareholder \n            of Tesla and ...Read more\"\n        },\n    ],\n    \"related_searches\": [\n        \"Elon Musk book\", \"Tesla\", \"The Boring Company\",\n    ]\n}\n```\n\nMany modern websites generate content dynamically using JavaScript, which means a simple HTTP request will not return all the data we're looking for. Relinos supports both simple HTTP requests and browser rendering through Chromium via the `render` key.\n\nBut when we render sites we often want to take `actions` that a normal user would make like clicking, typing or scrolling a site. In Relinos these interactions are simply a sequential list. actionWaitForAndSelect for example waits and selects the specified element, actionClick later clicks the prevously selected element. \n\nThis is an intentional limitation. By keeping actions linear and leaving complex logic to the calling application, routines remain predictable, easy to understand, and straightforward to debug. Combined with Relinos's debug mode, this makes identifying and fixing issues much easier.\n\n```\n\"actions\" = [\n    [\"actionWaitForAndSelect\", \"//textarea[@name='q']\", \"1\"],\n    [\"actionType\",\"your search term\",\"\"],\n    [\"actionSleep\", \"1\", \"\"],\n    [\"actionKeyboardEnter\", \"\", \"\"],\n    [\"actionSleep\", \"2\", \"\"]\n]\n```\n\nPagination often requires writing additional code to navigate between pages and combine their results. Relinos handles this through the `pagination` key containing the next-page selector and the total number of pages to visit.\n\n```\n\"pagination\": [\"//*[@id='pnnext']\", \"2\"]\n```\n\nRelinos follows the next-page selector, applies the same extraction to each page, and combines the results into a single JSON response.\n\nDuring development, we often execute the same request repeatedly. With \"cache\": \"1d\", Relinos can reuse previously collected results for one day or any duration instead of loading the website again. This makes experimentation faster and reduces unnecessary requests.\n\nWe can get data in \"html\", \"md\" or \"json\" `format`. We can build a json structure using the `extraction` key. \n\nWe could for example reason that google results consists of a list of search result each containing a title a url a description. We can use \"$xpath\" on each field to describe how we can locate the element in the full html or starting at its parent element.\n\n```\n\"results\": {\n      \"$xpath\": \"//div[contains(@class, 'MjjYud')]\",\n      \"$list\": true,\n      \"title\": { \"$xpath\": \".//h3[contains(@class, 'LC20lb MBeuO DKV0Md')]\" },\n      \"url\": { \"$xpath\": \".//a[contains(@class, 'zReHs')]/@href\" },\n      \"site\": { \"$xpath\": \".//span[contains(@class, 'VuuXrf')]\" },\n      \"snippet\": { \"$xpath\": \".//div[contains(@data-snf, 'nke7rc')]\" }\n}\n```\n\nIn the example, results uses an absolute selector, all children of result use a relative selector. \n\nSince both html and json represent trees this format is very expressive and allows to create complex but readable automations fast.\n\nWebsite structures change, and selectors eventually break. But because we've separated the data structure from its selectors, fixing an extraction usually means updating a few XPath expressions rather than rewriting the entire scraper.\n\nXpathschema also has other special keys next to \"$xpath\", there is more info on the [repository](https://github.com/realsirjoe/relinos)\n\nIn the end we get our google search results back\n\nWe could take this further by defining similar routines for DuckDuckGo, Bing, YouTube, and other websites, then parameterizing them to make them even easier to reuse:\n\n```\n{\n    \"name\": \"google_search\",\n    \"params\": {\n        \"search_term\": \"Cute cat pictures\"\n    }\n}\n```\n\nCombining their results would give us the foundations of a metasearch engine, similar to SearXNG, accessible through a single API.\n\nFurthermore, because the output is structured JSON, these routines can feed directly into LLMs or automated pipelines.\n\nThe web contains countless useful interfaces. Relinos makes them programmable.\n\nIf you want to try Relinos yourself you can download it on [https://github.com/realsirjoe/relinos](https://github.com/realsirjoe/relinos)", "url": "https://wpnews.pro/news/turning-google-search-into-an-api-with-35-lines-of-json", "canonical_source": "https://dev.to/realsirjoe/turning-google-search-into-an-api-with-35-lines-of-json-d3n", "published_at": "2026-11-10 11:43:28+00:00", "updated_at": "2026-11-10 12:22:46.972306+00:00", "lang": "en", "topics": ["ai-search", "developer-tools", "ai-tools"], "entities": ["Google", "Relinos", "Chromium"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/turning-google-search-into-an-api-with-35-lines-of-json", "markdown": "https://wpnews.pro/news/turning-google-search-into-an-api-with-35-lines-of-json.md", "text": "https://wpnews.pro/news/turning-google-search-into-an-api-with-35-lines-of-json.txt", "jsonld": "https://wpnews.pro/news/turning-google-search-into-an-api-with-35-lines-of-json.jsonld"}}