Welcome! Type "help" for available commands.
$
Loading terminal interface...
Back to Blog

Automated Sitemap Submissions: Bing & Google Guide

June 3, 2025
William Callahan

Software engineer and founder with a background in finance and tech. Currently building aVenture.vc, a platform for researching private companies. Based in San Francisco.

Automated Sitemap Submissions: Bing & Google Guide

Sitemaps and IndexNow Are Different Workflows

A sitemap lists URLs you want search engines to discover. IndexNow notifies participating search engines about individual URLs that were added, updated, or deleted. Sending /sitemap.xml to IndexNow does not submit the URLs inside that sitemap.

For an ordinary website or blog:

  • Google: submit the sitemap through Search Console or its Sitemaps API.
  • Bing: register the sitemap in Bing Webmaster Tools or advertise it in robots.txt.
  • IndexNow: send changed content URLs to a participating endpoint. Google is not an IndexNow participant.

These are discovery signals, not guarantees of crawling, indexing, rankings, or a particular turnaround time.

Correction, October 25, 2026: An earlier version conflated IndexNow with sitemap submission and suggested Google's Indexing API for ordinary pages. It also showed placeholder runner functions and command-line flags without implementations. The examples below replace those instructions with separate API building blocks.

1. Submit a Sitemap to Google Search Console

Authentication and Property Access

The Search Console API uses OAuth 2.0. A service account is one option for unattended automation; it is not the only authentication method.

  1. Enable the Google Search Console API in your Google Cloud project. The separate Indexing API is not needed for sitemap submission.
  2. For a service-account setup, create the account and give its email Full access to the intended property under Search Console → Settings → Users and permissions. The permissions matrix allows sitemap submission for Full users; Owner access is unnecessary for this task. A Google Cloud project role does not grant Search Console property access.
  3. Supply credentials only to your server-side job. The example uses Application Default Credentials: for a downloaded service-account JSON key, set GOOGLE_APPLICATION_CREDENTIALS to its private filesystem path. Do not put that file in public/, commit it, expose it through client-side environment variables, or log it. Use only a trusted credential configuration you control.

Use the exact Search Console property identifier, separately from the site's origin:

text
URL-prefix property: https://example.com/
Domain property:     sc-domain:example.com
Sitemap URL:         https://example.com/sitemap.xml

Protocol, hostname, path, and trailing slash matter for URL-prefix properties. A Domain property identifier is not a URL: do not pass it to new URL() or append /sitemap.xml to it. The authenticated account must have access to that property, and the sitemap must be valid for it.

TypeScript API Example

Install Google's authentication library and a TypeScript runner:

bash
npm install google-auth-library
npm install --save-dev tsx typescript @types/node

This function calls the documented Sitemaps: submit endpoint. It uses the write-capable webmasters scope; neither the read-only scope nor the Indexing API's scope is a substitute.

typescript
import { GoogleAuth } from "google-auth-library";

export async function submitSitemapToGoogle(
  propertyId: string,
  sitemapUrl: string,
): Promise<void> {
  const auth = new GoogleAuth({
    scopes: ["https://www.googleapis.com/auth/webmasters"],
  });

  await auth.request({
    method: "PUT",
    url:
      "https://www.googleapis.com/webmasters/v3/sites/" +
      encodeURIComponent(propertyId) +
      "/sitemaps/" +
      encodeURIComponent(sitemapUrl),
    timeout: 30_000,
  });

  console.log("Google accepted the sitemap submission; processing is separate.");
}

A successful submission has an empty response body. It does not mean Google has fetched or processed the sitemap, let alone indexed its entries. Check the Search Console Sitemaps report for processing errors, and follow Google's sitemap guidance.

What This Does Not Do

  • Google's Indexing API is restricted to pages with JobPosting, or BroadcastEvent embedded in VideoObject. It is not a general-purpose submission API for blog posts, product pages, or other ordinary URLs.
  • The URL Inspection API reports information about Google's indexed version. It does not request indexing or provide a live crawl. For a few URLs, use the manual Request indexing workflow in Search Console; there is no equivalent general-purpose API for ordinary pages.
  • Google's old unauthenticated sitemap ping endpoint is deprecated and returns 404. Use Search Console or a Sitemap: declaration in robots.txt instead.

2. Register the Sitemap With Bing

For Bing, submit the sitemap in the Sitemaps section of Bing Webmaster Tools. Bing's sitemap guidance also explains discovery through your site's robots.txt:

text
Sitemap: https://example.com/sitemap.xml

Bing also documents a separate authenticated Bing Webmaster API SubmitFeed method. That is a sitemap-submission API using Webmaster API credentials, not an IndexNow key. Bing's old anonymous sitemap-ping method was retired in 2022.

Keep the sitemap reachable by search engines, use absolute canonical URLs, and maintain accurate lastmod values for meaningful content changes. Do not reset every entry's modification time on every build.

3. Notify IndexNow About Changed Content URLs

Follow the IndexNow protocol to notify participating engines about pages that changed. A deletion notification uses the deleted page's URL too; the endpoint has no separate “delete” flag. The page should return the appropriate HTTP status when an engine checks it.

Host the Verification Key

Choose a key of 8–128 characters, using ASCII letters, digits, and hyphens. Host a UTF-8 text file named <key>.txt at the root of the same host, containing the key. For example, public/<key>.txt in a framework that serves public/ at the origin root.

The file must be publicly reachable without authentication. The IndexNow key is intentionally public proof of control of that host, not a Google service-account secret. An alternate file location can be supplied with keyLocation; its directory limits which URL paths it can authorize. A root key file avoids that narrower path scope. Subdomains are separate hosts.

TypeScript Batch Example

This helper accepts 1–10,000 changed URLs per request on one HTTPS origin. The same-origin restriction is a deliberate simplification for this example. It sends a JSON POST rather than putting a sitemap URL in a GET request.

typescript
export async function notifyIndexNow(
  siteOrigin: string,
  key: string,
  changedUrls: string[],
): Promise<void> {
  const site = new URL(siteOrigin);
  if (site.protocol !== "https:" || site.port || site.username || site.password) {
    throw new Error("Use the public HTTPS origin without credentials or a custom port.");
  }
  if (!/^[a-zA-Z0-9-]{8,128}$/.test(key)) {
    throw new Error("IndexNow keys must contain 8–128 letters, digits, or hyphens.");
  }
  const urls = [...new Set(changedUrls)];
  if (urls.length === 0 || urls.length > 10_000) {
    throw new Error("Send between 1 and 10,000 distinct changed URLs per batch.");
  }
  for (const value of urls) {
    const url = new URL(value);
    if (url.origin !== site.origin || url.username || url.password || url.hash) {
      throw new Error("Each URL must be on the configured origin, without credentials or a fragment.");
    }
  }

  const response = await fetch("https://api.indexnow.org/indexnow", {
    method: "POST",
    headers: { "Content-Type": "application/json; charset=utf-8" },
    body: JSON.stringify({
      host: site.hostname,
      key,
      keyLocation: `${site.origin}/${key}.txt`,
      urlList: urls,
    }),
    signal: AbortSignal.timeout(30_000),
  });

  if (response.status === 202) {
    console.log("IndexNow received the URLs; key validation is pending.");
  } else if (response.status === 200) {
    console.log("IndexNow accepted the URL notification; indexing is not guaranteed.");
  } else {
    throw new Error(`IndexNow submission failed with HTTP ${response.status}.`);
  }
}

The protocol also supports single-URL GET submissions using url and key, with optional keyLocation. For batch POST requests, host, key, and urlList belong in the JSON body, with optional keyLocation as shown. Use one participating endpoint; participating engines share notifications, so sending the same batch to each engine is unnecessary. See the IndexNow FAQ for participation and ownership details.

IndexNow documents 400 for an invalid request, 403 for an invalid or unavailable key, 422 for URL/host or key-scope problems, and 429 for too many requests. Fix the request or key configuration for client errors; use bounded backoff for rate limits or transient server/network failures. Do not treat 202 as completed validation or either success status as proof of indexing.

4. Integrate These Helpers Into a Deployment Job

These are API building blocks, not a complete CLI. Save them in your own server-side submission module and write a runner that loads and validates your deployment configuration, then calls the desired helper. No --sitemaps-only or --individual-only flags are implemented here.

  • Keep the intended production origin, Search Console property identifier, and sitemap URL as separate configuration values. Do not derive the Google property identifier from the origin unless you know that exact URL-prefix property exists.
  • Require an explicit submission opt-in in the deployment job and compare the configured origin with your known production origin. Skip local, preview, and staging jobs independently of NODE_ENV; production-mode builds can run in all three.
  • Run after the public deployment is available, not merely after compilation. Publish the sitemap and IndexNow key file before sending notifications.
  • Register a stable sitemap URL once and keep its contents current. Google can revisit a known sitemap; resubmitting it daily or after every build is not required. Submit again when the sitemap's location changes or when troubleshooting a processing issue warrants it.
  • Drive IndexNow from a publish/update/delete event or a durable change list. Send changed URLs, not every unchanged sitemap entry on a daily loop. Split larger change sets into batches and retain failed batches for bounded retries.
  • Let failures propagate to the job. A top-level handler should set process.exitCode = 1; catching errors and returning false without checking it makes failed submissions look successful to CI.
  • Never print credentials or complete authenticated request/error objects. Record the engine, HTTP status, counts, and a sanitized diagnostic instead.

Run a TypeScript entrypoint with npx tsx scripts/submit-search-updates.ts (using your actual filename), or compile it before running JavaScript. Recent Node.js releases can strip some TypeScript directly, but do not assume node file.ts supports every TypeScript feature or tsconfig setting. See Node.js TypeScript support.

Using a credential file through Application Default Credentials avoids hand-copying PEM strings into .env. If your platform instead supplies a private key as text, its parser may produce actual newlines or literal \n sequences. Normalize only literal \n sequences when needed; do not assume all environment loaders behave identically. Base64 encoding is a transport representation, not encryption.

The useful distinction is simple: register sitemap files with Google and Bing; notify IndexNow about changed content URLs. Monitor processing and crawl results separately from submission acknowledgments.