mcp

get_organic_traffic

MCP tool that returns the 28-day GA4 + GSC snapshot for an audit: sessions, daily trend, top landing pages, top queries, indexed pages.

What this tool does

get_organic_traffic returns a 28-day snapshot of organic traffic for an audit, pulled from the user’s linked Google Analytics 4 and Google Search Console properties.

  • Returns connected: false if the user has not linked Google yet: the rest of the fields are then empty.
  • When connected: sessions_28d (total organic sessions), sessions_trend (daily series), top_landing_pages (URL + sessions), top_queries (query + clicks + impressions), and indexed_pages from GSC.
  • Cached 24 hours server-side per audit, so repeat calls are cheap and idempotent.
  • Always tied to a specific audit_id; the data window is relative to that audit’s created_at.

Why it matters

SEO audits without traffic data tell agents what to fix, but not what to fix first. get_organic_traffic lets an agent rank failing pages by real organic value so the recommendations land on URLs that actually move revenue.

Concrete workflows:

  • A “where do I start?” agent calls get_audit for findings and get_organic_traffic for sessions, then drafts a fix-list ordered by sessions_28d per landing page.
  • A query-gap agent reads top_queries (high impressions, low clicks) and rewrites the matching page’s title and meta description to lift CTR.

How to use it

The user must connect Google Analytics 4 and Search Console inside the MetricSpot dashboard first. When unconnected, the tool still succeeds, it returns connected: false so agents can degrade gracefully and tell the user how to link Google.

Input schema

{
  "type": "object",
  "properties": {
    "audit_id": { "type": "string", "minLength": 1 }
  },
  "required": ["audit_id"]
}

Response schema sample

{
  "audit_id": "aud_01HZ8X9YP7K3T2N6Q5",
  "connected": true,
  "sessions_28d": 14328,
  "sessions_trend": [
    { "date": "2026-04-15", "sessions": 482 },
    { "date": "2026-04-16", "sessions": 511 },
    { "date": "2026-04-17", "sessions": 539 }
  ],
  "top_landing_pages": [
    { "url": "https://example.com/", "sessions": 5104 },
    { "url": "https://example.com/pricing", "sessions": 2871 },
    { "url": "https://example.com/blog/launch", "sessions": 1942 }
  ],
  "top_queries": [
    { "query": "example pricing", "clicks": 612, "impressions": 8412 },
    { "query": "example alternative", "clicks": 387, "impressions": 5101 }
  ],
  "indexed_pages": 184
}

When Google is not linked:

{
  "audit_id": "aud_01HZ8X9YP7K3T2N6Q5",
  "connected": false,
  "sessions_28d": null,
  "indexed_pages": null
}

Claude Code

claude mcp add --transport http metricspot https://mcp.metricspot.com/mcp \
  --header "Authorization: Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Prompt:

For MetricSpot audit aud_01HZ8X9YP7K3T2N6Q5, pull the organic traffic snapshot and tell me which top-10 query has the worst CTR.

Cursor

.cursor/mcp.json:

{
  "mcpServers": {
    "metricspot": {
      "url": "https://mcp.metricspot.com/mcp",
      "headers": {
        "Authorization": "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

Prompt:

Combine the audit findings with get_organic_traffic and list the failing pages that bring in the most sessions.

Python (rank pages by traffic)

import httpx, json

HEADERS = {
    "content-type": "application/json",
    "accept": "application/json, text/event-stream",
    "authorization": "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx",
}

r = httpx.post("https://mcp.metricspot.com/mcp", headers=HEADERS, json={
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": {
        "name": "get_organic_traffic",
        "arguments": {"audit_id": "aud_01HZ8X9YP7K3T2N6Q5"},
    },
}, timeout=30.0)

snap = json.loads(r.json()["result"]["content"][0]["text"])
if not snap["connected"]:
    print("Connect GA4 + GSC at app.metricspot.com/settings/integrations")
else:
    for q in snap.get("top_queries", [])[:5]:
        ctr = q["clicks"] / q["impressions"] if q["impressions"] else 0
        print(f"{q['query']}: CTR {ctr:.1%} ({q['clicks']} / {q['impressions']})")

Node / TypeScript (raw HTTP)

const res = await fetch("https://mcp.metricspot.com/mcp", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    accept: "application/json, text/event-stream",
    authorization: "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx",
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "tools/call",
    params: {
      name: "get_organic_traffic",
      arguments: { audit_id: "aud_01HZ8X9YP7K3T2N6Q5" },
    },
  }),
});
const snap = JSON.parse((await res.json()).result.content[0].text);
if (snap.connected) {
  console.log(`${snap.sessions_28d} sessions in last 28d`);
}

Common errors

CodeWhenAction
UNAUTHORIZED (401)Missing or invalid Bearer tokenIssue a key at https://app.metricspot.com/settings/api-keys
AUDIT_NOT_FOUND (404)audit_id doesn’t belong to this accountCall list_audits for valid ids
FORBIDDEN (403)Token lacks the Google scopeReissue the key with default scopes after linking Google
UPSTREAM_FAILED (5xx)GA4 or GSC API outageRetry; the response is cached 24h once successful

Frequently asked questions

Why is connected: false even though I see traffic in GA4?

The connection lives on the MetricSpot side: the user must visit app.metricspot.com/settings/integrations and link Google Analytics 4 and Search Console explicitly. MetricSpot reads GA4 via the Analytics Data API and GSC via the Search Console API; both require OAuth grants the user authorizes once.

Why a 28-day window?

It matches how GSC reports its top queries by default and is short enough to reflect recent changes (post-deploy, post-launch) while long enough to smooth weekly seasonality. The window is not configurable in v1.

How fresh is the data?

GA4 data is typically 24-48 hours behind real time, per Google’s processing window. GSC data lags 2-3 days. The MCP response is cached 24 hours per audit_id server-side, so repeat calls in the same day return the same snapshot.

Sources

Last updated 2026-05-13