When you publish a post, Bing may take days to notice it. IndexNow lets you tell it the same minute. This post explains what the protocol does, what it promises (less than people assume), and gives a short Python script to submit changed URLs. I tested the script against a local mock server I wrote, not against a real search engine. That distinction matters and I repeat it below.
I read the IndexNow documentation and FAQ on 2026-10-16.
How does IndexNow work?
From the documentation:
- You host a UTF-8 text key file at your site root to prove you own the host. Keys are 8 to 128 hexadecimal characters. The docs recommend this root-level option over hosting the key elsewhere.
- For one URL, you send a request to
https://<searchengine>/indexnow?url=...&key=...with the URL encoded. - For several, you send a JSON POST to
/indexnowwithhost,keyand aurlList. Up to 10,000 URLs per POST. - Engines that adopt the protocol share submitted URLs with each other. The FAQ lists these endpoints: Amazon, Bing, Naver, Seznam.cz, Yandex and Yep, plus a global one at
api.indexnow.org.
Response codes: 200 only means the engine received your URL, 202 means key validation is pending, and 429 means too many requests, which the page ties to potential spam.
What does IndexNow not do?
Three limits, all from the pages themselves:
- No indexing guarantee. The docs say the response "only indicates that the search engine has received your URL". The FAQ adds that submitting does not guarantee immediate indexing and gives no timeframe.
- Not a full-site tool. The FAQ says not to submit every small layout or cosmetic change, and that IndexNow is not meant for submitting every URL at once. It recommends using sitemaps alongside it for the complete inventory.
- Google is not mentioned. Neither page I read says whether Google supports it. Treat it as a Bing-and-friends tool and keep your normal Google setup.
Why bother then? It is free, small, and Bing's index feeds other products. I did not verify how any specific assistant uses Bing, so I will not claim IndexNow improves AI answers. See the AEO checklist for what the primary sources do support.
How do you set it up in four steps?
- Generate a key. 32 hex characters is within the allowed range. In Python,
secrets.token_hex(16). - Host the key file at your site root so it is reachable at
https://yourdomain/<key>.txt, containing the key. The documentation page I read says to host a UTF-8 text key file at the root; check the current page for the exact file naming rule before you deploy, since I did not confirm it beyond that. - Submit changed URLs with the script below.
- Submit only on real changes: new post, meaningful update, deletion.
On a Next.js site like this one, the key file goes in public/, which is served from the root.
The submit script
The script builds the JSON payload and refuses to send obviously wrong input: a key outside 8 to 128 hex characters, URLs on a different host, or an empty or oversized list. The submit function POSTs the payload and returns the status code.
KEY_RE = re.compile(r"^[0-9a-fA-F]{8,128}$") # docs: 8 to 128 hexadecimal characters
def build_payload(host, key, urls):
if not KEY_RE.match(key):
raise ValueError("key must be 8-128 characters")
bad = [u for u in urls if urlparse(u).netloc != host]
if bad:
raise ValueError(f"URLs not on host {host}: {bad}")
if not 1 <= len(urls) <= 10000:
raise ValueError("urlList must hold 1 to 10,000 URLs")
return {"host": host, "key": key, "urlList": urls}
def submit(endpoint, payload):
req = urllib.request.Request(
endpoint,
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json; charset=utf-8"},
method="POST",
)
with urllib.request.urlopen(req, timeout=10) as r:
return r.status
How I tested it without bothering a search engine
I wrote a tiny local HTTP server that returns 200 if the POST body contains host, key and urlList, and 400 otherwise. Then I ran the script against it on 2026-10-16:
key length: 32 | key file name: fdc5f09a719355e285e7e56cc5c560d3.txt
mock status: 200
wrong host: rejected -> URLs not on host example.com: ['https://other.org/x']
short key: rejected -> key must be 8-128 characters
empty list: rejected -> urlList must hold 1 to 10,000 URLs
What this proves: the payload has the three fields the documentation names, and the guard checks behave. What it does not prove: that a real endpoint accepts it, that your key file is reachable, or that anything gets indexed. To go live, point endpoint at the endpoint you choose from the IndexNow site and watch for 200 or 202. A 403 or similar usually means the key file is missing or unreachable, but I did not test that case.
Which URLs should you submit, and when?
A simple rule that matches the FAQ:
- Submit: a newly published post, a post you substantially rewrote, a deleted URL, a price or availability change on a product page.
- Skip: typo fixes, template changes, tag page reshuffles.
- Batch: collect the day's changes and send one POST, which is both polite and within the 10,000 limit.
If you publish through a Git push, run the script in your deploy step with the list of changed content files mapped to URLs. Do not run it on every build. For the full inventory, keep your sitemap healthy, and see how to check which AI crawlers can reach you.
What I could not verify
- Whether Google supports IndexNow (the pages I read are silent).
- The exact key-file naming rule and the behaviour of each engine's error responses.
- How quickly Bing or any other engine acts after a 200.
- Any claim that IndexNow raises AI citations. I found no primary source for it.
To test whether AI tools cite you at all, use the method in the AI citation test protocol.



