{"id":383255,"date":"2026-10-13T17:07:21","date_gmt":"2026-10-13T17:07:21","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/external-api-page-content\/"},"modified":"2026-10-13T17:06:46","modified_gmt":"2026-10-13T17:06:46","slug":"hivekit-external-api-page-content","status":"publish","type":"plugin","link":"https:\/\/ms.wordpress.org\/plugins\/hivekit-external-api-page-content\/","author":389188,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.3.0","stable_tag":"1.3.0","tested":"7.1.3","requires":"6.8","requires_php":"8.3","requires_plugins":null,"header_name":"HiveKit External API Page Content","header_author":"Corrado Fiore","header_description":"Replaces the body of configured WordPress pages with raw HTML or Markdown retrieved from external HTTP APIs.","assets_banners_color":"75aedd","last_updated":"2026-10-13 17:06:46","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/corradofiore\/wp-external-api-page-content","header_author_uri":"https:\/\/corradofiore.it","rating":0,"author_block_rating":0,"active_installs":0,"downloads":56,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.3.0":{"tag":"1.3.0","author":"corradofiore","date":"2026-10-13 17:06:46","revision":3744499}},"upgrade_notice":{"1.3.0":"<p>Adds per-mapping HTTP request headers. Existing 1.1 and 1.2 settings are migrated automatically.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3744499,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3744499,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3744499,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3744499,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.3.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3744499,"resolution":"1","location":"assets","locale":"","width":1112,"height":967}},"screenshots":{"1":"The settings screen. Every mapping is configured independently: label, WordPress page, API endpoint, payload format, cache lifetime and request headers."}},"plugin_section":[],"plugin_tags":[1556,529,246,4608,14512],"plugin_category":[],"plugin_contributors":[286521],"plugin_business_model":[],"class_list":["post-383255","plugin","type-plugin","status-publish","hentry","plugin_tags-api","plugin_tags-content","plugin_tags-html","plugin_tags-markdown","plugin_tags-remote","plugin_contributors-corradofiore","plugin_committers-corradofiore"],"banners":{"banner":"https:\/\/ps.w.org\/hivekit-external-api-page-content\/assets\/banner-772x250.png?rev=3744499","banner_2x":"https:\/\/ps.w.org\/hivekit-external-api-page-content\/assets\/banner-1544x500.png?rev=3744499","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/hivekit-external-api-page-content\/assets\/icon-128x128.png?rev=3744499","icon_2x":"https:\/\/ps.w.org\/hivekit-external-api-page-content\/assets\/icon-256x256.png?rev=3744499","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/hivekit-external-api-page-content\/assets\/screenshot-1.png?rev=3744499","caption":"The settings screen. Every mapping is configured independently: label, WordPress page, API endpoint, payload format, cache lifetime and request headers."}],"raw_content":"<!--section=description-->\n<p>External API Page Content lets you create any number of configurable page-to-API mappings. No page purpose or endpoint is hard-coded.<\/p>\n\n<p>Each mapping contains:<\/p>\n\n<ul>\n<li>an optional administrative label<\/li>\n<li>a WordPress page<\/li>\n<li>an external API URL<\/li>\n<li>payload format: auto-detect, HTML, or Markdown<\/li>\n<li>its own cache TTL<\/li>\n<li>optional HTTP request headers, entered one per line<\/li>\n<li>enabled\/disabled status<\/li>\n<\/ul>\n\n<p>The endpoint must return the document directly in the HTTP response body. JSON\/XML wrappers are not expected.<\/p>\n\n<p>Features:<\/p>\n\n<ul>\n<li>Repeatable mappings: add or remove as many mappings as needed.<\/li>\n<li>Uses the WordPress HTTP API (<code>wp_safe_remote_get<\/code>).<\/li>\n<li>Auto-detects HTML vs Markdown, or lets you force the format per mapping.<\/li>\n<li>Sanitizes output before rendering.<\/li>\n<li>Caches successful responses with a configurable TTL per mapping.<\/li>\n<li>Keeps the last successful API response per mapping as an outage fallback.<\/li>\n<li>Falls back to the original WordPress editor content if the API has never succeeded.<\/li>\n<li>Does not read PHP constants and does not store secrets, so it cannot be used to send <code>wp-config.php<\/code> values such as database credentials to a remote server.<\/li>\n<li>Includes a per-mapping Test API button that shows the raw response payload, HTTP status, content type, size, and detected format without changing the cache.<\/li>\n<li>Supports per-mapping request headers using a simple <code>Header-Name: value<\/code> textarea.<\/li>\n<li>Offers filters for advanced request\/header customization.<\/li>\n<\/ul>\n\n<h3>Upgrading from 1.0<\/h3>\n\n<p>Version 1.1 automatically converts the old fixed-slot settings into ordinary mappings. After migration there is no special handling for any page purpose.<\/p>\n\n<p>Version 1.3.0 removed the per-mapping token constant setting. The plugin no longer reads any PHP constant, so an administrator account cannot use it to send <code>wp-config.php<\/code> values such as database credentials to a remote URL. If you relied on that setting, add the header with the <code>hivekit_eapc_request_args<\/code> filter (see Authentication below), or type it into the mapping's Request headers field.<\/p>\n\n<h3>Request headers<\/h3>\n\n<p>Each mapping has a Request headers textarea. Enter one header per line:<\/p>\n\n<pre><code>X-API-Key: abc123\nX-Tenant: customer-a\nAccept-Language: en\n<\/code><\/pre>\n\n<p>Header names are validated and control characters are removed from values. Malformed lines are ignored. Explicit mapping headers take precedence over the plugin's default headers when the same header name is supplied.<\/p>\n\n<p>Header values entered here are stored in the WordPress options database, so anyone who can read the database can read them. For secrets, the filter shown under Authentication is preferable because it keeps the value in your own code.<\/p>\n\n<p>The Test API button uses the request headers currently entered in the mapping, including unsaved changes.<\/p>\n\n<h3>Authentication<\/h3>\n\n<p>The plugin does not read PHP constants and does not store secrets. It therefore cannot be used to send a value from <code>wp-config.php<\/code>, such as <code>DB_PASSWORD<\/code> or <code>AUTH_KEY<\/code>, to a remote server.<\/p>\n\n<p>Add an Authorization header from your own code with the <code>hivekit_eapc_request_args<\/code> filter, from a small site-specific plugin or your theme:<\/p>\n\n<pre><code>add_filter( 'hivekit_eapc_request_args', function( $args ) {\n    $args['headers']['Authorization'] = 'Bearer ' . MY_CONTENT_API_TOKEN;\n    return $args;\n} );\n<\/code><\/pre>\n\n<p>The filter receives <code>( $request_args, $url, $page_id, $mapping_id, $mapping )<\/code>, so a token can be limited to one mapping:<\/p>\n\n<pre><code>add_filter( 'hivekit_eapc_request_args', function( $args, $url, $page_id, $mapping_id, $mapping ) {\n    if ( 'm2' === $mapping_id ) {\n        $args['headers']['Authorization'] = 'Bearer ' . MY_LEGAL_API_TOKEN;\n    }\n    return $args;\n}, 10, 5 );\n<\/code><\/pre>\n\n<p>Any other header works the same way:<\/p>\n\n<pre><code>add_filter( 'hivekit_eapc_request_args', function( $args, $url, $page_id, $mapping_id, $mapping ) {\n    if ( 'Policy API' === $mapping['label'] ) {\n        $args['headers']['X-API-Key'] = 'replace-me';\n    }\n    return $args;\n}, 10, 5 );\n<\/code><\/pre>\n\n<p>The Test API button runs the same filter, so a test sends exactly the headers a real fetch would.<\/p>\n\n<p>Cached responses are not keyed by filtered arguments, so rotating a token does not by itself refresh cached content. Clear the cache after changing a token.<\/p>\n\n<h3>Security<\/h3>\n\n<ul>\n<li>URLs are restricted to public HTTP\/HTTPS endpoints and fetched with <code>wp_safe_remote_get()<\/code>.<\/li>\n<li>HTML responses are sanitized using WordPress KSES rules for post content.<\/li>\n<li>Markdown is converted to HTML and sanitized before display.<\/li>\n<li>Script tags and other unsafe HTML are removed by default.<\/li>\n<li>The plugin never reads PHP constants, so it cannot expose <code>wp-config.php<\/code> secrets such as database credentials or authentication salts.<\/li>\n<li>Secrets are not written to the plugin settings. Values typed into a mapping's Request headers field are stored in the site options table.<\/li>\n<\/ul>\n\n<p>If you intentionally need additional HTML tags\/attributes, use the <code>hivekit_eapc_allowed_html<\/code> filter.<\/p>\n\n<h3>Markdown support<\/h3>\n\n<p>The bundled dependency-free Markdown renderer is intentionally small and supports the common constructs typically used in content\/legal documents:<\/p>\n\n<ul>\n<li>headings<\/li>\n<li>paragraphs<\/li>\n<li>blockquotes<\/li>\n<li>horizontal rules<\/li>\n<li>fenced code blocks<\/li>\n<li>ordered and unordered lists<\/li>\n<li>links<\/li>\n<li>inline code<\/li>\n<li>bold, emphasis, and strikethrough<\/li>\n<\/ul>\n\n<p>It is not a full CommonMark implementation. If your source documents depend on advanced Markdown features such as tables, nested lists, footnotes, or reference-style links, replace\/extend the renderer through the <code>hivekit_eapc_markdown_html<\/code> filter or integrate a full Markdown parser.<\/p>\n\n<h3>Filters<\/h3>\n\n<pre><code>hivekit_eapc_should_replace_content\n<\/code><\/pre>\n\n<p>Controls whether content replacement occurs. Receives the page ID and full mapping.<\/p>\n\n<pre><code>hivekit_eapc_request_args\n<\/code><\/pre>\n\n<p>Changes WordPress HTTP request arguments before the API request. Receives URL, page ID, stable mapping ID, and full mapping.<\/p>\n\n<pre><code>hivekit_eapc_allowed_html\n<\/code><\/pre>\n\n<p>Changes the WordPress KSES allowlist for HTML responses.<\/p>\n\n<pre><code>hivekit_eapc_markdown_html\n<\/code><\/pre>\n\n<p>Changes rendered\/sanitized Markdown HTML.<\/p>\n\n<pre><code>hivekit_eapc_rendered_content\n<\/code><\/pre>\n\n<p>Changes the final HTML before caching\/output. Receives the stable mapping ID and full mapping.<\/p>\n\n<h3>External requests and privacy<\/h3>\n\n<p>This plugin does not send any data to the plugin author or to WordPress.org.<\/p>\n\n<p>For every enabled mapping, the plugin performs a server-side HTTP request to the exact URL entered on the settings screen and replaces the body of the mapped page with the response. You choose those endpoints, so review the privacy policy and terms of use of each API you connect before enabling a mapping.<\/p>\n\n<p>Requests use the WordPress HTTP API (<code>wp_safe_remote_get<\/code>) and accept only public HTTP\/HTTPS URLs. Redirects are limited to 3, the response body is capped at 2 MB and the request times out after 8 seconds.<\/p>\n\n<h3>Source code and support<\/h3>\n\n<p>Development happens at https:\/\/github.com\/corradofiore\/wp-external-api-page-content<\/p>\n\n<p>Bug reports and feature requests are welcome there. The distributed plugin contains the same unminified source as the repository, so no build step is required to review the code.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload and activate the plugin.<\/li>\n<li>Go to External API Content in the admin menu.<\/li>\n<li>Click Add mapping.<\/li>\n<li>Select a WordPress page and enter its API URL.<\/li>\n<li>Choose Auto-detect, HTML, or Markdown.<\/li>\n<li>Optionally add request headers, one per line in <code>Header-Name: value<\/code> format.<\/li>\n<li>Set the mapping's cache TTL.<\/li>\n<li>Optionally click Test API to inspect the endpoint response before saving.<\/li>\n<li>Save settings.<\/li>\n<\/ol>\n\n<p>Repeat for as many pages as required.<\/p>\n\n<p>The original content stored in each WordPress page is not overwritten. It acts as an emergency fallback.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"what%20happens%20when%20the%20external%20api%20is%20unavailable%3F\"><h3>What happens when the external API is unavailable?<\/h3><\/dt>\n<dd><p>The last successful response for that mapping is served instead. If the API has never returned a usable response, the normal WordPress page content is shown unchanged.<\/p><\/dd>\n<dt id=\"how%20do%20i%20authenticate%20to%20the%20api%3F\"><h3>How do I authenticate to the API?<\/h3><\/dt>\n<dd><p>Add an <code>Authorization<\/code> header with the <code>hivekit_eapc_request_args<\/code> filter, so the token stays in your own code and never reaches the database. If you instead type a header into a mapping's Request headers field, its value is stored in the site options table. The plugin never reads PHP constants, so it cannot expose <code>wp-config.php<\/code> secrets.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20send%20data%20to%20the%20plugin%20author%3F\"><h3>Does this plugin send data to the plugin author?<\/h3><\/dt>\n<dd><p>No. Requests go only to the API URLs you configure yourself.<\/p><\/dd>\n<dt id=\"can%20several%20pages%20use%20the%20same%20api%3F\"><h3>Can several pages use the same API?<\/h3><\/dt>\n<dd><p>Yes. Every mapping is independent and keeps its own cache TTL, request headers and payload format.<\/p><\/dd>\n<dt id=\"which%20markdown%20features%20are%20supported%3F\"><h3>Which Markdown features are supported?<\/h3><\/dt>\n<dd><p>Headings, paragraphs, blockquotes, horizontal rules, fenced code blocks, ordered and unordered lists, links, inline code, bold, emphasis and strikethrough. Tables and nested lists are not supported; see the Markdown support section above.<\/p><\/dd>\n<dt id=\"does%20it%20work%20on%20multisite%3F\"><h3>Does it work on multisite?<\/h3><\/dt>\n<dd><p>Settings are stored per site. After a network activation, configure each site through the External API Content screen.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>Renamed the plugin to HiveKit External API Page Content. Every option, hook, filter and constant now uses the <code>hivekit_eapc_<\/code> prefix, and settings saved by earlier versions are migrated automatically.<\/li>\n<li>Removed the per-mapping Bearer token constant setting. The plugin no longer reads any PHP constant, so an administrator cannot use it to send <code>wp-config.php<\/code> values such as database credentials to a remote URL. Add an Authorization header with the <code>hivekit_eapc_request_args<\/code> filter instead.<\/li>\n<li>Added per-mapping HTTP request headers textarea.<\/li>\n<li>Headers use one <code>Header-Name: value<\/code> entry per line and apply to both live fetches and Test API requests.<\/li>\n<li>Explicit mapping headers override the plugin's default headers when names conflict.<\/li>\n<li>Header changes are included in cache\/fallback identity to avoid reusing content from a different request context.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>Added a per-mapping Test API button to inspect the raw API payload.<\/li>\n<li>Test results include HTTP status, Content-Type, response size, and resolved HTML\/Markdown format.<\/li>\n<li>Tests use the current unsaved mapping values and do not populate or alter the normal content cache.<\/li>\n<li>Non-2xx responses still display their returned payload for diagnostics.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Replaced the fixed page slots with an arbitrary repeatable mapping list.<\/li>\n<li>Added per-mapping labels, cache TTL, enabled status, and configurable Bearer-token constant names.<\/li>\n<li>Generalized cache\/fallback storage and filter context around stable mapping IDs.<\/li>\n<li>Added automatic migration from version 1.0 settings.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"Replace the content body of configured WordPress pages with raw HTML or Markdown fetched from external HTTP APIs.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/383255","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=383255"}],"author":[{"embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/corradofiore"}],"wp:attachment":[{"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=383255"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=383255"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=383255"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=383255"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=383255"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ms.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=383255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}