Cookbook: Automate SERP monitoring with the API
A complete Node.js script that runs localized proxy searches for a set of queries, waits for results, and writes them to JSON — ready to run on a schedule.
Goal: run a set of localized Google searches on a cadence (say, nightly), capture the parsed results, and write them somewhere you can diff or feed into a report — with no manual clicking.
You’ll need
Section titled “You’ll need”- An organization with the Search Engine Proxy feature and token balance.
- An API key with the Search permission.
- Node.js 18+ (for the built-in
fetch), or adapt the logic to any language.
How proxy search works
Section titled “How proxy search works”A proxy search is asynchronous: you POST the search, get back an id with status: "pending",
then poll GET …/{id} until it’s completed. Each requested page costs one token. The script below
wraps that create-then-poll loop.
The script
Section titled “The script”Save as monitor.mjs:
const BASE_URL = process.env.ISEARCHFROM_BASE_URL ?? 'https://api.isearchfrom.com/v1';const API_KEY = process.env.ISEARCHFROM_API_KEY;if (!API_KEY) throw new Error('Set ISEARCHFROM_API_KEY');
// The searches to run each cycle. Add or edit freely.const QUERIES = [ { query: 'best crm software', country: 'US', language: 'en' }, { query: 'crm für startups', country: 'DE', language: 'de' }, { query: 'melhor crm', country: 'BR', language: 'pt' }];
async function api(path, init = {}) { const res = await fetch(`${BASE_URL}${path}`, { ...init, headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, ...(init.headers ?? {}) } }); const body = await res.json().catch(() => ({})); if (!res.ok) { // body has { statusCode, code, message } — branch on `code`, log `message`. throw new Error(`API ${res.status} (code ${body.code}): ${body.message}`); } return body;}
async function runSearch({ query, country, language }) { // 1. Create the search (1 page = 1 token). const created = await api('/tools/serp/proxy-search', { method: 'POST', body: JSON.stringify({ query, country, language, engine: 'google', device: 'desktop', pages: [1] }) });
// 2. Poll until completed / failed / timeout. const deadline = Date.now() + 90_000; let search = created; while (search.status === 'pending' || search.status === 'running') { if (Date.now() > deadline) throw new Error(`Timed out: ${query}`); await new Promise((r) => setTimeout(r, 2000)); search = await api(`/tools/serp/proxy-search/${created.id}`); } return search;}
const results = [];for (const q of QUERIES) { try { const search = await runSearch(q); results.push({ ...q, status: search.status, search }); console.error(`✓ ${q.query} (${q.country}) → ${search.status}`); } catch (err) { results.push({ ...q, status: 'error', error: String(err) }); console.error(`✗ ${q.query} (${q.country}) → ${err.message}`); }}
// 3. Emit JSON on stdout so you can redirect it to a file or a pipeline.process.stdout.write(JSON.stringify({ ranAt: new Date().toISOString(), results }, null, 2));Run it and write today’s snapshot to a file:
export ISEARCHFROM_API_KEY="isk_your_key_here"node monitor.mjs > "serp-$(date +%F).json"Run it on a schedule
Section titled “Run it on a schedule”cron (Linux/macOS) — nightly at 02:00:
0 2 * * * cd /path/to/monitor && ISEARCHFROM_API_KEY=isk_... /usr/bin/node monitor.mjs > "serp-$(date +\%F).json" 2>> monitor.logGitHub Actions — a scheduled workflow (store the key as a repository secret):
name: serp-monitoron: schedule: - cron: '0 2 * * *'jobs: run: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: node monitor.mjs > snapshot.json env: ISEARCHFROM_API_KEY: ${{ secrets.ISEARCHFROM_API_KEY }} - uses: actions/upload-artifact@v4 with: { name: serp-snapshot, path: snapshot.json }Verify
Section titled “Verify”- Each completed entry has
search.status === "completed"and pages with parsed organic results and ads. - Failed queries carry an
errorwith the APIcodeandmessage— handle those (bad country, insufficient balance, rate limit) rather than blindly retrying. See Errors, pagination & rate limits.
Variations
Section titled “Variations”- Diff over time — commit each snapshot and diff the JSON to spot ranking or ad changes.
- More pages — set
pages: [1, 2, 3]to capture deeper results (costs one token per page). - Let an agent do it — the same flow is available to an MCP agent via
run_proxy_search+get_proxy_search, with no script to maintain. - Respect the rate limit — the default is 600 requests/minute; for large query sets add a small delay between searches.
Related
Section titled “Related”Search Engine ProxyThe proxy search tool in depth.
Interactive API referenceExact request/response schemas.
Search ParametersCountry, language, device, engine, SafeSearch.