{"id":352972,"date":"2026-08-24T15:57:06","date_gmt":"2026-08-24T15:57:06","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/smartmatch-search\/"},"modified":"2026-08-24T16:05:57","modified_gmt":"2026-08-24T16:05:57","slug":"smartmatch-search","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/smartmatch-search\/","author":23232135,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.0","stable_tag":"1.0.0","tested":"7.1","requires":"7.0","requires_php":"8.0","requires_plugins":null,"header_name":"SmartMatch Search","header_author":"Burst Statistics","header_description":"Overrides the WordPress site search with AI-powered semantic relevance, using the WordPress \"AI\" plugin for provider\/model selection and Burst Statistics to restrict the feature to validated human visits (bot exclusion). Works in both cookie and cookieless mode.","assets_banners_color":"484b5d","last_updated":"2026-08-24 16:05:57","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/burst-statistics.com","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":50,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"burstbv","date":"2026-08-24 16:05:57"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3664878,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3664878,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3664878,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3664878,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[220145,944,140844,3541,47816],"plugin_category":[],"plugin_contributors":[277287,193375,259172,78306,223420],"plugin_business_model":[],"class_list":["post-352972","plugin","type-plugin","status-publish","hentry","plugin_tags-ai-search","plugin_tags-search","plugin_tags-semantic-search","plugin_tags-site-search","plugin_tags-smart-search","plugin_contributors-burstbv","plugin_contributors-hesseldejong","plugin_contributors-parthnvaswani","plugin_contributors-rogierlankhorst","plugin_contributors-sh4lin","plugin_committers-burstbv","plugin_committers-rogierlankhorst"],"banners":{"banner":"https:\/\/ps.w.org\/smartmatch-search\/assets\/banner-772x250.png?rev=3664878","banner_2x":"https:\/\/ps.w.org\/smartmatch-search\/assets\/banner-1544x500.png?rev=3664878","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/smartmatch-search\/assets\/icon-128x128.png?rev=3664878","icon_2x":"https:\/\/ps.w.org\/smartmatch-search\/assets\/icon-256x256.png?rev=3664878","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>SmartMatch Search keeps the normal WordPress search results intact and surfaces\nthe most semantically relevant post(s) on top as a \"Best match\" suggestion.<\/p>\n\n<h4>Search that understands intent<\/h4>\n\n<p>Classic WordPress search matches keywords: when the words in the query do not\nliterally appear in a post, the visitor gets \"nothing found\" \u2014 even when your\nsite has exactly the right page. SmartMatch Search judges meaning instead, so\nvisitors can search the way they actually talk:<\/p>\n\n<ul>\n<li>\"page where I can find my orders\" surfaces your <strong>Order history<\/strong> page,\neven though none of those words appear in its title.<\/li>\n<li>\"how do I get my money back\" finds your <strong>Returns &amp; refunds<\/strong> policy \u2014\nno overlap in wording needed.<\/li>\n<li>\"wo finde ich meine rechnung\" (German) still lands on your <strong>Invoices<\/strong>\npage: the model matches meaning across languages, so visitors searching in\ntheir own language get the right result.<\/li>\n<li>\"cancel my subscription\" surfaces <strong>Manage your membership<\/strong>, instead of\nevery post that happens to contain the word \"cancel\".<\/li>\n<li>\"what time are you open on saturday\" finds your <strong>Contact &amp; visiting\ninformation<\/strong> page.<\/li>\n<\/ul>\n\n<p>And it filters as well as it finds: a query like \"burst viewer\" will not match\na post just because the word \"burst\" appears somewhere \u2014 only posts that are\ngenuinely about the topic are suggested. The native results stay untouched\nbelow the suggestion, so search never gets worse, only better.<\/p>\n\n<h4>Why SmartMatch Search connects to Burst<\/h4>\n\n<p>The Burst Statistics integration exists for two reasons:<\/p>\n\n<ol>\n<li><strong>Real visitors only.<\/strong> Every AI search is a paid model call, so you only\nwant to spend it on real people. Burst already knows which visits are\nvalidated human visits \u2014 its tracking filters out bots, crawlers and spam\nbefore a visit is recorded, in both cookie and cookieless mode. SmartMatch\nSearch runs the AI only for those validated visits; bots silently get the\nnormal search results at zero AI cost.<\/li>\n<li><strong>Searches land in your statistics.<\/strong> Searches run through the normal\nWordPress search request, so they show up directly in Burst's Engagement\ntab. You see what visitors are looking for \u2014 and where your content falls\nshort \u2014 in the same dashboard you already use.<\/li>\n<\/ol>\n\n<h4>How it works<\/h4>\n\n<p>SmartMatch Search is a thin integration layer between two plugins:<\/p>\n\n<ul>\n<li><strong>AI<\/strong> (<code>WordPress\\AI<\/code>) \u2014 supplies the configured provider and text model.\nProvider\/model selection is delegated entirely to the AI plugin via\n  WordPress\\AI\\get_ai_service() and the <code>wpai_preferred_text_models<\/code> filter.<\/li>\n<li><strong>Burst Statistics \/ Burst Pro<\/strong> \u2014 supplies visit validation. The AI search\nonly runs for a <em>validated<\/em> Burst visit (a uid with a recorded hit in the\nlast 30 minutes), which excludes bots. Works in both cookie mode\n(<code>burst_uid<\/code> cookie) and cookieless mode (session fingerprint).<\/li>\n<\/ul>\n\n<p>How it works on a search request (<code>?s=...<\/code>):<\/p>\n\n<ol>\n<li>The visitor must be a validated Burst visit, otherwise the native results\nare shown untouched.<\/li>\n<li>Public, published posts of public post types are collected (title +\nexcerpt, capped, freshest first) and sent to the model with the search\nterm.<\/li>\n<li>The model returns only the genuinely relevant post IDs, ranked.<\/li>\n<li>Those posts are merged into the main query's result array itself (via the\n   the_posts filter): prepended above the native matches, de-duplicated, and\noptionally tagged with a configurable badge (via <code>the_title<\/code>). Because the\nchange happens at the data layer, placement does not depend on how the theme\nrenders the loop \u2014 it works the same for classic templates and block themes.\nWhen the native search found nothing, the suggestions become the results so\nthe page is not empty.<\/li>\n<li>Results are cached per search term (transient), invalidated when the\ncandidate set changes. Suggestions appear on the first results page only.<\/li>\n<\/ol>\n\n<p>If the AI is unavailable, errors, or finds nothing relevant, nothing is added\nand the normal results (or \"nothing found\") are displayed as usual.<\/p>\n\n<h3>Settings<\/h3>\n\n<p>The plugin adds a \"SmartMatch Search\" item to the Burst settings menu\n(Statistics \u2192 Settings \u2192 SmartMatch Search), with the options stored in the\nBurst settings store:<\/p>\n\n<ul>\n<li><strong>Daily search budget<\/strong> (default 50, seeded on activation) \u2014 the maximum\nnumber of AI searches per day. Only actual model calls count (cached\nrankings are free); once the budget is spent, visitors get the normal\nsearch results until the site's next midnight.<\/li>\n<li><strong>Posts sent to the AI<\/strong> (default 200) \u2014 how many posts (most recently\nupdated first) are sent to the model as candidates per search. More posts\nimprove coverage on large sites, but raise the token cost per search.<\/li>\n<li><strong>Excerpt length in words<\/strong> (default 40) \u2014 how many words of each post are\nincluded in the prompt. Longer excerpts give the model more context to\njudge relevance, but increase token usage per search.<\/li>\n<li><strong>Badge for the suggested result<\/strong> (default empty) \u2014 text shown before the\ntitle of the suggested result, e.g. \"(Best match)\", so visitors see why it\nis on top. Leave empty to show the suggestion without a badge.<\/li>\n<li><strong>Prompt customization<\/strong> \u2014 extra instructions appended to the system\ninstruction sent to the model, e.g. to prioritize certain content or set a\ntie-breaking rule.<\/li>\n<\/ul>\n\n<p>Editing the prompt customization or excerpt length invalidates previously\ncached rankings, so changes take effect immediately.<\/p>\n\n<h3>Filters<\/h3>\n\n<ul>\n<li><code>smartmatch_search_enabled<\/code> (bool, WP_Query) \u2014 disable per query.<\/li>\n<li><code>smartmatch_search_is_validated_visit<\/code> (bool, uid) \u2014 override the visit gate.<\/li>\n<li><code>smartmatch_search_post_types<\/code> (string[]) \u2014 post types to search.<\/li>\n<li><code>smartmatch_search_candidate_limit<\/code> (int) \u2014 override the configured number\nof candidate posts sent to the model.<\/li>\n<li><code>smartmatch_search_excerpt_words<\/code> (int) \u2014 override the configured excerpt\nlength in words.<\/li>\n<li><code>smartmatch_search_cache_ttl<\/code> (int seconds, default 1 hour) \u2014 ranking cache.<\/li>\n<li><code>smartmatch_search_suggestion_count<\/code> (int, default 1) \u2014 how many to surface.<\/li>\n<li><code>smartmatch_search_badge_label<\/code> (string) \u2014 override the configured badge\ntext; an empty string disables the badge.<\/li>\n<li><code>smartmatch_search_badge_html<\/code> (string) \u2014 replace the rendered badge markup;\nan empty string suppresses the badge. The result is passed through\nwp_kses_post() before it is output.<\/li>\n<li><code>smartmatch_search_rate_limit<\/code> (int, default 5) \u2014 AI searches per IP \/ window.<\/li>\n<li><code>smartmatch_search_rate_window<\/code> (int seconds, default 5 min) \u2014 the window.<\/li>\n<li><code>smartmatch_search_daily_budget<\/code> (int) \u2014 override the configured daily budget.<\/li>\n<li><code>smartmatch_search_debug<\/code> (bool) \u2014 toggle debug logging (see Testing).<\/li>\n<li><code>wpai_preferred_text_models<\/code> (from the AI plugin) \u2014 choose provider\/model.<\/li>\n<li><code>smartmatch_search_ranked<\/code> (action: ids, term, WP_Query) \u2014 observe results.<\/li>\n<li><code>smartmatch_search_rate_limited<\/code> (action: term, WP_Query) \u2014 fired when an IP\nexceeds the limit and falls back to normal search.<\/li>\n<li><code>smartmatch_search_budget_exceeded<\/code> (action: term) \u2014 fired when an AI search\nis skipped because the daily budget is spent.<\/li>\n<\/ul>\n\n<h3>Testing \/ diagnostics<\/h3>\n\n<p>Define <code>SMARTMATCH_SEARCH_BYPASS_GATES<\/code> as true (e.g. in wp-config.php) to disable\nthe visitor gates \u2014 the Burst uid validation and the per-IP rate limit \u2014 so AI\nsearch runs without a tracked Burst visit. Useful for local testing or to verify\na production setup where no validated visit is available. It does NOT bypass the\ndependency, main-query or relevance checks. Leave it undefined in normal use.<\/p>\n\n<pre><code>define( 'SMARTMATCH_SEARCH_BYPASS_GATES', true );\n<\/code><\/pre>\n\n<p>Define <code>SMARTMATCH_SEARCH_DEBUG<\/code> as true to log every point where AI search bails\nout silently to the PHP error log (prefixed \"[SmartMatch Search]\"): missing\ndependencies, no validated visit, rate limit hit, no AI credentials, provider\nunsupported, SDK exception, unparseable response, nothing relevant, or the IDs\nfinally selected. When the constant is undefined, logging follows WP_DEBUG; the\n    smartmatch_search_debug filter can override it. Logging stays off in normal use.<\/p>\n\n<pre><code>define( 'SMARTMATCH_SEARCH_DEBUG', true );&lt;h3&gt;Rate limiting&lt;\/h3&gt;\n<\/code><\/pre>\n\n<p>To curb abuse and AI cost, each IP may run at most 5 AI searches per rolling\n5-minute window (both filterable). Beyond that, the request falls back to normal\nWordPress search. Only searches that trigger an actual model call count against\nthe limit: cached rankings are served freely. The visitor IP is resolved through\nBurst's own proxy-aware detector (<code>Burst\\Frontend\\Ip\\Ip::get_ip_address()<\/code>) and\nis only stored hashed.<\/p>\n\n<h3>Requirements<\/h3>\n\n<p>Both the \"AI\" plugin and Burst Statistics (free) or Burst Pro must be active.\nThe AI plugin needs a configured provider with valid credentials.<\/p>\n\n<p>The AI plugin is declared through the WordPress \"Requires Plugins\" header, so\nWordPress itself enforces it on activation. The Burst requirement is enforced\nat runtime instead (with an admin notice when missing): that header is an\nAND-list of wordpress.org slugs, so it cannot express \"Burst free OR Burst\nPro\", and Burst Pro is not distributed on wordpress.org. The plugin simply\nstays inactive (and tells you why) until both are present.<\/p>\n\n<h3>External services<\/h3>\n\n<p>SmartMatch Search does not connect to any service of its own and sends no data\nto Burst or SmartMatch servers. When a search qualifies for AI ranking, the\nsearch term and the candidate posts (post ID, post type, title and a short\nexcerpt of public, published content) are sent to the AI provider that you\nyourself configured in the WordPress \"AI\" plugin \u2014 which provider that is\ndepends entirely on your configuration there, and its terms of service and\nprivacy policy apply to those requests. This happens only for validated human\nvisits, within the configured daily budget, and only when the ranking is not\nalready cached. No other data (no IP addresses, no visitor identifiers) is\nincluded in these requests.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate the \"AI\" plugin (https:\/\/wordpress.org\/plugins\/ai\/)\nand configure a provider with valid credentials in its settings.<\/li>\n<li>Install and activate Burst Statistics (free) or Burst Pro.<\/li>\n<li>Install and activate SmartMatch Search.<\/li>\n<li>Optionally adjust the settings under Statistics \u2192 Settings \u2192 SmartMatch\nSearch (daily search budget, badge, prompt customization).<\/li>\n<\/ol>\n\n<p>That is all: your search form and results template keep working as before,\nand the AI suggestion appears on top of the results for validated human\nvisits.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"why%20does%20smartmatch%20search%20require%20burst%20statistics%20and%20the%20ai%20plugin%3F\"><h3>Why does SmartMatch Search require Burst Statistics and the AI plugin?<\/h3><\/dt>\n<dd><p>Every AI search is a paid model call, so you only want to spend it on real\npeople. Burst already knows which visits are validated human visits \u2014 its\ntracking filters out bots, crawlers and spam in both cookie and cookieless\nmode \u2014 so SmartMatch Search runs the AI only for those visits; bots silently\nget the normal search results at zero cost. The AI plugin supplies the\nprovider and model: you configure your AI credentials once, centrally, and\nthis plugin simply uses that configuration.<\/p><\/dd>\n<dt id=\"how%20do%20i%20keep%20the%20ai%20costs%20under%20control%3F\"><h3>How do I keep the AI costs under control?<\/h3><\/dt>\n<dd><p>Four mechanisms bound the spend. Only validated human visits trigger a model\ncall; each IP is rate-limited (5 AI searches per 5 minutes by default);\nrankings are cached per search term, and cached results cost nothing; and the\ndaily search budget (default 50) caps the total number of model calls per\nday. Once the budget is spent, visitors simply get the normal search results\nuntil the site's next midnight.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>July 9th, 2026<\/li>\n<li>New: Initial release.<\/li>\n<\/ul>","raw_excerpt":"AI search that understands what visitors mean: they find the right page in their own words \u2014 or another language. Burst keeps bots out.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/352972","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=352972"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/burstbv"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=352972"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=352972"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=352972"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=352972"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=352972"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=352972"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}