# Inspect URLs — Google Search Console

> Returns Google's index status, canonical selection and crawl state for 1-10 URLs of one Search Console property in a single call. This is the API behind the URL Inspection tool in the Search Console UI.

- Key: `google_search_console-inspect-urls`
- Type: Action (Read-only)
- Version: 0.0.1
- App: Google Search Console (`google_search_console`) — https://pipedream.com/apps/google-search-console.md
- This page (HTML): https://pipedream.com/apps/google-search-console/actions/inspect-urls
- Hints: read-only · open-world
- Source: https://github.com/PipedreamHQ/pipedream/blob/master/components/google_search_console/actions/inspect-urls/inspect-urls.mjs

## Description

Returns Google's index status, canonical selection and crawl state for 1-10 URLs of one Search Console property in a single call. This is the API behind the URL Inspection tool in the Search Console UI.

**Use for** "is this page indexed?", "when did Google last crawl it?", "does Google's canonical match the one I declared?", "why is this URL missing from search?", and batch health checks after a deploy or migration — pass every URL you care about in ONE call, not one call per URL.

**Not for backlinks.** Search Console's Links report has no API at all. `referringUrls` here is only a small sample of pages Google happened to discover the URL from — not a backlink profile — so when asked for backlinks, call no Search Console tool and say plainly that the links report is not available through the API. The result also carries no Core Web Vitals or page-experience data. And if the user asks to "request indexing" or force a recrawl of an ordinary page, do not run this tool (or any other) on your own initiative: explain that no API does that, OFFER this index-status check or a sitemap resubmission via **Submit Sitemap**, and wait for them to choose.

**Returns** `{ results: [...], summary: { total, indexed, not_indexed, errors } }`, one row per URL in the same order as `inspectionUrls`. Reading the rows:
- `verdict` is `PASS` (indexed), `NEUTRAL` (known but not indexed, or unknown to Google), `FAIL` or `PARTIAL`. A URL Google has never seen returns `NEUTRAL` with a `coverageState` like `"URL is unknown to Google"` — that is a valid answer, not an error.
- `canonical_mismatch` is `true` when `googleCanonical` and `userCanonical` are both present and differ, `false` when they match, `null` when either is missing.
- `referringUrls` is truncated to the first 5; `referring_url_count` is the full count.
- `error` is `null` on success and a message when that single URL failed — one bad URL never aborts the batch. In `summary`, `indexed` counts `PASS` rows, `errors` counts rows with an `error`, and `not_indexed` is everything else.
- The raw API result is attached per row as `full_result` ONLY when `includeFullResult` is true. Leave it off unless the user asks for the complete raw result: it is large and mostly rich-results and AMP detail.

**Mistakes.** A path such as `/about` is not accepted — send full absolute URLs that live under `siteUrl`. Each inspection takes Google roughly 5-10 seconds, so a 10-URL batch runs about 20 seconds; that is normal, not a hang. Quota is 2,000 inspections per day AND 600 per minute per property, and a quota error does not say which was hit. Requires `siteOwner` or `siteFullUser` — a `siteRestrictedUser` gets 403, and so does a mismatched `siteUrl` (URLs outside the property, a missing trailing slash, `sc-domain:` versus URL-prefix confusion), so copy the identifier verbatim from **List Sites**.

**Example.** `siteUrl="sc-domain:example.com"`, `inspectionUrls=["https://www.example.com/"]` -> `results[0]` has `verdict: "PASS"`, `coverageState: "Submitted and indexed"`, `googleCanonical: "https://www.example.com/"`, `userCanonical: "https://example.com/"` and `canonical_mismatch: true` — Google indexed the www URL even though the page declares the non-www one.

[See the documentation](https://developers.google.com/webmaster-tools/v1/urlInspection.index/inspect)

## Props

| Prop | Type | Required | Description |
|---|---|---|---|
| `siteUrl` | `string` | Yes | Exact property identifier as returned by List Sites, copied verbatim. Every URL in inspectionUrls must belong to it, or the call returns 403 "User does not have sufficient permission for site". |
| `inspectionUrls` | `string[]` | Yes | 1-10 full absolute URLs to inspect, e.g. ["https://www.example.com/", "https://www.example.com/pricing"]. Each must live under the property given in siteUrl; paths alone (/pricing) are rejected. Batch the URLs into this one call rather than calling the action once per URL. On a quota failure wait a minute before retrying, and only then assume the 2,000-per-day cap rather than the 600-per-minute one. |
| `languageCode` | `string` | No | BCP-47 language code (e.g. en-US, fr, pt-BR) for the human-readable strings in the result, such as coverageState. Defaults to en-US; it changes no verdicts or data. |
| `includeFullResult` | `boolean` | No | When true, attach the untrimmed API inspectionResult for each URL as full_result. Defaults to false because that payload is large and mostly rich-results and AMP detail, and the curated fields already answer index-status, canonical and crawl questions. Set it true only when the user asks for the complete or raw inspection result. |

## Run it

**MCP**

```ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
import { PipedreamClient } from "@pipedream/sdk"

const pd = new PipedreamClient({
  projectId: process.env.PIPEDREAM_PROJECT_ID!,
  clientId: process.env.PIPEDREAM_CLIENT_ID!,
  clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
  projectEnvironment: "production",
})

const accessToken = await pd.rawAccessToken

const transport = new StreamableHTTPClientTransport(
  new URL("https://remote.mcp.pipedream.net/v3"),
  {
    requestInit: {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "x-pd-project-id": process.env.PIPEDREAM_PROJECT_ID!,
        "x-pd-environment": "production",
        "x-pd-external-user-id": "{external_user_id}", // any stable ID for this user in your system
        "x-pd-app-slug": "google_search_console",
      },
    },
  },
)

const mcp = new Client({ name: "my-agent", version: "1.0.0" })
await mcp.connect(transport)

const { tools } = await mcp.listTools()

// listTools() hands your model this tool's input schema, so it can
// fill the arguments itself:
const result = await mcp.callTool({
  name: "google_search_console-inspect-urls",
  arguments: {
    siteUrl: "Property (siteUrl)",
    inspectionUrls: ["URLs to Inspect"],
  },
})
```

**TypeScript**

```ts
import { PipedreamClient } from "@pipedream/sdk"

const pd = new PipedreamClient({
  projectId: process.env.PIPEDREAM_PROJECT_ID!,
  clientId: process.env.PIPEDREAM_CLIENT_ID!,
  clientSecret: process.env.PIPEDREAM_CLIENT_SECRET!,
  projectEnvironment: "production",
})

const result = await pd.actions.run({
  id: "google_search_console-inspect-urls",
  externalUserId: "{external_user_id}", // any stable ID for this user in your system
  configuredProps: {
    google_search_console: { authProvisionId: "apn_xxxxxxx" },
    siteUrl: "Property (siteUrl)",
    inspectionUrls: ["URLs to Inspect"],
  },
})

console.log(result)
```

**cURL**

```bash
curl -X POST https://api.pipedream.com/v1/connect/{project_id}/actions/run \
  -H "Content-Type: application/json" \
  -H "X-PD-Environment: production" \
  -H "Authorization: Bearer {access_token}" \
  -d '{
    "external_user_id": "{external_user_id}",
    "id": "google_search_console-inspect-urls",
    "configured_props": {
      "google_search_console": { "authProvisionId": "apn_xxxxxxx" },
      "siteUrl": "Property (siteUrl)",
      "inspectionUrls": ["URLs to Inspect"]
    }
  }'
```

---

- App: https://pipedream.com/apps/google-search-console.md · All apps: https://pipedream.com/apps
