```html

Injecting Structured Data Into Concert Event Pages: A Multi-Domain Schema Implementation

Over the course of a development session focused on improving search engine visibility and marketing infrastructure, we discovered that twelve active concert event pages across multiple subdomains were missing critical structured data markup. This post walks through the technical approach we took to identify, inject, and deploy Event and LocalBusiness JSON-LD schema to production across a distributed multi-domain architecture.

The Problem: Zero Structured Data on Revenue-Driving Pages

The JADA / Queen of San Diego event ecosystem consists of specialized concert subdomains (e.g., paulsimonradyshell.queenofsandiego.com, sailjada.queenofsandiego.com) hosting event landing pages. A structured audit revealed that despite having high-quality content, professional imagery, and review data, none of these pages were emitting Event or LocalBusiness JSON-LD schema. This meant search engines couldn't understand:

  • Event dates, times, and locations
  • Ticket availability and pricing
  • Venue information and ratings
  • Performer details and aggregated reviews

Without this markup, we were losing rich snippet eligibility in Google Search results and forfeiting schema-powered features in Google Business Profile integrations.

Technical Discovery Phase

We began by mapping the directory structure and identifying which pages needed treatment:

# Rady Shell Events directory structure
/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/
├── index.html
├── paul-simon.html
├── tools/
│   └── render_event_sites.py
└── [other event pages]

# Sailjada subdomain structure
/Users/cb/Documents/repos/sites/sailjada.queenofsandiego.com/
├── index.html
└── ranch-and-coast.html

We then audited twelve active event pages by examining their HTML <head> sections, confirming that none contained <script type="application/ld+json"> blocks. This was our baseline.

Schema Design and Implementation

We created a Python script at /Users/cb/Documents/repos/tools/inject_structured_data.py to systematically inject properly-formed JSON-LD into each page. The script:

  • Reads each HTML file and parses the <head> section
  • Generates Event schema with date, location, performer, and URL data
  • Generates LocalBusiness schema for the Rady Shell venue
  • Inserts both schemas before the closing </head> tag
  • Preserves all existing markup (particularly existing GA tracking)

The Event schema structure follows Google's Event schema specification and includes:

{
  "@context": "https://schema.org",
  "@type": "Event",
  "name": "[Event Name]",
  "url": "[Event URL]",
  "image": "[Hero Image URL]",
  "description": "[Event Description]",
  "startDate": "[ISO 8601 Datetime]",
  "endDate": "[ISO 8601 Datetime]",
  "eventStatus": "https://schema.org/EventScheduled",
  "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
  "location": {
    "@type": "Place",
    "name": "The Rady Shell at Jacobs Park",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "[Address]",
      "addressLocality": "San Diego",
      "addressRegion": "CA",
      "postalCode": "[ZIP]",
      "addressCountry": "US"
    }
  },
  "performer": {
    "@type": "Person",
    "name": "[Artist Name]"
  }
}

This structure enables Google to surface event details in Knowledge Panels, rich snippets, and Google Search event results.

Multi-Domain Deployment Strategy

The architecture presented a key complexity: event pages live in different S3 buckets and are served through different CloudFront distributions. We had to coordinate deployment across:

  • paulsimonradyshell.queenofsandiego.com — S3 bucket with dedicated CloudFront distribution
  • sailjada.queenofsandiego.com — Separate S3 bucket with separate CloudFront distribution
  • Additional event subdomains — Each with isolated infrastructure

Rather than deploy pages manually, we:

  1. Ran the injection script locally against all 12 pages
  2. Synced updated pages to their respective S3 buckets using AWS CLI
  3. Invalidated CloudFront caches for each distribution to ensure immediate propagation

Example deployment command pattern:

# Sync updated event pages to S3
aws s3 sync ./rady-shell-events/ s3://paulsimonradyshell-bucket/ \
  --include "*.html" \
  --exclude "tools/*" \
  --profile production

# Invalidate CloudFront to force cache refresh
aws cloudfront create-invalidation \
  --distribution-id E1A2B3C4D5E6F7 \
  --paths "/*.html" \
  --profile production

Key Decisions and Rationale

Why JSON-LD over Microdata: We chose JSON-LD because it's decoupled from HTML markup, easier to maintain in a multi-template environment, and has superior tooling support. Our event pages are generated by Python scripts (render_event_sites.py, generate_service_area_pages.py), so JSON-LD allows us to inject schema without modifying template logic.

Why Inject Rather Than Template-Generate: The injection script was faster to implement than refactoring the generation tools themselves. However, we documented the head tag locations in render_event_sites.py for future refactoring to make schema generation part of the build pipeline rather than a post-production patch.

CloudFront Invalidation Strategy: We used path-based invalidations rather than blanket distribution invalidations to minimize impact on other assets and keep CloudFront costs predictable.

Integration with Existing Infrastructure

The injection maintained compatibility with:

  • Google Analytics: All existing GA4 <script> tags remained unchanged and in their original positions
  • Email Popup Script: The /assets/js/email-popup.js injection on sailjada.com was unaffected
  • Referral Partner Templates: The referral partner brief template at /notes/referral_partner_brief_template.html was noted but not modified (it serves a different purpose)

What's Next

This injection is a tactical improvement. Strategically, we should:

  • Refactor generation tools: Update render_event_sites.py and generate_service_area_pages.py to emit schema directly during