Is AutoTDK - SEO Search Keyword Tool safe?
AutoTDK is medium risk. Confirming the AI detector in AutoTDK collects visible page text and sends up to 2,000 characters plus the page host to autotdk.com. The content script requires confirmation and a minimum length; captured traffic showed only a favicon hit.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
AI Detector Sends Page Text to AutoTDK
Confirming the AI detector in AutoTDK collects visible page text and sends up to 2,000 characters plus the page host to autotdk.com.
The content script requires confirmation and a minimum length; captured traffic showed only a favicon hit.
You confirm the AI detector feature on a page.
The UI wires the confirmation component's `onSure` handler to the detector function.
The extension extracts page text and sends it to AutoTDK for analysis.
The content script sends up to 2,000 characters and the current host to the background service worker.
| Field | Value | Why it matters | |
|---|---|---|---|
Page text | 2026 SEO checklist: improve page titles, headings, internal links, and content depth. (illustrative) | Text from the page you are checking leaves the browser for analysis; private or internal page content can be included. | |
Page host | www.example.com (illustrative) | The receiving service can associate the submitted text with the site you were viewing. | |
Extension source label | seo_search_keyword_tool | The request marks the submission as coming from the SEO Search Keyword Tool extension. |
| source | seo_search_keyword_tool |
| Content-Type | application/json |
The detector code path that prepares and sends page text
detect: async () => {
l.value = !0, i.globalLoading = !0, d.needSure = !1, h.timers = 6;
try {
if (!document.querySelector("h1")) throw d.rules_tip = t("ai-detector.no_h1_tips").replace("[check]", ""), l.value = !1, i.globalLoading = !1, new Error("no h1");
const e = function(e = 0) {
try {
const t = document.createElement("div");
let a = document.querySelector("h1");
if (a) {
const e = window.getComputedStyle(a);
"none" !== e.display && "hidden" !== e.visibility || (a = null)
}
if (a) {
const e = document.createRange();
e.setStartBefore(a), e.setEndAfter(document.body), t.appendChild(e.cloneContents())
} else t.appendChild(document.body.cloneNode(!0));
const s = ["CODE", "PRE", "IMG", "VIDEO", "AUDIO", "SCRIPT", "STYLE", "NOSCRIPT", "META", "LINK", "IFRAME", "NAV", "FOOTER", "SVG", "CANVAS"],
n = document.createTreeWalker(t, NodeFilter.SHOW_ELEMENT, {
acceptNode: e => s.includes(e.tagName) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
}),
i = [];
for (; n.nextNode();) i.push(n.currentNode);
i.forEach((e => e.parentNode?.removeChild(e)));
let l = "",
o = !1,
r = null;
const c = new Set(["DIV", "P", "H1", "H2", "H3", "H4", "H5", "H6", "LI", "UL", "OL"]),
d = document.createTreeWalker(t, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null),
u = (t, a = !1) => {
if (e > 0 && l.length >= e) return !1;
const s = e > 0 ? e - l.length : 1 / 0;
let n = t;
return t.length > s && (n = "", o = !0), l += n, !o
};
e: for (; d.nextNode();) {
const e = d.currentNode;
if (e.nodeType === Node.TEXT_NODE) {
let t = e.textContent.replace(/\s+/g, " " ).replace(/^\s+|\s+$/g, "");
if (t) {
if ("BLOCK_START" === r && (t = t.trimStart()), !u(t)) break;
r = "TEXT"
}
} else if (e.nodeType === Node.ELEMENT_NODE) {
const t = e.tagName,
a = window.getComputedStyle(e);
if ("none" === a.display || "hidden" === a.visibility) continue;
if (c.has(t)) {
if (l.length > 0 && !l.endsWith("\n") && !u("\n", !0)) break;
r = "BLOCK_START"
}
switch (t) {
case "LI":
if (!u("OL" === e.parentNode.tagName ? `${Array.from(e.parentNode.children).indexOf(e)+1}. ` : "• ")) break e;
break;
case "BR":
if (!u("\n", !0)) break e;
break;
case "H1":
case "H2":
case "H3":
case "H4":
case "H5":
case "H6":
if (!u(`\n${"#".repeat(parseInt(t[1]))}`, !0)) break e
}
}
if (o) break
}
let m = l.slice(0, e);
return m = m.replace(/(#+)\s*\n+/g, "$1\n\n").replace(/(\S)\n(\S)/g, "$1 $2").replace(/\s+$/, "").trim(), o && (m = m.replace(/\s+$/, "") + "..."), m
} catch (e) {
throw e
}
}(2e3),
s = e.substring(0, 2e3);
if (s.length <= 300) throw d.content_tip = t("ai-detector.contet_short"), new Error("content short");
if (await new Promise((e => setTimeout(e, 500))), u) return;
const n = await chrome.runtime.sendMessage({
type: "ai-detector",
content: s,
host: location.host
});
if (u) return;
if (1e5 === n.code && n.data.task_id) h.result_timer = setTimeout((() => {
u || (h.result(n.data.task_id, s), clearTimeout(d.result_timer))
}), 2e3);
else {
if (120008 !== n.code) throw new Error("error");
d.err = !1, d.limit_tip = t("ai-detector.limit_tips").replace("[href]", `https://autotdk.com${"en"===a.value?"":`/${a.value}`}/ai-detector`), d.limit = !0, l.value = !1, i.globalLoading = !1
}
} catch (e) {
l.value = !1, i.globalLoading = !1, d.err = !1, ["content short", "no h1"].includes(e.message) || (d.err = !0, H.nk.error({
message: t('message["Operation failed"]'),
appendTo: w.querySelector(".autotdk_ext_content"),
customClass: "position-absolute z-4"
}))
}
}} else if ("ai-detector" === e.type) {
let o = await fetch(`${t}/api/text/detect-ai?f=seo_search_keyword_tool&h=${e.host}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
source: "seo_search_keyword_tool"
},
body: JSON.stringify({
text: e.content
})
});
o = await o.json(), s(o)
}- autotdk.com
Receives the AI-detector POST at /api/text/detect-ai with submitted page text and the page host.