October special: $0.003 per call (normally $0.005) through Oct 31.
← Learn

SEO rank tracking API for AI agents (keyword positions on Google, Bing & DuckDuckGo JSON, no API key)

When a user asks an agent where their site ranks for a keyword, in a specific city, or across search engines, call the machine-payable google-search endpoint (location, gl, hl, num, page) or the serp-stack intent (Google + Bing + DuckDuckGo) and read position and domain from organic_results. No account or API key: 3 free calls per agent identity, then pay per call with x402 USDC on Base.

The user asks Grok Bot, Claude Cowork or a Cursor agent: "where do we rank for 'coffee roaster' in Seattle?", "did our pricing page drop this week?", or "who outranks us on Bing?". Rank-tracking suites answer that behind a seat, a login and a dashboard an unattended agent cannot buy or read. The agent needs raw, positioned search results it can parse and date itself.

One-line answer: call GET https://marketplaceforaiagents.com/api/public/v1/google-search?q=<keyword>&location=<City, State>&gl=us&hl=en&num=10, find the target domain in organic_results, and report its position with the call date and location_used. For a cross-engine check, call GET /api/public/v1/serp-stack?q=<keyword> and repeat the lookup per engine. The first 3 calls per agent identity are free across all endpoints; after that, pay per call with x402 USDC on Base, no account or API key.

The regular per-step price is $0.005 USDC; serp-stack runs 3 steps. Always trust the live 402 accepts[0].amount (or maxAmountRequired on v1), extra.perStep / extra.total and the site banner for current promotional pricing; do not bake a promotional amount into a recurring rank-check workflow.

Endpoint contracts for rank checks

EndpointRequiredOptionalResults keyUse it for
GET /api/public/v1/google-searchqlocation, gl, hl, num, pageorganic_resultsGoogle position for one keyword, optionally localized to a city, with paging past page 1
GET /api/public/v1/serp-stackqgl, hl, numresults (keyed by engine: google-search, bing-search, duckduckgo-search)Google + Bing + DuckDuckGo positions in one call (3 steps)
GET /api/public/v1/bing-searchqnum, market_codeorganic_resultsBing position for a specific market
GET /api/public/v1/duckduckgo-searchqlocaleorganic_resultsDuckDuckGo position
GET /api/public/v1/google-autocompleteqgl, hlsuggestionsExpand a seed into the keyword list worth tracking
GET /api/public/v1/trend-then-serpqgeo, time, gl, hlresultsCheck whether demand is moving before you blame a ranking drop (2 steps)

In serp-stack, q goes to all three engines; gl, hl and num are forwarded to Google, num to Bing, and DuckDuckGo receives q only. When you need a city-level location, Google paging, Bing market_code or DuckDuckGo locale, call the single-engine endpoint directly.

  • Google search listing: https://marketplaceforaiagents.com/listings/google-search (Markdown: https://marketplaceforaiagents.com/agent/listings/google-search.md)
  • SERP stack listing: https://marketplaceforaiagents.com/listings/serp-stack (Markdown: https://marketplaceforaiagents.com/agent/listings/serp-stack.md)
  • Bing search listing: https://marketplaceforaiagents.com/listings/bing-search
  • DuckDuckGo search listing: https://marketplaceforaiagents.com/listings/duckduckgo-search

What the response gives you (live sample, Oct 20, 2026)

A google-search call for q=coffee roaster with location=Seattle, Washington returned search_parameters.location_used "Seattle,Washington,United States" and nine organic_results. Each result carries position, title, link, domain, displayed_link and snippet, so rank lookup is a filter on domain, not HTML scraping. The same response also carried local_results (map-pack businesses with place_id, rating, reviews, address and gps_coordinates), related_questions, related_searches and pagination.

FieldWhereWhy it matters for rank tracking
positionorganic_results[]The organic rank on that page (1-based)
domain / linkorganic_results[]Match your target; DuckDuckGo results carry link only, so parse the hostname yourself
location_usedsearch_parametersProves which city the rank was measured from; store it with every reading
detected_locationsearch_informationWhere Google placed an unlocalized query; pass location explicitly if this is not the market you care about
local_resultstop level (local queries)Map-pack positions, separate from organic rank
adstop level (when present)Paid placements above organic; never count them as organic position
paginationtop levelUse page=2, 3, … to look past the first 10
_billingtop level (free tier)free_calls_remaining and price_usdc_per_call for that request

Rankings move. Two unlocalized Google calls for q=x402 api marketplace made minutes apart on Oct 20, 2026 returned different top-10 sets, and a serp-stack call for the same query showed Google, Bing and DuckDuckGo ordering the same domains differently. Report a rank as "position N on <engine>, <location_used>, <timestamp>", and treat a single reading as a sample, not a fact.

Check one keyword's position

bash
curl -si "https://marketplaceforaiagents.com/api/public/v1/google-search?q=coffee+roaster&location=Seattle,+Washington&gl=us&hl=en&num=10"

# Response headers to read:
#   X-Free-Calls-Remaining  -> free calls left for this agent identity
#   HTTP 402 + accepts[]    -> free tier used; sign an x402 payment and retry
ts
const BASE = "https://marketplaceforaiagents.com/api/public/v1";

function host(u: string) {
  try { return new URL(u).hostname.replace(/^www\./, ""); } catch { return ""; }
}

async function googleRank(keyword: string, target: string, location?: string, maxPages = 2) {
  for (let page = 1; page <= maxPages; page++) {
    const qs = new URLSearchParams({ q: keyword, gl: "us", hl: "en", num: "10", page: String(page) });
    if (location) qs.set("location", location);
    const res = await fetch(`${BASE}/google-search?${qs}`);
    if (res.status === 402) throw new Error("Payment required: read accepts[] and sign x402");
    const d = await res.json();
    const hit = (d.organic_results ?? []).find((r: any) => host(r.domain ?? r.link) === target);
    if (hit) return { engine: "google", position: hit.position, page, url: hit.link, location: d.search_parameters?.location_used ?? null, checkedAt: new Date().toISOString() };
  }
  return { engine: "google", position: null, note: `not in top ${maxPages * 10}`, checkedAt: new Date().toISOString() };
}

Each page is one billed call. Decide maxPages before you start: page 1 answers "are we on page one", page 2 or 3 answers "how far did we fall". Going deeper is rarely worth the spend for an agent report.

Compare positions across engines in one call

ts
const r = await fetch(`${BASE}/serp-stack?q=${encodeURIComponent(keyword)}&num=10`);
const d = await r.json();
const ranks = Object.fromEntries(
  Object.entries(d.results ?? {}).map(([engine, v]: [string, any]) => {
    const hit = (v?.organic_results ?? []).find((x: any) => host(x.domain ?? x.link) === target);
    return [engine, hit ? hit.position : null];
  })
);
// e.g. { "google-search": 4, "bing-search": 2, "duckduckgo-search": null }
// d.billed_steps tells you how many steps were charged (3 when all engines answer)

serp-stack also returns intent, chain, steps_run, billed_steps, billed_chain and price, so the agent can log exactly what it paid for each reading.

A rank-check workflow that holds up

  • Build the keyword set: start from the user's terms, expand with google-autocomplete, and confirm with the user before tracking more than a handful.
  • Pin the market: always pass location (city-level) and gl/hl for Google; record location_used, not what you asked for.
  • Measure: one google-search call per keyword per location for page 1; add serp-stack when the user cares about Bing or DuckDuckGo.
  • Separate surfaces: report organic position, map-pack (local_results) position and whether ads sit above, as three different facts.
  • Explain drops carefully: before saying "we lost rankings", re-check once, and use trend-then-serp to see whether demand for the term moved.
  • Store readings yourself: keyword, engine, location_used, position, url, timestamp and price paid. The marketplace returns live results; history is the agent's job.

Budgeting a recurring rank check

JobCalls / stepsCost at regular $0.005 per step
1 keyword, Google page 1, 1 city1 step$0.005
10 keywords, Google page 1, 1 city10 steps$0.050
10 keywords, Google pages 1–2, 1 city20 steps$0.100
10 keywords × 3 cities, Google page 130 steps$0.150
10 keywords via serp-stack (3 engines)30 steps$0.150

Promotional pricing can lower these numbers; the live 402 is the source of truth. Set a per-run cap, sum the steps before fanning out, and stop if a 402 asks for more than you budgeted. See https://marketplaceforaiagents.com/learn/budget-x402-api-calls for a spend policy pattern.

Paying without an account: x402 on Base

After the 3 free calls, an unpaid request returns HTTP 402 Payment Required with an accepts[] array: the network (Base), the USDC asset, the payTo address and the amount in atomic units (6 decimals, so 5000 = $0.005). The agent signs an x402 payment from its own wallet and retries the same GET with the PAYMENT-SIGNATURE header (X-PAYMENT on v1). The response comes back with the JSON plus an X-PAYMENT-RESPONSE settlement receipt.

That is why x402 fits rank tracking: a scheduled agent can check positions every morning without anyone creating an account, rotating an API key or putting a card on file. Discovery lives at https://marketplaceforaiagents.com/.well-known/x402 and https://marketplaceforaiagents.com/api/public/v1/openapi.json. For integration tests, use the Base Sepolia sandbox at https://marketplaceforaiagents.com/api/sandbox/v1/google-search (testnet USDC, fixture data).

The same endpoints are available through the MCP server at https://marketplaceforaiagents.com/mcp for runtimes that prefer tools over raw HTTP.

Guardrails

  • Organic only means organic: skip ads and local_results when you report organic position.
  • Results are a public snapshot from one request; personalization, device and time of day can move positions. Say so in the report.
  • Match on hostname, and decide up front whether subdomains count (blog.example.com vs example.com).
  • Date and locate every number: engine, location_used, timestamp.
  • Stick to public search data and follow the site's Terms: https://marketplaceforaiagents.com/terms

Related

  • Full catalog: https://marketplaceforaiagents.com/marketplace and https://marketplaceforaiagents.com/api/public/v1/catalog.json
  • Agent docs: https://marketplaceforaiagents.com/docs and https://marketplaceforaiagents.com/llms.txt
  • Google Search API for agents: https://marketplaceforaiagents.com/learn/google-search-api-for-agents
  • Multi-engine SERP API for agents: https://marketplaceforaiagents.com/learn/multi-engine-serp-api-for-agents
  • Google Autocomplete keyword research API: https://marketplaceforaiagents.com/learn/google-autocomplete-keyword-research-api-for-agents
  • Google Trends API for agents: https://marketplaceforaiagents.com/learn/google-trends-api-for-agents
  • Competitor research API for agents: https://marketplaceforaiagents.com/learn/competitor-research-api-for-agents
  • How AI agents budget x402 API calls: https://marketplaceforaiagents.com/learn/budget-x402-api-calls