Auto-Push URLs to Bing Search Engines with IndexNow
IndexNow — Push-Notifying Search Engines Yourself
For starters, I registered my sitemap.xml with two major search platforms (Google Search Console and Bing Webmaster Tools), thinking "Alright, crawling is sorted." But that's the tragedy of running a minor site—it takes forever to actually get crawled.
That's where IndexNow comes in (and hopefully helps).
IndexNow is a protocol jointly announced by Microsoft and Yandex in 2021 that lets you actively notify search engines of new or updated pages.
- Supported search engines: Bing, Yandex, Naver (South Korea), Seznam (Czech Republic)
- Google: Not on board (Google prefers to discover things using their own crawlers)
- How it works: Simply send your URLs via an HTTP POST request
It sounded promising, and since it's already integrated right into the Bing Webmaster Tools menu, I figured it was finally time to set it up. It’s a shift from a passive approach ("Here's my sitemap, please grab it whenever you visit") to a proactive stance ("I just published a new post, come index it right now!").
Going Public Is What Proves Domain Ownership
Usually, Web APIs handle authentication using a secret API key. IndexNow, however, goes for a much simpler approach:
- Generate a key however you want (an 8 to 128-character alphanumeric string with hyphens).
- Place a text file containing that key at
https://yourdomain/<key>.txt. - Include the
keyandkeyLocationin your API requests. - The search engine sends a GET request to the
keyLocationand verifies the content matches.
At first glance, putting a key in a public folder might seem insecure, but IndexNow's authentication isn't about keeping the key itself a secret. Sure, it's better if it doesn't get leaked, but both the key and keyLocation are sent in plain text in the API request anyway, and the search engines log them on their end anyway.
What actually keeps it secure is the "proof of control" concept: the fact that you can host content at this URL proves you own the domain. It’s the same logic behind ACME challenges for HTTPS certificates or Google Search Console's HTML file verification.
Honestly, even if a bad actor gets hold of your API key, the only thing they can do is ask Bing to index your site's pages. Since it's locked down to your domain, leaks aren't really an issue. It’s a quietly clever design.
Setting It Up in 5 Minutes
-
Get an API key from Bing Webmaster Tools
As I mentioned, you can technically generate your own key as long as it fits the rules. But since Bing has "IndexNow" built right into the left menu of its dashboard, with a [Generate] button that creates the key for you, I went with that.
It gives you a UUID-like 32-character hexadecimal string. -
Place the key file at the root
Since this blog is built on Next.js and hosted on Vercel, I just needed to drop it inpublic/<key>.txt. The file content is literally just the key string.
Once pushed and deployed, as long as it's accessible athttps://diary.dazydayz.com/<key>.txt, you're good to go. -
Test the API
There are two ways to send URLs to IndexNow: \- GET: Send one URL at a time using
?url=X&key=Y&keyLocation=Z\ - POST: Send up to 10,000 URLs at once in a
urlListarray
Since I publish 2+ URLs per post on this blog (JA and EN, possibly more languages later), batching them into one POST request is more efficient than firing them off one by one — so I went with POSTurlList.
- GET: Send one URL at a time using
Let's test it using curl:
curl -X POST "https://api.indexnow.org/indexnow" \
-H "Content-Type: application/json" \
-d '{
"host": "diary.dazydayz.com",
"key": "<KEY>",
"keyLocation": "https://diary.dazydayz.com/<KEY>.txt",
"urlList": [
"https://diary.dazydayz.com/ja/posts/2026/05/tokuryu",
"https://diary.dazydayz.com/en/posts/2026/05/tokuryu"
]
}'
I tried testing it with the URLs from the Tokuryu post and got a 202 back.
202 Accepted: URL received. IndexNow key validation pending.
This status means the URL was received, but the key hasn't been validated yet. Apparently on the first API call, IndexNow hadn't fetched and verified the keyLocation content.
When I resent the same request a short while later, I got a 200 OK.
Automating It with GitHub Actions
Running curl manually every time is a pain, and automation is the whole point, so I set up a workflow to automatically ping IndexNow whenever changes are pushed to posts/.
The Script (scripts/indexnow-ping.ts)
This script filters the modified markdown files passed as arguments, grabs only those with status: published, and POSTs their corresponding public URLs to the IndexNow API:
const HOST = "diary.dazydayz.com";
const KEY = process.env.INDEXNOW_KEY!;
// posts/ja/2026/05/indexnow.md → /ja/posts/2026/05/indexnow
const m = file.match(/^posts\/(ja|en)\/(.+)\.md$/);
urls.push(`https://${HOST}/${m[1]}/posts/${m[2]}`);
await fetch("https://api.indexnow.org/indexnow", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
host: HOST,
key: KEY,
keyLocation: `https://${HOST}/${KEY}.txt`,
urlList: urls,
}),
});
I also added support for a --dry-run flag so I can double-check which URLs will be notified locally before actually sending them.
$ npx tsx scripts/indexnow-ping.ts posts/ja/2026/05/indexnow.md --dry-run
Notified 1 URL to IndexNow:
- https://diary.dazydayz.com/ja/posts/2026/05/indexnow
The Workflow (.github/workflows/indexnow.yml)
on:
push:
paths:
- 'posts/ja/**.md'
- 'posts/en/**.md'
branches:
- main
jobs:
ping:
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Detect changed posts
id: changes
run: |
FILES=$(git diff --name-only --diff-filter=AM HEAD~1 HEAD \
-- 'posts/ja/**.md' 'posts/en/**.md' | tr '\n' ' ')
echo "files=$FILES" >> $GITHUB_OUTPUT
- name: IndexNow ping
if: steps.changes.outputs.files != ''
env:
INDEXNOW_KEY: ${{ secrets.INDEXNOW_KEY }}
run: npx tsx scripts/indexnow-ping.ts ${{ steps.changes.outputs.files }}
Key points:
- We use
--diff-filter=AMto only pick up Added or Modified files (ignoring Deleted ones). - The
status: publishedfiltering is handled inside the script (so draft posts don't trigger a ping). - The API key is read from GitHub secrets (meaning no hardcoded keys in the script or workflow files).
Wrapping Up
Pushing this article itself is the first live test of the workflow.
If everything runs smoothly, I should see a recent crawl record for this URL in Bing Webmaster Tools' URL Inspection tool within a few minutes after pushing.
With this setup, pushes now trigger an automatic ping to multiple search engines within minutes.
Unfortunately, Google isn't part of this alliance, so I'll still have to wait for their regular GSC crawl cycle.