Injecting Structured Data Into 12 Concert Event Pages: Automating Schema.org JSON-LD Deployment Across Multi-Site Infrastructure
What Was Done
Over a single development session, we identified that zero structured data existed across 12 active concert event pages spanning 6 CloudFront-distributed subdomains. We built an automated injection pipeline that:
- Wrote a Python script (
inject_structured_data.py) to parse HTML and inject Event + LocalBusiness JSON-LD schemas - Applied the script to all active event pages across the Rady Shell event subdomain network
- Deployed updated pages to S3 buckets for each event subdomain
- Invalidated CloudFront caches to ensure immediate propagation
- Established a repeatable pattern for future event pages
This work was part of a larger CMO initiative to close the visibility gap across JADA's multi-site presence: 3,676 email contacts, 157 reviews, and a beautiful brand footprint are generating minimal inbound traffic due to zero structured data, Google Business Profile misconfiguration, and dormant social channels.
Technical Details: The Structured Data Injection Script
The core script, /Users/cb/Documents/repos/tools/inject_structured_data.py, was designed to be idempotent and non-destructive. It performs three operations:
1. Parse HTML and detect existing structured data (skip if present)
2. Build Event + LocalBusiness JSON-LD payloads from page metadata
3. Inject into <head> before the closing tag
Why this approach? Concert pages live in multiple code repositories with different build systems. Rather than modifying 6 separate build pipelines or templates, we built a post-deployment injection layer. This is a common pattern when retrofitting SEO infrastructure into existing multi-site deployments.
The script targets two schema types:
- Event schema: Captures concert name, date, time, location, performer, and ticket URL. Google uses this for rich event cards in search results and knowledge panels.
- LocalBusiness schema: Reinforces venue location (The Rady Shell at Jacobs Park), phone, address, and business hours. Helps Google associate events with the physical location.
The injection point is always </head>, placed after any existing meta tags but before the closing head tag. This ensures:
- Google's parser encounters structured data before rendering the page
- No conflicts with existing analytics or tracking scripts
- Validates cleanly against Schema.org specification
Infrastructure: S3 Buckets and CloudFront Distribution Map
The Rady Shell event subdomain network spans 6 independent S3+CloudFront deployments, each with its own bucket and distribution ID. The pattern is:
Subdomain: [event-name].queenofsandiego.com
S3 Bucket: [event-name].queenofsandiego.com
CloudFront Distribution: E2[...] (varies by event)
We identified the following active event subdomains requiring updates:
paulsimonradyshell.queenofsandiego.combobdylanradyshell.queenofsandiego.comfleetmacradyshell.queenofsandiego.comsteely-dan-rady-shell.queenofsandiego.com- Plus 2 additional event subdomains
Each subdomain bucket contains a static site with an index.html serving as the primary event page. The injection script targeted these index files plus any secondary pages (ticketing, venue details, artist bios).
Why S3 + CloudFront for event pages? These are static, high-traffic pages that need:
- Geographic distribution: CloudFront edge locations serve cached pages from 200+ global POPs, reducing latency for ticket-buyers searching from anywhere
- Cost efficiency: S3 + CloudFront is ~90% cheaper than EC2-based hosting for static content
- Cache control: CloudFront's 24-hour default TTL ensures schema updates propagate predictably
- SEO stability: Static content = zero runtime errors, zero downtime, perfect crawlability
Deployment and Cache Invalidation
After running the injection script locally and validating output, we synced updated files using the AWS CLI pattern:
aws s3 sync ./updated-pages/ s3://[event-name].queenofsandiego.com/ --delete
The --delete flag ensures orphaned files are removed; since we're only updating index files, this is safe.
Immediately after each sync, we invalidated the CloudFront cache:
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/*"
Why full-path invalidation? We could have invalidated only /index.html, but concert pages often reference secondary assets (social images, structured data in secondary pages). A full cache invalidation costs the same as a partial one (CloudFront charges per invalidation request, not per path) and eliminates edge-case stale-cache issues.
Invalidation status was monitored using:
aws cloudfront get-invalidation --distribution-id [DIST_ID] --id [INVALIDATION_ID]
CloudFront invalidations typically propagate globally within 2–5 minutes. We waited for all 6 distributions to report Completed status before declaring the deployment done.
Key Decisions and Trade-offs
Decision 1: Why not modify source templates? The event pages are built from 6 different source repositories, some using static site generators, others hand-authored HTML. Coordinating template changes across 6 repos introduces merge-conflict risk and slows future event deployments. The injection-after-build approach is slower for initial setup but faster for operations.
Decision 2: Why Event + LocalBusiness schemas, not Organization? Google's search algorithm has matured significantly for Event schema. A concert page with Event + LocalBusiness signals:
- What is being sold (Event)
- Where it's happening (LocalBusiness venue details)
- Who's running the venue (optional Organization)
This triple-signal improves click-through rate on Google search and enables rich snippets in the Google Events portal.
Decision 3: JSON-LD vs. Microdata? We chose JSON-LD because:
- It's Google's preferred format (stated in their structured data docs)
- It doesn't pollute HTML class/attribute syntax
- It's easier to inject post-build without parsing/modifying the DOM
- It's more maintainable in version control (pure JSON vs. scattered attributes)
What's Next
This structured data injection establishes a repeatable pattern for future event pages. The next priorities in the CMO