{"id":3176,"date":"2026-09-05T07:00:52","date_gmt":"2026-09-05T07:00:52","guid":{"rendered":"https:\/\/digitalbridgehub.com\/acrossysdigital\/?page_id=3176"},"modified":"2026-09-09T08:15:22","modified_gmt":"2026-09-09T08:15:22","slug":"keyword-clusterer","status":"publish","type":"page","link":"https:\/\/digitalbridgehub.com\/acrossysdigital\/tools\/keyword-clusterer\/","title":{"rendered":"Keyword Clusterer"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"3176\" class=\"elementor elementor-3176\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-24b23c9 e-flex e-con-boxed qodef-elementor-content-no e-con e-parent\" data-id=\"24b23c9\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f7d76c9 elementor-widget elementor-widget-shortcode\" data-id=\"f7d76c9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">\r\n<div class=\"aitool-wrapper\">\r\n    <div class=\"aitool-header\">\r\n        <h2>Semantic Keyword Clusterer<\/h2>\r\n        <p>Group search queries into intent-focused content hubs and topic clusters using Gemini AI<\/p>\r\n    <\/div>\r\n\r\n    <div class=\"aitool-body\">\r\n        <div class=\"aitool-calc-grid\">\r\n            <!-- Left Input Column -->\r\n            <div class=\"aitool-card\">\r\n                <div class=\"aitool-card-header\">\r\n                    <span class=\"aitool-card-title\">Keyword Repository<\/span>\r\n                    <span class=\"aitool-card-badge\">Data Entry<\/span>\r\n                <\/div>\r\n                <div class=\"aitool-card-body\">\r\n                    <div class=\"aitool-row\">\r\n                        <label for=\"kc-keywords\">Target Keywords (One per line) *<\/label>\r\n                        <textarea id=\"kc-keywords\" rows=\"12\" placeholder=\"seo audit checklist&#10;technical seo audit&#10;how to do a backlink audit&#10;backlink analysis tools&#10;content optimization tips&#10;how to optimize on-page content\"><\/textarea>\r\n                        <div class=\"serp-char-row\">\r\n                            <span id=\"kc-keyword-counter\">0 entries detected<\/span>\r\n                            <span id=\"kc-validation-badge\" class=\"serp-char-badge warn\">Awaiting Input<\/span>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"aitool-row\">\r\n                        <label for=\"kc-cluster-count\">Cluster Count Target (Optional)<\/label>\r\n                        <input type=\"number\" id=\"kc-cluster-count\" min=\"0\" max=\"25\" value=\"0\" placeholder=\"0 = Auto-detect based on intent\">\r\n                        <div class=\"aitool-row-hint\">Set to 0 to let the semantic model determine the optimal topic silos automatically<\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"aitool-buttons\" style=\"margin-top: 15px;\">\r\n                        <button type=\"button\" id=\"kc-cluster-btn\" class=\"aitool-btn aitool-btn-primary\">Generate Clusters<\/button>\r\n                        <button type=\"button\" id=\"kc-clear-btn\" class=\"aitool-btn aitool-btn-secondary\">Clear Inputs<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- Right Results Column -->\r\n            <div class=\"aitool-sticky-panel\">\r\n                <div class=\"aitool-card\">\r\n                    <div class=\"aitool-card-header\">\r\n                        <span class=\"aitool-card-title\">Clustering Overview<\/span>\r\n                        <span class=\"aitool-card-badge\" id=\"kc-status-badge\">Idle<\/span>\r\n                    <\/div>\r\n                    <div class=\"aitool-card-body\">\r\n                        <div class=\"aitool-metric-grid\">\r\n                            <div class=\"aitool-metric-card highlight\">\r\n                                <div class=\"aitool-mc-label\">Clusters Identified<\/div>\r\n                                <div class=\"aitool-mc-val\" id=\"kc-cluster-total\">0<\/div>\r\n                            <\/div>\r\n                            <div class=\"aitool-metric-card\">\r\n                                <div class=\"aitool-mc-label\">Assigned Terms<\/div>\r\n                                <div class=\"aitool-mc-val\" id=\"kc-assigned-total\">0<\/div>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div id=\"kc-export-actions\" class=\"aitool-actions\" style=\"display: none; margin-top: 15px;\">\r\n                            <button type=\"button\" id=\"kc-copy-btn\" class=\"aitool-action-btn\">Copy Structure<\/button>\r\n                            <button type=\"button\" id=\"kc-export-btn\" class=\"aitool-action-btn\">Download CSV<\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Dynamic Output Stream -->\r\n        <div id=\"kc-results-stream\" style=\"margin-top: 25px; display: flex; flex-direction: column; gap: 15px;\">\r\n            <div class=\"aitool-card-highlight\" style=\"margin-bottom: 0;\">\r\n                <div class=\"aitool-card-highlight-label\">Awaiting Execution<\/div>\r\n                <div class=\"aitool-card-content\" style=\"color: var(--BodyTxtColor);\">\r\n                    Enter at least 3 keyword entries and select Generate Clusters to create structured content topic hubs.\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"aitool-info-box\" style=\"margin-top: 25px;\">\r\n            <strong>Architectural Content Planning:<\/strong>\r\n            <ul style=\"margin: 10px 0 0 20px; line-height: 1.7;\">\r\n                <li><strong>Hub-and-Spoke Topology:<\/strong> Map each generated cluster directly to a dedicated cornerstone pillar page or topical resource center.<\/li>\r\n                <li><strong>Search Intent Separation:<\/strong> Terms that address transactional questions versus purely informational how-to queries should remain in isolated clusters.<\/li>\r\n                <li><strong>Avoid Cannibalization:<\/strong> Ensure two separate pages on your website are not targeting identical semantic keywords within the same cluster.<\/li>\r\n            <\/ul>\r\n        <\/div>\r\n    <\/div>\r\n<\/div>\r\n\r\n<script>\r\njQuery(document).ready(function ($) {\r\n    var ajaxUrl = 'https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-admin\/admin-ajax.php';\r\n    var nonce   = '89030fef68';\r\n\r\n    var currentClusters = [];\r\n\r\n    var $keywordsInput   = $('#kc-keywords');\r\n    var $clusterCount    = $('#kc-cluster-count');\r\n    var $counterText     = $('#kc-keyword-counter');\r\n    var $validBadge      = $('#kc-validation-badge');\r\n\r\n    var $clusterBtn      = $('#kc-cluster-btn');\r\n    var $clearBtn        = $('#kc-clear-btn');\r\n    var $copyBtn         = $('#kc-copy-btn');\r\n    var $exportBtn       = $('#kc-export-btn');\r\n\r\n    var $statusBadge     = $('#kc-status-badge');\r\n    var $clusterTotal    = $('#kc-cluster-total');\r\n    var $assignedTotal   = $('#kc-assigned-total');\r\n    var $exportActions   = $('#kc-export-actions');\r\n    var $resultsStream   = $('#kc-results-stream');\r\n\r\n    function escapeHtml(text) {\r\n        if (!text) return '';\r\n        return String(text).replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;')\r\n                           .replace(\/>\/g, '&gt;').replace(\/\"\/g, '&quot;').replace(\/'\/g, '&#39;');\r\n    }\r\n\r\n    function parseInputKeywords() {\r\n        var raw = $keywordsInput.val().trim();\r\n        if (!raw) {\r\n            $counterText.text('0 entries detected');\r\n            $validBadge.attr('class', 'serp-char-badge warn').text('Awaiting Input');\r\n            return [];\r\n        }\r\n\r\n        var lines = raw.split(\/\\r?\\n\/).map(function (item) {\r\n            return item.trim();\r\n        }).filter(Boolean);\r\n\r\n        $counterText.text(lines.length + ' keyword' + (lines.length !== 1 ? 's' : '') + ' detected');\r\n\r\n        if (lines.length >= 3) {\r\n            $validBadge.attr('class', 'serp-char-badge ok').text('Ready');\r\n        } else {\r\n            $validBadge.attr('class', 'serp-char-badge over').text('Minimum 3 Required');\r\n        }\r\n\r\n        return lines;\r\n    }\r\n\r\n    async function executeClustering(keywords, targetCount) {\r\n        var keywordList = keywords.join('\\n');\r\n        var directive = targetCount > 0 \r\n            ? 'Generate exactly ' + targetCount + ' balanced clusters.' \r\n            : 'Organize into the optimal number of semantic topical clusters based on search intent.';\r\n\r\n        var prompt = 'You are an enterprise SEO content architect.\\n' +\r\n            'Group the following search keywords into distinct, non-overlapping topical clusters.\\n\\n' +\r\n            'Keywords:\\n' + keywordList + '\\n\\n' +\r\n            'Directives:\\n' + directive + '\\n\\n' +\r\n            'Output Format:\\n' +\r\n            'Respond ONLY with a valid JSON object matching the following specification:\\n' +\r\n            '{\\n' +\r\n            '  \"clusters\": [\\n' +\r\n            '    {\\n' +\r\n            '      \"name\": \"Cluster Title (concise, 2 to 4 words)\",\\n' +\r\n            '      \"description\": \"Specific user search intent or content scope addressed by this cluster\",\\n' +\r\n            '      \"keywords\": [\"keyword 1\", \"keyword 2\"]\\n' +\r\n            '    }\\n' +\r\n            '  ]\\n' +\r\n            '}\\n\\n' +\r\n            'Rules:\\n' +\r\n            '1. Every input keyword must appear in exactly one cluster.\\n' +\r\n            '2. Preserve original keyword phrasing accurately without altering spelling.\\n' +\r\n            '3. Output raw JSON without commentary or Markdown backticks.';\r\n\r\n        var params = new URLSearchParams();\r\n        params.append('action', 'cluster_keywords_content');\r\n        params.append('nonce', nonce);\r\n        params.append('prompt', prompt);\r\n\r\n        var resp = await fetch(ajaxUrl, {\r\n            method: 'POST',\r\n            headers: { 'Content-Type': 'application\/x-www-form-urlencoded' },\r\n            body: params.toString()\r\n        });\r\n\r\n        if (!resp.ok) {\r\n            throw new Error('Server returned HTTP ' + resp.status);\r\n        }\r\n\r\n        var json = await resp.json();\r\n        if (!json.success) {\r\n            throw new Error(json.data ? json.data.message : 'Clustering request failed.');\r\n        }\r\n\r\n        var raw = json.data.text.trim();\r\n        var match = raw.match(\/\\{[\\s\\S]*\\}\/);\r\n        if (!match) {\r\n            throw new Error('Could not parse clean JSON from AI output.');\r\n        }\r\n\r\n        var parsed = JSON.parse(match[0]);\r\n        if (!Array.isArray(parsed.clusters)) {\r\n            throw new Error('Malformed cluster payload received.');\r\n        }\r\n\r\n        return parsed.clusters;\r\n    }\r\n\r\n    function renderClusterStream(clusters) {\r\n        var html = '';\r\n        var totalTerms = 0;\r\n\r\n        clusters.forEach(function (cluster, idx) {\r\n            totalTerms += cluster.keywords.length;\r\n\r\n            html += '<div class=\"aitool-card\">';\r\n            html += '<div class=\"aitool-card-header kc-toggle-header\" data-target=\"kc-content-' + idx + '\" style=\"cursor: pointer;\">';\r\n            html += '<div style=\"display: flex; align-items: center; gap: 10px;\">';\r\n            html += '<span class=\"kc-indicator\" style=\"color: var(--PrimaryColor); font-size: 11px; font-weight: bold;\">[ - ]<\/span>';\r\n            html += '<span class=\"aitool-card-title\">' + (idx + 1) + '. ' + escapeHtml(cluster.name) + '<\/span>';\r\n            html += '<\/div>';\r\n            html += '<span class=\"aitool-card-badge\">' + cluster.keywords.length + ' Keywords<\/span>';\r\n            html += '<\/div>';\r\n\r\n            html += '<div class=\"aitool-card-body kc-toggle-body\" id=\"kc-content-' + idx + '\" style=\"display: flex; flex-direction: column; gap: 14px;\">';\r\n\r\n            html += '<div class=\"serp-chip-row\">';\r\n            cluster.keywords.forEach(function (kw) {\r\n                html += '<span class=\"aitool-card-badge\" style=\"background: var(--BodyBGColor); color: var(--AccentTxtColor); border-color: rgba(214,214,214,0.15); padding: 5px 12px; font-size: 12.5px; text-transform: none; letter-spacing: normal;\">' + escapeHtml(kw) + '<\/span>';\r\n            });\r\n            html += '<\/div>';\r\n\r\n            if (cluster.description) {\r\n                html += '<div class=\"aitool-card-highlight\" style=\"margin-bottom: 0; background: var(--BodyBGColor); border-left-color: var(--PrimaryColor);\">';\r\n                html += '<div class=\"aitool-card-highlight-label\">Topical Scope &amp; Intent<\/div>';\r\n                html += '<div style=\"font-size: 13px; color: var(--BodyTxtColor);\">' + escapeHtml(cluster.description) + '<\/div>';\r\n                html += '<\/div>';\r\n            }\r\n\r\n            html += '<\/div><\/div>';\r\n        });\r\n\r\n        $resultsStream.html(html);\r\n\r\n        $clusterTotal.text(clusters.length);\r\n        $assignedTotal.text(totalTerms);\r\n\r\n        \/\/ Accordion functionality\r\n        $('.kc-toggle-header').on('click', function () {\r\n            var targetId = $(this).data('target');\r\n            var $body = $('#' + targetId);\r\n            var $indicator = $(this).find('.kc-indicator');\r\n\r\n            if ($body.is(':visible')) {\r\n                $body.slideUp(180);\r\n                $indicator.text('[ + ]');\r\n            } else {\r\n                $body.slideDown(180);\r\n                $indicator.text('[ - ]');\r\n            }\r\n        });\r\n    }\r\n\r\n    $clusterBtn.on('click', async function () {\r\n        var keywords = parseInputKeywords();\r\n        if (keywords.length < 3) {\r\n            alert('Please supply at least 3 distinct keywords to perform clustering.');\r\n            return;\r\n        }\r\n\r\n        var targetCount = parseInt($clusterCount.val(), 10) || 0;\r\n\r\n        $clusterBtn.prop('disabled', true).text('Synthesizing...');\r\n        $statusBadge.text('Clustering').css('color', 'var(--PrimaryColor)');\r\n        $exportActions.hide();\r\n\r\n        $resultsStream.html(\r\n            '<div class=\"aitool-loading\">' +\r\n            '<span class=\"aitool-loading-spinner\">\u27f3<\/span>' +\r\n            'Analyzing semantic intent and assembling topical clusters...' +\r\n            '<\/div>'\r\n        );\r\n\r\n        try {\r\n            var clusters = await executeClustering(keywords, targetCount);\r\n            currentClusters = clusters;\r\n\r\n            renderClusterStream(clusters);\r\n\r\n            $statusBadge.text('Completed').css('color', '#22c55e');\r\n            $exportActions.show();\r\n\r\n        } catch (err) {\r\n            $statusBadge.text('Failed').css('color', '#ef4444');\r\n            $resultsStream.html('<div class=\"aitool-error\">Error: ' + escapeHtml(err.message) + '<\/div>');\r\n        } finally {\r\n            $clusterBtn.prop('disabled', false).text('Generate Clusters');\r\n        }\r\n    });\r\n\r\n    $copyBtn.on('click', function () {\r\n        if (!currentClusters.length) return;\r\n\r\n        var text = '';\r\n        currentClusters.forEach(function (c) {\r\n            text += '=== ' + c.name.toUpperCase() + ' (' + c.keywords.length + ' Keywords) ===\\n';\r\n            if (c.description) text += 'Intent Scope: ' + c.description + '\\n';\r\n            text += 'Keywords:\\n';\r\n            c.keywords.forEach(function (k) {\r\n                text += '  - ' + k + '\\n';\r\n            });\r\n            text += '\\n';\r\n        });\r\n\r\n        var $btn = $(this);\r\n        navigator.clipboard.writeText(text).then(function () {\r\n            $btn.text('Copied Structure');\r\n            setTimeout(function () { $btn.text('Copy Structure'); }, 1800);\r\n        });\r\n    });\r\n\r\n    $exportBtn.on('click', function () {\r\n        if (!currentClusters.length) return;\r\n\r\n        var rows = [];\r\n        rows.push(['Cluster Name', 'Keywords', 'Count', 'Intent Description']);\r\n\r\n        currentClusters.forEach(function (c) {\r\n            rows.push([\r\n                c.name,\r\n                c.keywords.join('; '),\r\n                c.keywords.length,\r\n                c.description || ''\r\n            ]);\r\n        });\r\n\r\n        var csv = rows.map(function (row) {\r\n            return row.map(function (cell) {\r\n                return '\"' + String(cell).replace(\/\"\/g, '\"\"') + '\"';\r\n            }).join(',');\r\n        }).join('\\n');\r\n\r\n        var blob = new Blob([\"\\uFEFF\" + csv], { type: 'text\/csv;charset=utf-8;' });\r\n        var link = document.createElement('a');\r\n        link.href = URL.createObjectURL(blob);\r\n        link.download = 'keyword-clusters.csv';\r\n        document.body.appendChild(link);\r\n        link.click();\r\n        document.body.removeChild(link);\r\n        URL.revokeObjectURL(link.href);\r\n    });\r\n\r\n    $clearBtn.on('click', function () {\r\n        $keywordsInput.val('');\r\n        $clusterCount.val('0');\r\n        parseInputKeywords();\r\n\r\n        $statusBadge.text('Idle').css('color', 'var(--BodyTxtColor)');\r\n        $clusterTotal.text('0');\r\n        $assignedTotal.text('0');\r\n        $exportActions.hide();\r\n        currentClusters = [];\r\n\r\n        $resultsStream.html(\r\n            '<div class=\"aitool-card-highlight\" style=\"margin-bottom: 0;\">' +\r\n            '<div class=\"aitool-card-highlight-label\">Awaiting Execution<\/div>' +\r\n            '<div class=\"aitool-card-content\" style=\"color: var(--BodyTxtColor);\">' +\r\n            'Enter at least 3 keyword entries and select Generate Clusters to create structured content topic hubs.' +\r\n            '<\/div>' +\r\n            '<\/div>'\r\n        );\r\n    });\r\n\r\n    $keywordsInput.on('input', parseInputKeywords);\r\n    parseInputKeywords();\r\n});\r\n<\/script><\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":3234,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-3176","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/pages\/3176","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/comments?post=3176"}],"version-history":[{"count":4,"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/pages\/3176\/revisions"}],"predecessor-version":[{"id":3180,"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/pages\/3176\/revisions\/3180"}],"up":[{"embeddable":true,"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/pages\/3234"}],"wp:attachment":[{"href":"https:\/\/digitalbridgehub.com\/acrossysdigital\/wp-json\/wp\/v2\/media?parent=3176"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}