{"id":371036,"date":"2026-09-18T16:04:17","date_gmt":"2026-09-18T16:04:17","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/shootcal-social-feed\/"},"modified":"2026-09-19T01:17:16","modified_gmt":"2026-09-19T01:17:16","slug":"shootcal-social-feed","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/shootcal-social-feed\/","author":6600172,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.4.1","stable_tag":"0.4.1","tested":"7.1.1","requires":"6.4","requires_php":"8.0","requires_plugins":null,"header_name":"ShootCal Social Feed","header_author":"ShootCal","header_description":"Display a lightweight, cached Instagram Business or Creator feed with exact caption hashtag filtering.","assets_banners_color":"","last_updated":"2026-09-19 01:17:16","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/www.shootcal.com\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":70,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.4.0":{"tag":"0.4.0","author":"rsmith4321","date":"2026-09-18 16:03:57","revision":3702312},"0.4.1":{"tag":"0.4.1","author":"rsmith4321","date":"2026-09-19 01:17:16","revision":3702898}},"upgrade_notice":[],"ratings":[],"assets_icons":[],"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.4.0","0.4.1"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[252,210,6179,311,154],"plugin_category":[50,56],"plugin_contributors":[265481],"plugin_business_model":[],"class_list":["post-371036","plugin","type-plugin","status-publish","hentry","plugin_tags-feed","plugin_tags-gallery","plugin_tags-hashtag","plugin_tags-instagram","plugin_tags-social-media","plugin_category-media","plugin_category-social-and-sharing","plugin_contributors-rsmith4321","plugin_committers-rsmith4321"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/s.w.org\/plugins\/geopattern-icon\/shootcal-social-feed.svg","icon_2x":false,"generated":true},"screenshots":[],"raw_content":"<!--section=description-->\n<p>ShootCal Social Feed displays recent posts from one connected Instagram Business or Creator account on your WordPress website. Connect with Facebook, choose your linked account, and add a shortcode. The plugin refreshes posts on a schedule and serves visitors the last successful WordPress cache without visitor-triggered Instagram API requests.<\/p>\n\n<p><a href=\"https:\/\/www.shootcal.com\/\">ShootCal<\/a> also provides website building, scheduling, booking, contracts, invoices, client galleries, and a print store for photographers.<\/p>\n\n<p>Features:<\/p>\n\n<ul>\n<li>Feed assets load only on pages containing a feed.<\/li>\n<li>Responsive image grid with no jQuery or heavy libraries. Static embeds need no JavaScript; dynamic embeds use one small deferred script.<\/li>\n<li>Images, video thumbnails, Reels, and carousel cover images.<\/li>\n<li>Exact, case-insensitive caption hashtag filtering with a single tag, any-of lists, and optional exclusions.<\/li>\n<li>Saved feeds: name a filter set once in ShootCal Apps &gt; Social Feed, paste <code>[shootcal_instagram_feed feed=\"1\"]<\/code> anywhere, and later edits apply everywhere.<\/li>\n<li>Shortcode-specific hashtags and display limits.<\/li>\n<li>Optional five-desktop\/four-mobile layout and account follow button.<\/li>\n<li>Optional post-load refresh that bypasses full-page caches while reading only WordPress's saved feed.<\/li>\n<li>Scheduled cache refresh with a last-known-good fallback.<\/li>\n<li>One-click Facebook authorization through ShootCal, plus manual-token fallback.<\/li>\n<li>Manual refresh and connection status in ShootCal Apps &gt; Social Feed.<\/li>\n<li>Preview saved Smash Balloon feed imports, map existing shortcode IDs, and explicitly switch with an undo option.<\/li>\n<\/ul>\n\n<p>Requirements: a WordPress site using HTTPS and an Instagram Business or Creator account linked to a Facebook Page that you can manage. The Connect with Facebook flow uses ShootCal's Meta app, so you do not need to create your own developer app. Advanced users can enter their own numeric Instagram account ID and Page access token instead. The plugin uses the official Instagram API with Facebook Login. It does not scrape Instagram or support Personal accounts.<\/p>\n\n<h3>External services<\/h3>\n\n<p>The plugin contacts ShootCal only when an administrator starts or completes Connect with Facebook. During one-click connection, the plugin sends this site's WordPress admin callback URL, WordPress Address, plugin version, and a one-time cryptographic challenge to the ShootCal OAuth broker at <code>api.shootcal.com<\/code>. ShootCal redirects the administrator to Meta, temporarily handles the resulting Page-token candidate, and releases it only to this WordPress server after the server proves possession of the one-time verifier. Tokens are never placed in browser URLs. The broker attempt expires after ten minutes.<\/p>\n\n<p>After connection, this plugin connects to Meta's Graph API at <code>graph.facebook.com<\/code> during a scheduled or administrator-requested refresh. It sends the configured Instagram business account ID and Page access token to request the account username, captions, media type, media URLs, post links, timestamps, and carousel cover data. The token is decrypted only for these server-to-server requests and is never included in front-end HTML.<\/p>\n\n<p>Feed images are served from the remote Meta\/Facebook CDN URLs returned by the API. A visitor's browser therefore connects directly to Meta to load each visible image, which can disclose ordinary request information such as the visitor's IP address and browser user agent to Meta. The plugin applies a no-referrer policy to image requests.<\/p>\n\n<p>Service terms and privacy policies:<\/p>\n\n<ul>\n<li>ShootCal Terms of Service: https:\/\/shootcal.com\/terms\/<\/li>\n<li>ShootCal Privacy Policy: https:\/\/shootcal.com\/privacy\/<\/li>\n<li>ShootCal data-deletion instructions: https:\/\/shootcal.com\/data-deletion\/<\/li>\n<li>Meta Platform Terms: https:\/\/developers.facebook.com\/terms\/<\/li>\n<li>Meta Privacy Policy: https:\/\/www.facebook.com\/privacy\/policy\/<\/li>\n<\/ul>\n\n<h3>Support<\/h3>\n\n<p>For help with connection or feed display, contact support@shootcal.com. Include your WordPress and plugin versions and a description of the issue. Never send your access token or Facebook password.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>shootcal-social-feed<\/code> directory to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate ShootCal Social Feed.<\/li>\n<li>Open ShootCal Apps &gt; Social Feed.<\/li>\n<li>Choose Connect with Facebook and select the linked professional Instagram account.<\/li>\n<li>Add <code>[shootcal_instagram_feed]<\/code> to a Shortcode block.<\/li>\n<\/ol>\n\n<p>To show only posts whose captions include a particular hashtag:<\/p>\n\n<pre><code>[shootcal_instagram_feed hashtag=\"weddings\"]\n<\/code><\/pre>\n\n<p>Comma-separated lists match any of the tags, and <code>exclude<\/code> removes posts even when they match:<\/p>\n\n<pre><code>[shootcal_instagram_feed hashtag=\"familyportraits, family\" exclude=\"wedding\"]\n<\/code><\/pre>\n\n<p>Prefer managing filters without editing pages? Create a saved feed under ShootCal Apps &gt; Social Feed and embed it by id:<\/p>\n\n<pre><code>[shootcal_instagram_feed feed=\"1\"]\n<\/code><\/pre>\n\n<p>The leading <code>#<\/code> is optional. Additional examples:<\/p>\n\n<pre><code>[shootcal_instagram_feed hashtag=\"#weddings\" limit=\"9\" columns=\"3\"]\n\n[shootcal_instagram_feed limit=\"12\" columns=\"4\"]\n<\/code><\/pre>\n\n<p>To mirror a compact social feed with five desktop tiles, four mobile tiles, and an account button:<\/p>\n\n<pre><code>[shootcal_instagram_feed hashtag=\"wedding\" limit=\"5\" columns=\"5\" mobile_limit=\"4\" follow=\"true\"]\n<\/code><\/pre>\n\n<p>If the surrounding page is held in a full-page cache, add <code>dynamic=\"true\"<\/code>. The cached page keeps a server-rendered fallback, while a small deferred script refreshes the markup after load from a public, non-stored WordPress REST response. That route reads only the last successful plugin cache and never triggers an Instagram API request:<\/p>\n\n<pre><code>[shootcal_instagram_feed hashtag=\"wedding\" limit=\"5\" columns=\"5\" mobile_limit=\"4\" follow=\"true\" dynamic=\"true\"]\n<\/code><\/pre>\n\n<!--section=faq-->\n<dl>\n<dt id=\"can%20i%20migrate%20from%20smash%20balloon%3F\"><h3>Can I migrate from Smash Balloon?<\/h3><\/dt>\n<dd><p>Yes. In ShootCal Apps &gt; Social Feed, select the Import from Smash Balloon button after connecting the same Instagram account. The separate walkthrough guides you through preparation, feed selection, previews, and switching. Saved definitions can be read while Smash Balloon is active, inactive, or removed, provided its data remains in the database. Supported single-account grid feeds can become new ShootCal presets. You can also explicitly map an old feed ID to an existing ShootCal preset, including when the old settings cannot be converted automatically.<\/p>\n\n<p>Leave Smash Balloon active during import and preview if it serves your live pages. Do not delete it to start the import: deletion can erase its data unless its Preserve settings if plugin is removed option is enabled.<\/p>\n\n<p>Importing saves presets and ID mappings only. Review the cached previews, then choose Switch shortcodes to ShootCal. If Smash Balloon is active, the form requires an explicit choice to deactivate it. The importer preserves its saved data and does not copy credentials or rewrite page content. Undo switch reactivates only the Smash Balloon plugin that this importer deactivated; imported presets remain available.<\/p>\n\n<p>After switching, mapped <code>[instagram-feed feed=\"12\"]<\/code> shortcodes and the standard Smash Balloon Instagram blocks render through ShootCal. Supported inline overrides are <code>num<\/code>, <code>cols<\/code>, <code>nummobile<\/code>, <code>showfollow<\/code>, and <code>class<\/code>. Bare legacy shortcodes without a saved feed ID and unsupported inline options require manual updates. Native Smash Balloon widgets and Elementor widgets must first be replaced with Shortcode blocks\/widgets. Network-activated Smash Balloon installations cannot be switched by this site-level importer.<\/p>\n\n<p>This is a migration aid, not full feature or visual parity. ShootCal uses one connected professional account, its own responsive grid and cached recent posts, and exact caption hashtag filters. Word\/phrase filters, public hashtag or tagged feeds, multiple-account sources, moderation, shopping, and other unsupported selection settings require an explicitly chosen replacement. Headers, captions, likes, lightboxes, Load More, and custom styles are not copied. The importer scans stored content and widgets; review any theme or external template embeds separately. Clear your page cache and verify affected pages after switching or undoing.<\/p><\/dd>\n<dt id=\"i%20use%20a%20performance%20plugin%20%28perfmatters%2C%20wp%20rocket%2C%20litespeed%20cache%2C%20autoptimize%29%20and%20the%20feed%20looks%20unstyled%20or%20images%20misbehave.\"><h3>I use a performance plugin (Perfmatters, WP Rocket, LiteSpeed Cache, Autoptimize) and the feed looks unstyled or images misbehave.<\/h3><\/dt>\n<dd><p>ShootCal Social Feed registers its own exclusions with Perfmatters and WP Rocket automatically: its stylesheet is excluded from Remove Unused CSS, and its images carry the standard <code>skip-lazy<\/code> marker that most lazy-load plugins honor. After updating this plugin, clear your optimizer's CSS cache once so it regenerates.<\/p>\n\n<p>For other optimizers, exclude these manually:<\/p>\n\n<ul>\n<li>Unused\/critical CSS removal: exclude the stylesheet path <code>\/shootcal-social-feed\/<\/code> (or safelist selectors beginning with <code>.shootcal-instagram-feed<\/code>).<\/li>\n<li>Lazy loading: exclude images with the <code>skip-lazy<\/code> class if your tool does not already honor it. The plugin times its own image loading.<\/li>\n<li>JavaScript delay\/defer tools: <code>feed.js<\/code> is small and already deferred; if your tool delays scripts until user interaction, exclude <code>shootcal-social-feed\/assets\/feed.js<\/code> so the feed can load on scroll.<\/li>\n<\/ul><\/dd>\n<dt id=\"does%20filtering%20call%20instagram%27s%20public%20hashtag%20search%20api%3F\"><h3>Does filtering call Instagram's public hashtag search API?<\/h3><\/dt>\n<dd><p>No. Filtering is performed locally against captions from the connected account's own recent posts. This keeps permissions and API usage small and predictable.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20instagram%20is%20temporarily%20unavailable%3F\"><h3>What happens if Instagram is temporarily unavailable?<\/h3><\/dt>\n<dd><p>The last successful cached metadata stays visible. The actual images remain hosted by Meta, and Meta's signed media URLs may eventually expire during a long outage or after access is revoked. An administrator can see the error and retry from ShootCal Apps &gt; Social Feed.<\/p><\/dd>\n<dt id=\"does%20a%20page%20visitor%20ever%20trigger%20a%20live%20instagram%20request%3F\"><h3>Does a page visitor ever trigger a live Instagram request?<\/h3><\/dt>\n<dd><p>No. Front-end rendering reads only the WordPress cache. Visitors' browsers still load feed images from Meta's CDN, as described under External services.<\/p><\/dd>\n<dt id=\"how%20do%20i%20delete%20the%20instagram%20connection%20data%3F\"><h3>How do I delete the Instagram connection data?<\/h3><\/dt>\n<dd><p>Open ShootCal Apps &gt; Social Feed and choose Disconnect and clear cache. This removes the encrypted token, selected Instagram account ID, OAuth state, refresh status, and cached feed from this WordPress installation. Deleting the plugin from WordPress also removes all plugin options and its scheduled refresh job. See https:\/\/shootcal.com\/data-deletion\/ for the complete instructions.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.4.1<\/h4>\n\n<ul>\n<li>Move Smash Balloon import behind a single settings-page button and a four-step guided walkthrough.<\/li>\n<li>Explain preserved shortcodes, importing retained data after removal, and deactivation at the final switch.<\/li>\n<li>Keep source feeds, previews, and switch controls off the regular settings page.<\/li>\n<\/ul>\n\n<h4>0.4.0<\/h4>\n\n<ul>\n<li>Add reviewed Smash Balloon feed import and explicit existing-feed mapping without rewriting content or copying credentials.<\/li>\n<li>Add opt-in shortcode and standard-block compatibility, guarded plugin switching, cached previews, and undo.<\/li>\n<li>Detect unsupported sources, filters, inline settings, native widgets, and Elementor widgets before switching.<\/li>\n<\/ul>\n\n<h4>0.3.9<\/h4>\n\n<ul>\n<li>Keep the multiphoto icon unchanged when hovering or focusing a feed image.<\/li>\n<li>Add a subtle cell border so white-padded Instagram photos have a clear edge.<\/li>\n<\/ul>\n\n<h4>0.3.8<\/h4>\n\n<ul>\n<li>Prepare the public plugin-directory submission with current setup instructions, account requirements, and external-service disclosures.<\/li>\n<li>Use the ShootCal Social Feed directory slug while preserving existing connections, saved feeds, shortcodes, and cache keys.<\/li>\n<\/ul>\n\n<h4>0.3.7<\/h4>\n\n<ul>\n<li>Name the slider app Photo Slider to match the ShootCal Slider admin screen.<\/li>\n<\/ul>\n\n<h4>0.3.6<\/h4>\n\n<ul>\n<li>Move ShootCal Apps below Settings in the WordPress sidebar.<\/li>\n<li>Show Natural Photo Slider in the apps overview when it is installed.<\/li>\n<\/ul>\n\n<h4>0.3.5<\/h4>\n\n<ul>\n<li>Group Social Feed and Calendar under one ShootCal sidebar menu, with an overview of the available plugins.<\/li>\n<li>Keep the existing Social Feed settings address and saved connection unchanged. Either ShootCal plugin can provide the shared menu independently.<\/li>\n<\/ul>\n\n<h4>0.3.4<\/h4>\n\n<ul>\n<li>Register exclusions with CSS optimizers automatically: the stylesheet is excluded from Perfmatters and WP Rocket unused-CSS removal, feed images carry the standard skip-lazy marker for lazy-load plugins, and the FAQ documents manual exclusions for other tools.<\/li>\n<li>Document that assets load only on pages rendering a feed; nothing is enqueued site-wide.<\/li>\n<\/ul>\n\n<h4>0.3.3<\/h4>\n\n<ul>\n<li>Drop the glass chip behind the carousel icon: the double-photo mark now sits directly on the image in a dark shade with a soft light halo, deepening slightly on hover.<\/li>\n<\/ul>\n\n<h4>0.3.2<\/h4>\n\n<ul>\n<li>Lazy-load the entire dynamic feed: nothing is fetched and no feed image loads until the feed nears the viewport, then the fresh markup and its images load together. Script-injected images are invisible to native and plugin lazy-loaders, so the script now owns image timing end to end; pages whose visitors never reach the feed no longer call the REST route at all.<\/li>\n<li>Replace the Carousel text chip with a light glass double-photo icon that stays visible and darkens slightly on hover; badges can no longer slip under the zoomed hover image.<\/li>\n<li>Move the plugin into its own top-level \"Instagram Feed\" admin menu item instead of a Settings submenu.<\/li>\n<\/ul>\n\n<h4>0.3.1<\/h4>\n\n<ul>\n<li>Fix images never loading after the deferred AJAX refresh: Chromium does not natively lazy-load images parsed via innerHTML, so the plugin now promotes them itself once the feed nears the viewport.<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Add saved feeds: create named hashtag filter sets in Settings and embed them with <code>[shootcal_instagram_feed feed=\"N\"]<\/code>; editing a saved feed updates every page using it, and dynamic embeds pick up edits through full-page caches.<\/li>\n<li>Support comma-separated any-of hashtag lists and a new <code>exclude<\/code> attribute in the shortcode, the REST route, and the default-hashtag setting.<\/li>\n<li>Hide empty-feed and configuration messages from visitors; administrators still see them.<\/li>\n<li>Use the first carousel child with a usable image as the cover instead of only the first child.<\/li>\n<li>Warn administrators on every dashboard page when the feed has not refreshed for two days, before Meta's signed image URLs expire.<\/li>\n<\/ul>\n\n<h4>0.2.2<\/h4>\n\n<ul>\n<li>Allow validated OAuth and disconnect writes through the settings sanitizer, with exact database read-back verification.<\/li>\n<\/ul>\n\n<h4>0.2.1<\/h4>\n\n<ul>\n<li>Distinguish secure broker-response failures from local encrypted-storage failures during one-click setup without exposing tokens or provider data.<\/li>\n<\/ul>\n\n<h4>0.2.0<\/h4>\n\n<ul>\n<li>Rename the plugin to ShootCal Social Feed while preserving existing shortcodes, settings, and cached data.<\/li>\n<li>Add one-click Facebook authorization through ShootCal with one-time server-to-server token redemption.<\/li>\n<li>Keep the existing encrypted local token storage, local feed cache, and manual-token fallback.<\/li>\n<li>Clear the selected account boundary on disconnect and document complete plugin data removal.<\/li>\n<\/ul>\n\n<h4>0.1.4<\/h4>\n\n<ul>\n<li>Load dynamic-feed CSS after page load so full-page cache CSS optimizers cannot strip the feed layout.<\/li>\n<\/ul>\n\n<h4>0.1.3<\/h4>\n\n<ul>\n<li>Load feed assets before the document head closes when a page contains the shortcode.<\/li>\n<\/ul>\n\n<h4>0.1.2<\/h4>\n\n<ul>\n<li>Add optional responsive mobile item limits and an account follow button.<\/li>\n<li>Add an opt-in JavaScript refresh backed by a cache-only REST endpoint for full-page-cached sites.<\/li>\n<\/ul>\n\n<h4>0.1.1<\/h4>\n\n<ul>\n<li>Prevent a carousel video URL from being rendered as an image when Meta does not provide a thumbnail.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial private testing release.<\/li>\n<\/ul>","raw_excerpt":"A lightweight, cached Instagram feed for professional accounts with local hashtag filtering.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/371036","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=371036"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/rsmith4321"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=371036"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=371036"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=371036"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=371036"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=371036"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=371036"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}