GridWhy

Docs

MCP server and API

Ask your AI assistant which clients dropped this week and why, or start a scan from a script. Included on every plan, Solo too.

1. Create a key

Sign in, open Settings → API and MCP and create a key. It starts with gw_ and is shown once. A key acts for your whole workspace; revoke it there at any time.

MCP endpoint: https://gridwhy.com/mcp (Streamable HTTP). Send the key as Authorization: Bearer gw_….

2. Connect your client

Claude Code (terminal)
claude mcp add --transport http gridwhy https://gridwhy.com/mcp \
  --header "Authorization: Bearer gw_your_key"
Claude Desktop: Settings → Developer → Edit config (claude_desktop_config.json), then restart
{
  "mcpServers": {
    "gridwhy": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://gridwhy.com/mcp", "--header", "Authorization:${GRIDWHY_AUTH}"],
      "env": { "GRIDWHY_AUTH": "Bearer gw_your_key" }
    }
  }
}

Needs Node.js. The key sits in an environment variable so the space after “Bearer” survives on Windows.

Cursor: ~/.cursor/mcp.json (or .cursor/mcp.json in a project)
{
  "mcpServers": {
    "gridwhy": {
      "url": "https://gridwhy.com/mcp",
      "headers": { "Authorization": "Bearer gw_your_key" }
    }
  }
}
ChatGPT models through the OpenAI Responses API
curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model>",
    "tools": [{
      "type": "mcp",
      "server_label": "gridwhy",
      "server_url": "https://gridwhy.com/mcp",
      "headers": { "Authorization": "Bearer gw_your_key" },
      "require_approval": { "never": { "tool_names": ["get_history", "explain_change"] } }
    }],
    "input": "Which of my clients lost ground on Google Maps this week, and why?"
  }'

Connectors inside the ChatGPT app sign in with OAuth, which GridWhy does not offer yet; the API above takes the key directly.

3. Tools

  • get_history

    Locations, tracked keywords and their scans, newest first: how many grid points rank in the top 20 and top 3, average and best rank, the one-line 'what changed', and a report link. Without arguments: every keyword with its latest scan, plus the manual scans left.

    Arguments: location, keyword, keyword_id, limit (all optional)

  • explain_change

    Why the ranking changed between a scan and the one before it: findings (coverage, competitors that rose or appeared, review velocity, rating, category or name changes), each with a line to paste to the client, plus next steps. Pass scan_id, or a location and keyword for their latest finished scan.

    Arguments: scan_id, location, keyword, keyword_id (all optional)

  • run_scan

    Starts a grid scan for one tracked keyword now (in addition to the schedule). Counts against the account's manual scans (per day and per 30 days); a keyword's first scan is free. Takes 15-30 seconds; then call explain_change with the returned scan_id.

    Arguments: location, keyword, keyword_id (all optional)

Try: “Explain what changed for Wilson Plumbing on ‘plumber’ and write the update for the client.”

4. REST

The same three operations, the same key, limits and answers (JSON).

get_history
curl "https://gridwhy.com/api/v1/history?location=Wilson&keyword=plumber&limit=5" \
  -H "Authorization: Bearer gw_your_key"
explain_change
curl "https://gridwhy.com/api/v1/explain?scan_id=<scan id>" \
  -H "Authorization: Bearer gw_your_key"
run_scan (202 Accepted; the scan takes 15–30 seconds)
curl -X POST "https://gridwhy.com/api/v1/scans" \
  -H "Authorization: Bearer gw_your_key" -H "Content-Type: application/json" \
  -d '{ "location": "Wilson Plumbing", "keyword": "plumber" }'

Errors come back as { "error": "…" } with 400, 401, 403, 404, 409 or 429 (with Retry-After).

5. Limits

PlanAPI calls an hourrun_scan (manual scans)
Solo1005 a day, 30 per 30 days
Agency50020 a day, 100 per 30 days
Agency+2,00050 a day, 250 per 30 days

Scheduled scans are never limited by the API. The free trial has Solo’s limits. A key stops working when a trial ends or a subscription is canceled.