```html

Injecting Structured Data into Concert Event Pages: A Multi-Site JSON-LD Strategy

A CMO assessment revealed that JADA's beautiful multi-site presence was invisible to search engines and third-party platforms. While the brand had 157 reviews across Google and Yelp, zero structured data existed on concert event pages—meaning Google couldn't parse event details, dates, or venue information. This session focused on closing that gap by systematically injecting Event and LocalBusiness JSON-LD schema across 12 concert pages deployed to 4 separate event subdomains.

The Problem: Invisible Events in Search Results

Concert pages at domains like paulsimonradyshell.com, fleetwood-mac.queenofsandiego.com, and other event subdomains were generating HTML but zero semantic markup. Without structured data:

  • Google Search couldn't display event details in rich snippets
  • Third-party ticketing platforms couldn't scrape event metadata
  • OTA integrations (if deployed later) would have no machine-readable source of truth
  • Local business schema was missing, even though events were tied to physical venues

The fix required:

  • Building a Python script to audit which pages had schema
  • Creating JSON-LD templates for Event + LocalBusiness schema
  • Injecting that schema into all active concert pages
  • Deploying to S3 and invalidating CloudFront caches

Technical Approach: Automated Schema Injection

Step 1: Auditing Existing Schema

First, I checked which pages already had structured data by scanning the event subdomain file structure:


# Check all event pages for schema presence
/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/

# Sample active event page directories:
- paulsimonradyshell.com/ → index.html
- fleetwood-mac.queenofsandiego.com/ → index.html
- other-concert-subdomain/ → index.html

Result: Zero pages had structured data. Every event page was pure HTML with no <script type="application/ld+json"> blocks.

Step 2: Building the Injection Script

I created /Users/cb/Documents/repos/tools/inject_structured_data.py to:

  • Parse HTML files and detect the closing </head> tag
  • Generate Event schema with title, date, location, and description
  • Generate LocalBusiness schema for the venue
  • Insert both JSON-LD blocks before </head>
  • Preserve original HTML formatting
  • Log which pages were modified

The script targeted this file pattern:


/Users/cb/Documents/repos/sites/sailjada.queenofsandiego.com/
/Users/cb/Documents/repos/sites/[event-subdomain]/index.html

Step 3: Schema Structure

Each injected block included two schema types:


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Event",
  "name": "Concert Title",
  "description": "Event description",
  "startDate": "2024-MM-DDTHH:MM:SS",
  "endDate": "2024-MM-DDTHH:MM:SS",
  "location": {
    "@type": "Place",
    "name": "Venue Name",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "...",
      "addressLocality": "San Diego",
      "addressRegion": "CA",
      "postalCode": "92101",
      "addressCountry": "US"
    }
  },
  "image": "https://...",
  "organizer": {
    "@type": "Organization",
    "name": "JADA"
  }
}
</script>

Plus a LocalBusiness schema for the venue, enabling Google Business Profile integration.

Infrastructure: S3 + CloudFront Deployment Pipeline

S3 Bucket Configuration

Event subdomains are distributed across multiple S3 buckets, one per subdomain:

  • paulsimonradyshell.com → S3 bucket paulsimonradyshell-com
  • fleetwood-mac.queenofsandiego.com → S3 bucket fleetwood-mac-queenofsandiego-com
  • Additional concert subdomains → Similar naming pattern

Each bucket is configured for:

  • Static website hosting (index.html default)
  • CloudFront distribution as the public-facing CDN
  • Origin access control (not public read permissions)

CloudFront Cache Invalidation

After uploading updated HTML files with schema, I invalidated CloudFront distributions to ensure users received fresh content:


# Invalidate all event pages across distributions
# For each distribution ID:
aws cloudfront create-invalidation \
  --distribution-id [DIST_ID] \
  --paths "/*"

This ensures that cached versions (TTL typically 86400 seconds) don't serve stale pages without schema to users or crawlers.

Execution: 12 Pages Injected and Deployed

The injection and deployment workflow:

  1. Run script locally: python inject_structured_data.py processed all event pages in the working directory
  2. Verify schema: Checked each file for proper JSON-LD placement before </head>
  3. Sync to S3: Used AWS CLI to sync updated pages to their respective S3 buckets
  4. Invalidate cache: Created CloudFront invalidations for all modified distributions
  5. Log deployment: Updated the progress dashboard with completion status

Result: 12 concert pages across 4 event subdomains now carry Event + LocalBusiness schema, ready for Google Search indexing, rich snippet display, and third-party platform integration.

Key Decisions and Rationale

Why JSON-LD instead of Microdata or RDFa? JSON-LD is Google's recommended format for structured data, easier to inject into existing HTML without modifying the DOM structure, and widely supported by third-party platforms and OTA integrations.

Why inject before </head> instead of in <body>? Search engines and metadata parsers expect schema in the document head. Placing it there ensures crawlers find it immediately without parsing the entire body.

Why separate Event and LocalBusiness schemas?