Skip to content

Commit c3d5ff6

Browse files
committed
SEO: Add IndexNow for instant search engine indexing
- Add IndexNow key file (a743d8b18b9b4efeb89378e9a803f956.txt) - Add scripts/indexnow-submit.js to submit 21 URLs to IndexNow API - Update deploy.yml to auto-submit URLs after deployment - Update robots.txt with IndexNow reference - Exclude scripts folder from production build in copy-assets.js IndexNow notifies Bing, Yandex, Seznam, Naver and other participating search engines instantly when content is deployed.
1 parent adcb78b commit c3d5ff6

File tree

5 files changed

+194
-1
lines changed

5 files changed

+194
-1
lines changed

.github/workflows/deploy.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ jobs:
2020
# Job 1: Build with Vite
2121
build:
2222
name: Build with Vite
23-
needs: []
2423
runs-on: ubuntu-latest
2524
outputs:
2625
build_status: ${{ steps.build_check.outputs.status }}
@@ -160,6 +159,17 @@ jobs:
160159
--data '{"purge_everything":true}' | jq .
161160
echo "✅ Cache purge requested"
162161
162+
- name: Checkout code for IndexNow script
163+
uses: actions/checkout@v4
164+
with:
165+
sparse-checkout: scripts
166+
167+
- name: Submit URLs to IndexNow
168+
run: |
169+
echo "🔍 Submitting URLs to IndexNow..."
170+
node scripts/indexnow-submit.js
171+
continue-on-error: true
172+
163173
- name: Report deployment success
164174
run: |
165175
echo "🚀 DEPLOYMENT SUCCESSFUL"
@@ -168,4 +178,5 @@ jobs:
168178
echo "URL: ${{ steps.deployment.outputs.page_url }}"
169179
echo "Built with: Vite (optimized)"
170180
echo "Cache: Purged via Cloudflare API"
181+
echo "IndexNow: URLs submitted to search engines"
171182
echo "================================"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a743d8b18b9b4efeb89378e9a803f956

copy-assets.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ const EXCLUDE = [
2727
'copy-assets.js',
2828
'cache-bust.js',
2929
'generate-sitemap.js',
30+
31+
// Scripts folder (build/dev tools, not needed in production)
32+
'scripts',
3033
'.gitignore',
3134
'.gitattributes',
3235
'.eslintrc.js',

robots.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# robots.txt for Unity AI Lab
22
# https://www.unityailab.com/
3+
#
4+
# IndexNow enabled for instant indexing:
5+
# https://www.unityailab.com/a743d8b18b9b4efeb89378e9a803f956.txt
36
#
47
# Unity AI Lab - Your Premier AI Innovation Partner
58
# Specializing in: Artificial Intelligence, Machine Learning, Generative AI,

scripts/indexnow-submit.js

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env node
2+
/**
3+
* IndexNow URL Submission Script
4+
*
5+
* Submits all site URLs to IndexNow API for instant indexing by:
6+
* - Bing
7+
* - Yandex
8+
* - Seznam
9+
* - Naver
10+
* - And other participating search engines
11+
*
12+
* Usage:
13+
* node scripts/indexnow-submit.js [--dry-run]
14+
*
15+
* The script reads URLs from sitemap.xml and submits them to IndexNow.
16+
* All participating search engines share submitted URLs automatically.
17+
*/
18+
19+
const https = require('https');
20+
21+
// Configuration
22+
const CONFIG = {
23+
host: 'www.unityailab.com',
24+
key: 'a743d8b18b9b4efeb89378e9a803f956',
25+
// IndexNow API endpoint (Bing's endpoint, shared with all participants)
26+
endpoint: 'api.indexnow.org',
27+
// All URLs to submit
28+
urls: [
29+
// Main pages
30+
'https://www.unityailab.com/',
31+
'https://www.unityailab.com/about/',
32+
'https://www.unityailab.com/services/',
33+
'https://www.unityailab.com/projects/',
34+
'https://www.unityailab.com/contact/',
35+
36+
// AI section
37+
'https://www.unityailab.com/ai/',
38+
'https://www.unityailab.com/ai/demo/',
39+
40+
// Apps section
41+
'https://www.unityailab.com/apps/',
42+
'https://www.unityailab.com/apps/unityDemo/unity.html',
43+
'https://www.unityailab.com/apps/textDemo/text.html',
44+
'https://www.unityailab.com/apps/personaDemo/persona.html',
45+
'https://www.unityailab.com/apps/slideshowDemo/slideshow.html',
46+
'https://www.unityailab.com/apps/screensaverDemo/screensaver.html',
47+
'https://www.unityailab.com/apps/helperInterfaceDemo/helperInterface.html',
48+
'https://www.unityailab.com/apps/talkingWithUnity/',
49+
'https://www.unityailab.com/apps/talkingWithUnity/indexAI.html',
50+
'https://www.unityailab.com/apps/oldSiteProject/',
51+
'https://www.unityailab.com/apps/oldSiteProject/screensaver.html',
52+
53+
// Downloads section
54+
'https://www.unityailab.com/downloads/',
55+
'https://www.unityailab.com/downloads/moana/'
56+
]
57+
};
58+
59+
/**
60+
* Submit URLs to IndexNow API
61+
*/
62+
async function submitToIndexNow(dryRun = false) {
63+
const payload = {
64+
host: CONFIG.host,
65+
key: CONFIG.key,
66+
urlList: CONFIG.urls
67+
};
68+
69+
console.log('IndexNow URL Submission');
70+
console.log('==========================');
71+
console.log(`Host: ${CONFIG.host}`);
72+
console.log(`Key: ${CONFIG.key}`);
73+
console.log(`URLs to submit: ${CONFIG.urls.length}`);
74+
console.log('');
75+
76+
CONFIG.urls.forEach((url, i) => {
77+
console.log(` ${i + 1}. ${url}`);
78+
});
79+
console.log('');
80+
81+
if (dryRun) {
82+
console.log('DRY RUN - No actual submission');
83+
console.log('');
84+
console.log('Would POST to: https://api.indexnow.org/indexnow');
85+
console.log('Payload:', JSON.stringify(payload, null, 2));
86+
return { success: true, dryRun: true };
87+
}
88+
89+
return new Promise((resolve, reject) => {
90+
const data = JSON.stringify(payload);
91+
92+
const options = {
93+
hostname: CONFIG.endpoint,
94+
port: 443,
95+
path: '/indexnow',
96+
method: 'POST',
97+
headers: {
98+
'Content-Type': 'application/json; charset=utf-8',
99+
'Content-Length': Buffer.byteLength(data)
100+
}
101+
};
102+
103+
console.log(`Submitting to https://${CONFIG.endpoint}/indexnow ...`);
104+
105+
const req = https.request(options, (res) => {
106+
let responseData = '';
107+
108+
res.on('data', (chunk) => {
109+
responseData += chunk;
110+
});
111+
112+
res.on('end', () => {
113+
const statusCode = res.statusCode;
114+
115+
console.log('');
116+
console.log(`Response: HTTP ${statusCode}`);
117+
118+
if (statusCode === 200) {
119+
console.log('SUCCESS - URLs submitted successfully');
120+
console.log(' All participating search engines will be notified.');
121+
resolve({ success: true, statusCode, response: responseData });
122+
} else if (statusCode === 202) {
123+
console.log('ACCEPTED - URLs received, key validation pending');
124+
console.log(' Search engines will validate the key file.');
125+
resolve({ success: true, statusCode, response: responseData });
126+
} else if (statusCode === 400) {
127+
console.log('BAD REQUEST - Invalid format');
128+
reject(new Error(`HTTP 400: Invalid format. Response: ${responseData}`));
129+
} else if (statusCode === 403) {
130+
console.log('FORBIDDEN - Key not valid');
131+
console.log(' Make sure the key file exists at:');
132+
console.log(` https://${CONFIG.host}/${CONFIG.key}.txt`);
133+
reject(new Error(`HTTP 403: Key not valid. Response: ${responseData}`));
134+
} else if (statusCode === 422) {
135+
console.log('UNPROCESSABLE - URLs don\'t belong to host or key mismatch');
136+
reject(new Error(`HTTP 422: URL/Key mismatch. Response: ${responseData}`));
137+
} else if (statusCode === 429) {
138+
console.log('TOO MANY REQUESTS - Rate limited');
139+
console.log(' Wait before submitting again.');
140+
reject(new Error(`HTTP 429: Rate limited. Response: ${responseData}`));
141+
} else {
142+
console.log(`UNEXPECTED RESPONSE: ${statusCode}`);
143+
if (responseData) console.log(` Response: ${responseData}`);
144+
resolve({ success: false, statusCode, response: responseData });
145+
}
146+
});
147+
});
148+
149+
req.on('error', (error) => {
150+
console.log('REQUEST ERROR:', error.message);
151+
reject(error);
152+
});
153+
154+
req.write(data);
155+
req.end();
156+
});
157+
}
158+
159+
// Main execution
160+
const args = process.argv.slice(2);
161+
const dryRun = args.includes('--dry-run');
162+
163+
submitToIndexNow(dryRun)
164+
.then((result) => {
165+
console.log('');
166+
console.log('==========================');
167+
console.log('IndexNow submission complete');
168+
process.exit(0);
169+
})
170+
.catch((error) => {
171+
console.error('');
172+
console.error('==========================');
173+
console.error('IndexNow submission failed:', error.message);
174+
process.exit(1);
175+
});

0 commit comments

Comments
 (0)