{"id":358827,"date":"2026-08-26T17:19:49","date_gmt":"2026-08-26T17:19:49","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/botpolice\/"},"modified":"2026-08-29T10:34:33","modified_gmt":"2026-08-29T10:34:33","slug":"psb-bot-traffic-controller","status":"publish","type":"plugin","link":"https:\/\/gax.wordpress.org\/plugins\/psb-bot-traffic-controller\/","author":13958511,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.4","stable_tag":"1.0.4","tested":"7.1","requires":"6.0","requires_php":"8.0","requires_plugins":null,"header_name":"PSB Bot Traffic Controller","header_author":"Paul Stephen Borile","header_description":"Categorized bot detection (AI training bots, AI assistants, autonomous AI agents, search engine crawlers, scraping agents) with per-category block\/limit\/allow policy, rate limiting, robots.txt generation, and a lightweight invisible human-verification check.","assets_banners_color":"d8d9e1","last_updated":"2026-08-29 10:34:33","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/www.paulstephenborile.com\/psb-bot-traffic-controller","header_author_uri":"https:\/\/www.paulstephenborile.com\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":76,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.2":{"tag":"1.0.2","author":"paulborile","date":"2026-08-26 17:19:31","revision":3667516},"1.0.3":{"tag":"1.0.3","author":"paulborile","date":"2026-08-29 09:04:52","revision":3671283},"1.0.4":{"tag":"1.0.4","author":"paulborile","date":"2026-08-29 10:34:33","revision":3671406}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3667516,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3667516,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3667516,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3667516,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.2","1.0.3","1.0.4"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3667516,"resolution":"1","location":"assets","locale":"","width":790,"height":658},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3667516,"resolution":"2","location":"assets","locale":"","width":790,"height":658}},"screenshots":{"1":"Settings page \u2014 configure block\/limit\/allow policy and the per-hour rate threshold for\neach of the 5 bot categories, plus a global safety-net cap applied to all bot traffic.","2":"Dashboard \u2014 mu-plugin install\/health status, persistent object cache detection, bot\npattern-data version, and a rolling per-category block\/limit\/allow activity summary."}},"plugin_section":[],"plugin_tags":[246479,4866,1174,232610,600],"plugin_category":[54],"plugin_contributors":[277694],"plugin_business_model":[],"class_list":["post-358827","plugin","type-plugin","status-publish","hentry","plugin_tags-ai-crawlers","plugin_tags-bots","plugin_tags-firewall","plugin_tags-rate-limiting","plugin_tags-security","plugin_category-security-and-spam-protection","plugin_contributors-paulborile","plugin_committers-paulborile"],"banners":{"banner":"https:\/\/ps.w.org\/psb-bot-traffic-controller\/assets\/banner-772x250.png?rev=3667516","banner_2x":"https:\/\/ps.w.org\/psb-bot-traffic-controller\/assets\/banner-1544x500.png?rev=3667516","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/psb-bot-traffic-controller\/assets\/icon-128x128.png?rev=3667516","icon_2x":"https:\/\/ps.w.org\/psb-bot-traffic-controller\/assets\/icon-256x256.png?rev=3667516","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/psb-bot-traffic-controller\/assets\/screenshot-1.png?rev=3667516","caption":"Settings page \u2014 configure block\/limit\/allow policy and the per-hour rate threshold for\neach of the 5 bot categories, plus a global safety-net cap applied to all bot traffic."},{"src":"https:\/\/ps.w.org\/psb-bot-traffic-controller\/assets\/screenshot-2.png?rev=3667516","caption":"Dashboard \u2014 mu-plugin install\/health status, persistent object cache detection, bot\npattern-data version, and a rolling per-category block\/limit\/allow activity summary."}],"raw_content":"<!--section=description-->\n<p>PSB Bot Traffic Controller sits in front of your WordPress site and classifies incoming traffic into five bot categories:<\/p>\n\n<ol>\n<li>AI training bots<\/li>\n<li>AI assistants<\/li>\n<li>Autonomous AI agents<\/li>\n<li>Search engine crawlers<\/li>\n<li>Scraping agents<\/li>\n<\/ol>\n\n<p>Each category can be configured independently to block (HTTP 403), rate-limit (HTTP 429 once a configurable per-hour threshold is exceeded), or allow traffic. A global safety-net cap also applies to all detected bot traffic regardless of per-category policy.<\/p>\n\n<p>Detection runs via a free, zero-dependency regex\/User-Agent pattern engine, curated from third-party bot-pattern data (see Credits below).<\/p>\n\n<p>For performance, bot interception runs as early as possible via a small mu-plugin installed automatically into <code>wp-content\/mu-plugins\/<\/code> \u2014 this avoids the cost of a full WordPress bootstrap for traffic that ends up being blocked.<\/p>\n\n<h3>Credits<\/h3>\n\n<p>The bundled bot-pattern data (<code>includes\/detection\/regex\/data\/bot-patterns.php<\/code>) is built from three third-party sources, each under a GPL-compatible license:<\/p>\n\n<ul>\n<li><a href=\"https:\/\/github.com\/matomo-org\/device-detector\">matomo-org\/device-detector<\/a> \u2014 LGPL-3.0<\/li>\n<li><a href=\"https:\/\/github.com\/monperrus\/crawler-user-agents\">crawler-user-agents<\/a> \u2014 MIT<\/li>\n<li><a href=\"https:\/\/github.com\/ai-robots-txt\/ai.robots.txt\">ai.robots.txt<\/a> \u2014 MIT<\/li>\n<\/ul>\n\n<p>No code from these projects is bundled \u2014 only pattern\/name\/category data, transformed at build time (<code>tools\/cmd\/build-patterns<\/code>) into this plugin's own runtime format.<\/p>\n\n<p>This plugin makes no outbound HTTP requests and does not integrate with any third-party or external service. Earlier versions of the bundled pattern data included a per-signature <code>producer<\/code> field (an attribution URL crediting the upstream source of each entry); it has been removed entirely, since it was never fetched or otherwise dereferenced by the plugin at runtime.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin zip via Plugins &gt; Add New &gt; Upload Plugin, or extract it into <code>wp-content\/plugins\/psb-bot-traffic-controller\/<\/code>.<\/li>\n<li>Activate the plugin. This installs a small companion file into <code>wp-content\/mu-plugins\/<\/code> (WordPress's \"must-use plugin\" mechanism) that performs early bot interception.<\/li>\n<li>Configure per-category policy under Settings &gt; PSB Bot Traffic Controller.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20replace%20a%20waf%20or%20cdn-level%20bot%20protection%3F\"><h3>Does this replace a WAF or CDN-level bot protection?<\/h3><\/dt>\n<dd><p>No \u2014 PSB Bot Traffic Controller runs inside WordPress (or, for the mu-plugin path, as early in WordPress's own bootstrap as possible) and cannot intercept traffic before it reaches your web server. It complements, rather than replaces, edge-level protection.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20the%20mu-plugin%20file%20can%27t%20be%20written%20on%20activation%3F\"><h3>What happens if the mu-plugin file can't be written on activation?<\/h3><\/dt>\n<dd><p>An admin notice is shown with the filesystem error. The plugin still activates, but early interception won't run until the mu-plugin is successfully installed (retry by deactivating\/reactivating, or check file permissions on <code>wp-content\/mu-plugins\/<\/code>).<\/p><\/dd>\n<dt id=\"how%20do%20i%20reset%20rate-limit%20counters%20while%20testing%3F\"><h3>How do I reset rate-limit counters while testing?<\/h3><\/dt>\n<dd><p>Add <code>define( 'PSB_BTC_DEBUG', true );<\/code> to <code>wp-config.php<\/code> on a staging\/test site. This enables a rotating-token reset URL shown on the Dashboard (valid for a few hours at a time, then automatically expires). It is disabled by default and should never be enabled on a production site.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Implemented robots.txt generation, previously advertised in this plugin's description but not\nactually built: categories set to \"block\" now get a <code>User-agent<\/code>\/<code>Disallow: \/<\/code> block in\nrobots.txt, sourced from the same curated bot-name data the detector already uses. Default\nmode is dynamic (WordPress's <code>robots_txt<\/code> filter, nothing written to disk); an advanced,\nopt-in setting writes a physical robots.txt file instead, with a persistent warning (Settings\npage and Dashboard) whenever a physical file exists on disk, since it always takes precedence\nover the dynamic filter regardless of which mode is configured.<\/li>\n<li>Fixed readme.txt's Description\/Installation\/Credits sections rendering with broken mid-sentence\nline breaks on the WP.org plugin page. The source file had each paragraph hard-wrapped across\nmultiple lines for editor readability, but WP.org's readme parser treats every newline as a\nliteral line break rather than reflowing prose \u2014 unlike Markdown, a single <code>\\n<\/code> does not get\ncollapsed into a space. Fixed by joining each paragraph onto one logical line. Docs-only, no\ncode changes.<\/li>\n<li>robots.txt generation now detects other active SEO plugins (AIOSEO, Yoast, Rank Math,\nSEOPress, The SEO Framework) that commonly also manage robots.txt, and shows an informational\nnotice on the Settings and Dashboard pages when one is found \u2014 WordPress's <code>robots_txt<\/code> filter\nis designed for multiple plugins to extend the same output, so this doesn't change behavior by\ndefault, it just surfaces the interaction. Our filter callback also now registers at a later\npriority to reduce the chance of being silently overwritten by another plugin's callback.<\/li>\n<li>Added a master on\/off switch for robots.txt generation on the Settings page (on by default),\nindependent of the existing physical-file advanced-mode toggle \u2014 when off, this plugin never\nmodifies robots.txt at all, regardless of category policy. Turning it off also removes a\nphysical robots.txt file this plugin had previously written, so a stale Disallow list doesn't\nlinger on disk after the feature is disabled.<\/li>\n<li>Added a help icon with a plain-language explanation to each bot category on the Settings page\n(hover for a description of what that category actually covers), aimed at non-technical site\nowners deciding how to configure policy.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Removed a stale description line claiming an \"optional WURFL.js-based detection engine can\nbe enabled for higher accuracy\" \u2014 no such engine exists yet (see <code>CLAUDE.md<\/code>'s architecture\nnotes: WURFL.js is planned, not implemented). Docs-only correction, no code changes.<\/li>\n<li>Added a global average bot request rate (requests\/minute) to the Dashboard's Recent Activity\nsection, computed from the same 24-hour activity summary already used for the per-category\nblock\/limit\/allow table \u2014 no new data collection, just a new aggregate view over existing\ncounters.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Re-analyzed every remaining automated Plugin Check finding against this version's actual\ncode, rather than restating prior documentation, and applied two genuine fixes found in the\nprocess (both comment-only, zero behavior change):<\/li>\n<li>Added <code>phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared<\/code> annotations to the\nthree interpolated-table-name queries in <code>class-ratelimit-store-db.php<\/code>\n(<code>increment_and_get()<\/code>, <code>get_count()<\/code>, <code>cleanup_expired()<\/code>) \u2014 this file had none, unlike its\nsibling <code>class-ratelimit-reset.php<\/code>, despite sharing the identical unfixable-by-design\npattern (table names can never be <code>%s<\/code>\/<code>%d<\/code> <code>$wpdb-&gt;prepare()<\/code> placeholders).<\/li>\n<li>Corrected <code>class-ratelimit-reset.php<\/code>'s existing <code>phpcs:ignore<\/code> comment on its\n  TRUNCATE TABLE query: it previously named the sniff <code>WordPress.DB.PreparedSQL.NotPrepared<\/code>,\nwhich is not what actually fires there \u2014 fixed to name\n  WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,\nmatching what the checker actually reports, so the comment provides real suppression instead\nof none.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Regenerated the icon\/banner\/logo image assets from an updated source design (ring emblem +\n\"BOT TRAFFIC CONTROLLER\" wordmark), replacing the placeholder artwork shipped in 1.0.0.<\/li>\n<li>Sanitize <code>psb_btc_mu_respond()<\/code>'s output (strip control characters before echoing) rather\nthan relying on every current call site happening to pass a hardcoded literal \u2014 all 3 call\nsites still do today, so this closes a latent gap rather than fixing a live exploit, but\nescaping at the point of output shouldn't depend on that staying true as the file is edited.<\/li>\n<li>Re-reviewed the remaining automated Plugin Check findings against this version and confirmed\nthey remain the already-documented false positives \/ accepted architectural exceptions\ncovered in <code>SUBMISSION.md<\/code> (non-atomic-counter <code>flock()<\/code> locking has no <code>WP_Filesystem<\/code>\nequivalent and must run pre-bootstrap; <code>$wpdb-&gt;prepare()<\/code> placeholders the checker's static\nanalysis can't trace through) \u2014 nothing else new to fix.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Renamed the plugin from \"botpolice\" to \"PSB Bot Traffic Controller\" (slug\n  psb-bot-traffic-controller), following a WordPress.org plugin review finding that\n\"botpolice\" too closely matched an existing project with overlapping functionality.\nEvery internal class\/function\/constant prefix, option\/table\/transient name, and\ngenerated mu-plugin filename was renamed to match (<code>Botpolice_*<\/code> \u2192 <code>PSB_BTC_*<\/code>,\n  botpolice_* \u2192 <code>psb_btc_*<\/code>, <code>BOTPOLICE_*<\/code> \u2192 <code>PSB_BTC_*<\/code>).<\/li>\n<li>Removed the <code>producer<\/code> attribution field from the bundled bot-pattern data\n(<code>includes\/detection\/regex\/data\/bot-patterns.php<\/code>) \u2014 it held a URL in most entries and\nwas flagged twice by review under two headings (\"calling files remotely\" and\n\"undocumented use of a 3rd party\/external service\"), even though it was never\ndereferenced as a URL anywhere in the runtime code. Removed entirely rather than\ndisclosed, since the plugin makes no outbound HTTP requests and integrates with no\nexternal service.<\/li>\n<li>Changed the mu-plugin's per-category config cache from an executable\n  &lt;?php return array(...); file to plain JSON (now <code>psb-btc-config-cache.json<\/code>,\nread via <code>json_decode()<\/code>), per a review finding against writing executable code\nfiles into <code>wp-content\/mu-plugins\/<\/code> outside the small set of core-defined drop-in\nexceptions. The mu-plugin bootstrap file itself and the (much larger) pattern-data\ncache were not affected \u2014 review did not flag either of those.<\/li>\n<li>Fixed a real correctness bug review's automated tooling also caught: both the\nobject-cache-backed rate-limit store and the mu-plugin's flat-file rate-limit\ncounters used a non-atomic read-increment-write sequence, letting concurrent\nrequests undercount and bypass configured limits. The object-cache store now uses\n  wp_cache_add()\/<code>wp_cache_incr()<\/code> (atomic at the cache backend); the mu-plugin's\nflat-file counters now use <code>flock()<\/code>-based exclusive locking around the\nread-modify-write.<\/li>\n<li>Replaced the compound <code>if ( ! defined('ABSPATH') &amp;&amp; ! defined('...STANDALONE') )<\/code>\ndirect-access guard (used to allow PHPUnit to load classes without a full WP\ninstall) with the literal bare <code>if ( ! defined( 'ABSPATH' ) ) { exit; }<\/code> review\nexpects, across all affected files. Test-only standalone loading now works by\nhaving the PHPUnit bootstrap itself define <code>ABSPATH<\/code>, rather than each production\nfile special-casing a second constant.<\/li>\n<\/ul>\n\n<h4>0.4.1<\/h4>\n\n<ul>\n<li>Address findings from the official WordPress Plugin Check tool: escape remaining\nunescaped Dashboard output, switch <code>class-mu-installer.php<\/code>'s version-stamp read and\n  class-ratelimit-reset.php's counter-file cleanup off raw file-handle\/<code>unlink()<\/code> calls,\ndocument the intentional nonce-free design of the (opt-in, off-by-default) debug reset\nendpoint, add a direct-access guard to the generated <code>bot-patterns.php<\/code> (via\n  tools\/cmd\/build-patterns, not by hand-editing the output), and correct \"Tested up to\"\nto the current WordPress release. See <code>SUBMISSION.md<\/code> for a list of remaining Plugin\nCheck findings that are intentional design choices rather than defects (the\n  BOTPOLICE_STANDALONE direct-access guard variant, the mu-plugin's necessarily-early\nfilesystem calls, and the reset endpoint's token-based-not-nonce-based auth).<\/li>\n<\/ul>\n\n<h4>0.4.0<\/h4>\n\n<ul>\n<li>The rate-limit-reset debug endpoint (<code>?botpolice_reset=&lt;token&gt;<\/code>) is no longer loaded or\nregistered by default \u2014 it now requires <code>define( 'BOTPOLICE_DEBUG', true );<\/code> in\n  wp-config.php to become reachable at all, rather than being an unauthenticated endpoint\npresent on every install. <code>uninstall.php<\/code> also now removes its secret option, if present.<\/li>\n<li>Prepare for WordPress.org Plugin Directory submission: tightened the short description\nto fit the directory's display limit, corrected the \"Tested up to\" header (was a\nnonexistent \"7.0\"), added a Screenshots section covering the Settings and Dashboard\nadmin pages, added a Credits section disclosing and licensing the bundled bot-pattern\ndata's third-party sources, and fixed composer.json declaring \"proprietary\" while the\nplugin itself is GPLv2 or later.<\/li>\n<\/ul>\n\n<h4>0.3.2<\/h4>\n\n<ul>\n<li>Fix: the debug rate-limit-reset token (<code>?botpolice_reset=&lt;token&gt;<\/code>, shown on the\nDashboard for manual testing) was a single fixed value stored in <code>wp_options<\/code> \u2014\nonce leaked (screenshot, shared log, browser history), it stayed valid forever.\nIt now rotates automatically every 4 hours: the displayed\/accepted token is an\nHMAC-SHA256 of a persistent random secret (never displayed) and the current\n4-hour time window, so a leaked token expires on its own without any manual\nrotation step, and reading the plugin's source alone is not enough to compute a\nvalid token (the algorithm is public; the secret it's keyed on is not). The\nDashboard now also shows how many minutes remain before the current URL expires.<\/li>\n<\/ul>\n\n<h4>0.3.1<\/h4>\n\n<ul>\n<li>Add the product logo\/emblem to the Settings and Dashboard admin page headers\n(<code>assets\/images\/logo.png<\/code>, shipped in the plugin zip) and WordPress.org-listing icon\nand banner assets (<code>.wordpress-org\/<\/code>, not bundled into the plugin zip per WP.org\nconvention \u2014 those live in the SVN assets\/ directory of the plugin listing, separate\nfrom trunk\/).<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Fix: rate-limiting and category policy keyed on the raw TCP peer address\n(<code>REMOTE_ADDR<\/code>), which is the reverse proxy's\/load balancer's own IP on any site\nrunning behind one (Cloudflare, nginx, AWS ALB\/ELB) \u2014 collapsing every distinct\nvisitor onto one shared rate-limit bucket. Added <code>Botpolice_Client_Ip<\/code>\n(<code>includes\/detection\/class-client-ip.php<\/code>, documented in <code>CLIENT_IP.md<\/code>), which\nresolves the real public client IP by walking the <code>Forwarded<\/code> (RFC 7239) and\n  X-Forwarded-For headers before falling back to the raw peer address, and only\never returns an internet-routable address for rate-limiting purposes. Wired into\nboth enforcement layers: <code>Botpolice_Request_Context<\/code> (full-WP path) and a\nduplicated <code>botpolice_mu_client_ip()<\/code> in the self-contained mu-plugin hot path.<\/li>\n<\/ul>\n\n<h4>0.2.1<\/h4>\n\n<ul>\n<li>Fix: mu-plugin bootstrap and its pattern-data cache could go stale after an upload-based\nplugin upgrade (WordPress does not re-fire the activation hook on that path), silently\nrunning old enforcement logic indefinitely. Added <code>Botpolice_Upgrade_Handler<\/code>, which resyncs\nthe mu-plugin on <code>upgrader_process_complete<\/code> and via a cheap staleness check on every\nadmin page load, covering upgrade paths that bypass WordPress's upgrader (e.g. manual FTP\nreplacement) as well.<\/li>\n<\/ul>\n\n<h4>0.2.0<\/h4>\n\n<ul>\n<li>Add per-category, per-action activity recording (<code>Botpolice_Activity_Recorder<\/code> and the\nmu-plugin's own inline counter), surfaced on the dashboard's Recent Activity view.<\/li>\n<li>Add a token-gated debug endpoint (<code>?botpolice_reset=&lt;token&gt;<\/code>) to clear rate-limit counters\nfor iterative live-site testing without waiting out hourly buckets.<\/li>\n<li>Add a curl-based live end-to-end test suite (<code>tests\/live\/test-live-site.sh<\/code>,\n  make test-live) verifying block\/limit\/allow enforcement against a real deployed site.<\/li>\n<\/ul>\n\n<h4>0.1.1<\/h4>\n\n<ul>\n<li>Fix: mu-plugin helper functions were declared conditionally (inside <code>function_exists()<\/code>\nguards) but called from a closure defined before those declarations executed \u2014 PHP only\nhoists unconditional top-level function declarations, so any request matching a bot pattern\nhit a fatal \"call to undefined function\" error, blocking all traffic on the plugin's\nhighest-risk hot path. Passed a plain <code>php -l<\/code> lint check without complaint. Caught by a new\nsubprocess-based regression suite (<code>tests\/php\/mu-bootstrap\/<\/code>), since PHPUnit cannot safely\nintercept <code>exit()<\/code>-calling code in-process.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial development release: regex\/User-Agent detection engine (911 pattern entries across\n5 categories, built from matomo-org\/device-detector, crawler-user-agents, and ai.robots.txt),\npolicy engine (block\/limit\/allow per category plus a global rate cap), rate limiter\n(transients or DB-table-backed), mu-plugin-based early interception with a\nWP-API-backed fallback layer, Settings\/Dashboard admin UI, and installable-zip packaging.<\/li>\n<\/ul>","raw_excerpt":"Block, limit, or allow AI crawlers, search bots, and scrapers by category \u2014 with rate limiting and robots.txt generation.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/358827","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=358827"}],"author":[{"embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/paulborile"}],"wp:attachment":[{"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=358827"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=358827"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=358827"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=358827"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=358827"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/gax.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=358827"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}