How to Integrate IndexNow API with Node.js — Step-by-Step Guide for Instant SEO Indexing
TL;DR
What is IndexNow?
IndexNow is this cool open protocol that lets websites instantly tell search engines (like Bing, Yandex, and others) when you add, update, or delete stuff. It's a pretty neat way to help search engines keep their indexes current. (In-Depth Guide to How Google Search Works)
Prerequisites
You'll need a Node.js project, and make sure you've got
axiosinstalled.You gotta have access to your website’s root directory, 'cause that's where we'll put the key.
And of course, a public-facing domain (like
https://www.example.com).
Step 1: Generate Your IndexNow API Key
So, you need an api key to prove your domain is yours.
- Go ahead and generate a key. The official docs say it should be a UTF-8 key, and while they don't specify a length, a common practice is to use a 32-character key. (Generate Random UTF8) You can use an online generator, or if you're feeling code-y, use this Node.js snippet:
const crypto = require('crypto');
const key = crypto.randomBytes(16).toString('hex');
console.log("Generated IndexNow Key:", key);
Example Key:4262631fe57245bd9bd1cef01d1c3fa4
Step 2: Host the API Key on Your Site
Now, you gotta create a .txt file. Name it using your key and stick it in the root of your website.
Filename:
4262631fe57245bd9bd1cef01d1c3fa4.txtContent inside file: Just the key itself (
4262631fe57245bd9bd1cef01d1c3fa4)URL:
https://www.example.com/4262631fe57245bd9bd1cef01d1c3fa4.txt
Quick Note: If you decide to put the key file somewhere else, that's fine, but you'll need to tell IndexNow where it is using the keyLocation parameter when you submit URLs.
Step 3: Submit URLs via Node.js Script
File Structure
Here's a simple way to organize your project:
├── config.js
├── indexnow.js <-- This is where the IndexNow magic happens
└── submit.js <-- This is for running and testing the submission
config.js
Let's set up some basic config:
module.exports = {
indexNow: {
agent: 'my-custom-agent',
source: 'my-website-source'
}
};
indexnow.js — Core Logic
This is the main part of the script:
const crypto = require('crypto');
const axios = require('axios');
const config = require('./config');
function getDomainFromUrl(url) {
try {
const parsedUrl = new URL(url);
return parsedUrl.hostname;
} catch (e) {
console.error('Invalid URL:', url);
return null;
}
}
async function submitToIndexNow(siteUrl, indexNowKey, urlList) {
try {
// Basic checks to make sure we have everything
if (!siteUrl || !indexNowKey || !urlList || !Array.isArray(urlList)) {
throw new Error('Missing required parameters');
}
<span class="hljs-comment">// If there are no URLs, we can just skip it</span>
<span class="hljs-keyword">if</span> (urlList.<span class="hljs-property">length</span> === <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span> { <span class="hljs-attr">status</span>: <span class="hljs-string">'skipped'</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'No URLs to submit'</span> };
}
<span class="hljs-comment">// Make sure the URLs are absolute</span>
urlList = urlList.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">url</span>) =></span> siteUrl.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/\/$/</span>, <span class="hljs-string">''</span>) + <span class="hljs-string">"/"</span> + url);
<span class="hljs-keyword">const</span> siteDomain = <span class="hljs-title function_">getDomainFromUrl</span>(siteUrl);
<span class="hljs-keyword">if</span> (!siteDomain) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Invalid site URL'</span>);
<span class="hljs-keyword">const</span> data = {
<span class="hljs-attr">host</span>: siteDomain,
<span class="hljs-attr">key</span>: indexNowKey,
<span class="hljs-comment">// Construct the keyLocation assuming it's in the root</span>
<span class="hljs-attr">keyLocation</span>: <span class="hljs-string">`<span class="hljs-subst">${siteUrl.replace(/\/$/, <span class="hljs-string">''</span>)}</span>/<span class="hljs-subst">${indexNowKey}</span>.txt`</span>,
<span class="hljs-attr">urlList</span>: urlList
};
<span class="hljs-keyword">const</span> headers = {
<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
<span class="hljs-comment">// Creating a user-agent, just for good measure</span>
<span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">`<span class="hljs-subst">${config.indexNow.agent}</span>/<span class="hljs-subst">${crypto.createHash(<span class="hljs-string">'md5'</span>).update(siteUrl).digest(<span class="hljs-string">'hex'</span>)}</span>`</span>,
<span class="hljs-string">'X-Source-Info'</span>: <span class="hljs-string">`https://<span class="hljs-subst">${config.indexNow.source}</span>/1.0/`</span>
};
<span class="hljs-comment">// Sending the request to the IndexNow API</span>
<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">post</span>(<span class="hljs-string">'https://api.indexnow.org/indexnow'</span>, data, { headers });
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">status</span>: <span class="hljs-string">'success'</span>,
<span class="hljs-attr">statusCode</span>: response.<span class="hljs-property">status</span>,
<span class="hljs-attr">message</span>: <span class="hljs-string">`Successfully submitted <span class="hljs-subst">${urlList.length}</span> URLs`</span>
};
} catch (error) {
// Handling potential errors during the submission
const errorMessage = error.response ?
HTTP <span class="hljs-subst">${error.response.status}</span>: <span class="hljs-subst">${error.response.statusText}</span> : error.message;
return {
status: 'error',
error: errorMessage,
message: 'Failed to submit URLs'
};
}
}
module.exports = {
submitToIndexNow,
getDomainFromUrl
};
submit.js — Run the Submission
This is the file you'll run to actually send the URLs.
const { submitToIndexNow } = require('./indexnow');
// Your API key and the URLs you want to submit
const apiKey = '4262631fe57245bd9bd1cef01d1c3fa4';
const urls = [
'blog/passwordless-authentication-guide',
'blog/resistant-cryptography-migration'
];
const siteUrl = 'https://www.example.com';
// Calling the function to submit
submitToIndexNow(siteUrl, apiKey, urls)
.then(result => {
console.log('📬 Submission Result:', result);
})
.catch(err => {
console.error('❌ Unexpected error:', err.message);
});
Step 4: Verify Submission in Bing Webmaster Tools
After you've run the script, you can check if everything went through okay.
Head over to https://www.bing.com/webmasters/indexnow
If you haven't already, add and verify your domain.
You can use the URL inspection tool there to see the submission status.
References
API Endpoint