MainStage Vision API Documentation

v3.0 February 2026

Public API Overview

No Authentication Required CORS Enabled

All endpoints listed below are publicly accessible and support cross-origin requests. No API key or authentication is needed. Responses are returned as JSON (unless otherwise noted).

MethodEndpointDescription
GET/api/events/publicUpcoming active events with lineup
GET/api/events/public/:idEvent detail with related content
GET/api/events/pastPast events archive
GET/api/news/publicPublished news articles
GET/api/news/categoriesAvailable news categories
GET/api/albums/publicApproved photo albums
GET/api/videos/publicApproved videos
GET/api/artists/publicAll artists with stats
GET/api/artists/public/:idArtist profile with related content
GET/api/searchGlobal content search
GET/api/public/headliner-announcementsActive headliner announcements
POST/api/newsletter/subscribeSubscribe to newsletter
POST/api/public/staff-applicationSubmit staff application
GET/api/calendar/events.icsiCal calendar feed
POST/api/contactSubmit contact form
GET/api/sitemap.xmlXML sitemap
GET/api/llms.txtLLM-friendly site summary
GET/api/llms-full.txtFull LLM context with all content

CORS Headers

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type

Standard Error Response

{
  "message": "Error description"
}

Events API

GET /api/events/public Upcoming active events

Returns all active future events sorted by start date, including full lineup artists with photos and genre information.

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Maximum number of events. Use 0 for unlimited.
recentbooleanfalseSort by most recently added instead of soonest
includePastbooleanfalseInclude past events in the response

Response

{
  "events": [
    {
      "id": 87,
      "name": "Suikerrock 2026 - Day 3",
      "eventType": "Festival",
      "artist": "Headliner Name",
      "lineupArtists": [
        {
          "id": 5,
          "name": "Coldplay",
          "photoUrl": "https://mainstage.vision/.../coldplay.jpg",
          "genre": "Pop/Rock"
        }
      ],
      "logo": "event-logo-123.jpg",
      "logoUrl": "https://mainstage.vision/.../event-logo-123.jpg",
      "startDate": "2026-07-25T14:00:00.000Z",
      "endDate": "2026-07-25T23:00:00.000Z",
      "venueName": "Festivalpark",
      "city": "Tienen",
      "province": "Vlaams-Brabant",
      "country": "Belgium",
      "parentEventId": 85,
      "organizationName": "Festival Organization"
    }
  ],
  "total": 23,
  "timestamp": "2026-02-19T12:00:00.000Z"
}
GET /api/events/public/:id Event detail with related content

Returns detailed event information including full lineup (with artist photos), related news articles, photo albums, videos, and sub-events (for multi-day festivals).

URL Parameters

ParameterTypeDescription
idintegerEvent ID

Response

{
  "id": 87,
  "name": "Suikerrock 2026 - Day 3",
  "eventType": "Festival",
  "artist": "Headliner",
  "description": "Full event description...",
  "startDate": "2026-07-25T14:00:00.000Z",
  "endDate": "2026-07-25T23:00:00.000Z",
  "venueName": "Festivalpark",
  "address": "Grote Markt",
  "city": "Tienen",
  "postalCode": "3300",
  "province": "Vlaams-Brabant",
  "country": "Belgium",
  "status": "open",
  "logoUrl": "https://mainstage.vision/.../event-logo.jpg",
  "parentEvent": { "id": 85, "name": "Suikerrock 2026" },
  "childEvents": [
    { "id": 86, "name": "Suikerrock 2026 - Day 2", "startDate": "..." }
  ],
  "lineup": [
    {
      "id": 5, "name": "Coldplay", "genre": "Pop/Rock",
      "description": "...", "photoUrl": "..."
    }
  ],
  "relatedNews": [
    {
      "id": 12, "title": "...", "summary": "...",
      "category": "Festival", "featuredImage": "...", "publishDate": "..."
    }
  ],
  "relatedAlbums": [
    {
      "id": 3, "title": "...", "artistName": "Coldplay",
      "photoCount": 25, "featuredPhotoUrl": "..."
    }
  ],
  "relatedVideos": [
    {
      "id": 1, "title": "...", "artistName": "Coldplay",
      "youtubeVideoId": "abc123",
      "youtubeUrl": "https://www.youtube.com/watch?v=abc123",
      "thumbnailUrl": "https://img.youtube.com/vi/abc123/hqdefault.jpg"
    }
  ]
}
GET /api/events/past Past events archive

Returns all past events sorted by date (most recent first), including lineup artists and logo URLs.

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Maximum number of events. Use 0 for unlimited.

Response

{
  "events": [
    {
      "id": 54,
      "name": "Rock Werchter 2025",
      "eventType": "Festival",
      "startDate": "2025-07-04T14:00:00.000Z",
      "venueName": "Festivalpark Werchter",
      "city": "Werchter",
      "lineupArtists": [
        { "id": 3, "name": "Dua Lipa", "photoUrl": "...", "genre": "Pop" }
      ],
      "logoUrl": "https://mainstage.vision/.../rw-2025.jpg"
    }
  ],
  "total": 45
}

News API

GET /api/news/public Published news articles

Returns all published news articles sorted by publication date (newest first). Includes rich text content, tags, and related event information.

Query Parameters

ParameterTypeDescription
limitintegerMaximum articles (omit for all)
eventIdintegerFilter by event ID
categorystringFilter by category name
breakingbooleanOnly breaking news (true)

Response

{
  "articles": [
    {
      "id": 12,
      "title": "Festival announces headliners",
      "slug": "festival-announces-headliners",
      "excerpt": "Short summary...",
      "summary": "Full summary...",
      "content": "<p>Full HTML content...</p>",
      "photoCredit": "Sarah Lens",
      "author": "Gunther Vangenechten",
      "publishedAt": "2026-06-01T10:00:00.000Z",
      "createdAt": "2026-06-01T09:30:00.000Z",
      "category": "Festival",
      "tags": ["festival", "lineup"],
      "featuredImage": "/uploads/news/headliners.jpg",
      "isBreaking": false,
      "isFeatured": true,
      "readTime": 3,
      "eventId": 85,
      "relatedEventIds": [85, 86, 87],
      "isEventSpecific": true
    }
  ],
  "total": 25,
  "timestamp": "2026-02-19T12:00:00.000Z"
}
Image URLs: The featuredImage field may be a relative path (/uploads/news/file.jpg), a filename (file.jpg), or an absolute URL. Prepend the base URL for relative paths.
GET /api/news/categories Available news categories

Returns all available news categories. Categories are dynamically managed via the admin settings.

Response

{
  "categories": [
    { "id": 0, "name": "Algemeen", "slug": "algemeen" },
    { "id": 1, "name": "Concert", "slug": "concert" },
    { "id": 2, "name": "Festival", "slug": "festival" },
    { "id": 3, "name": "Nieuws", "slug": "nieuws" }
  ]
}

Albums API

GET /api/albums/public Approved photo albums

Returns approved photo albums including all photos with per-photo photographer credits. Albums go through an approval workflow before becoming public.

Query Parameters

ParameterTypeDefaultDescription
idinteger-Fetch a single album by ID
limitinteger20Maximum albums (max: 50)
eventIdinteger-Filter by event
featuredboolean-Only featured albums
recentboolean-Sort by newest first

Response

{
  "albums": [
    {
      "id": 3,
      "title": "Suikerrock 2026 - Coldplay",
      "slug": "suikerrock-2026-coldplay",
      "eventId": 87,
      "eventName": "Suikerrock 2026 - Day 3",
      "artistName": "Coldplay",
      "photographerName": "Sarah Lens",
      "photoCount": 25,
      "featuredPhotoId": 42,
      "isFeatured": true,
      "featuredPhoto": {
        "url": "https://mainstagevision.be/photos/.../photo-001.jpg",
        "thumbnailUrl": "https://mainstagevision.be/photos/.../thumb.jpg",
        "isFeatured": true
      },
      "photos": [
        {
          "id": 42,
          "url": "https://mainstagevision.be/photos/.../photo-001.jpg",
          "thumbnailUrl": "https://mainstagevision.be/photos/.../thumb.jpg",
          "filename": "photo-001.jpg",
          "isFeatured": true,
          "photographerName": "Sarah Lens"
        }
      ],
      "event": {
        "name": "Suikerrock 2026 - Day 3",
        "startDate": "2026-07-25T14:00:00.000Z",
        "venueName": "Festivalpark",
        "city": "Tienen"
      },
      "tags": ["concert", "festival"],
      "createdAt": "2026-07-26T10:00:00.000Z"
    }
  ],
  "total": 5,
  "timestamp": "2026-02-19T12:00:00.000Z"
}
Per-photo credits: Each photo in the photos array has its own photographerName field, which may differ per photo when multiple photographers contributed to an album.

Videos API

GET /api/videos/public Approved videos

Returns approved and published videos. Videos go through an approval workflow before becoming public. Most videos are hosted on YouTube.

Query Parameters

ParameterTypeDefaultDescription
idinteger-Fetch a single video
limitinteger20Maximum videos (max: 50)
eventIdinteger-Filter by event
recentboolean-Sort by newest first

Response

{
  "videos": [
    {
      "id": 1,
      "title": "Suikerrock 2026 - Coldplay Highlights",
      "slug": "suikerrock-2026-coldplay-highlights",
      "description": "Concert highlights",
      "eventId": 87,
      "eventName": "Suikerrock 2026 - Day 3",
      "artistName": "Coldplay",
      "videographerName": "Mark Capture",
      "youtubeVideoId": "dQw4w9WgXcQ",
      "youtubeUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
      "thumbnailUrl": "https://img.youtube.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
      "websiteUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
      "duration": 180,
      "tags": ["concert", "live"],
      "createdAt": "2026-07-26T12:00:00.000Z",
      "approvedAt": "2026-07-26T14:00:00.000Z"
    }
  ],
  "total": 3,
  "timestamp": "2026-02-19T12:00:00.000Z"
}
YouTube Integration: Use youtubeVideoId to construct embed URLs: https://www.youtube.com/embed/{youtubeVideoId}. The thumbnailUrl provides a high-quality YouTube thumbnail.

Artists API

GET /api/artists/public All artists with stats

Returns all artists with upcoming event count and total photo count from approved albums.

Response

[
  {
    "id": 5,
    "name": "Coldplay",
    "genre": "Pop/Rock",
    "bio": "British rock band",
    "photoUrl": "https://mainstage.vision/.../coldplay.jpg",
    "eventCount": 2,
    "photoCount": 45
  }
]
GET /api/artists/public/:id Artist profile with related content

Returns a full artist profile including all related events (upcoming and past), photo albums, news articles, and videos.

URL Parameters

ParameterTypeDescription
idintegerArtist ID

Response

{
  "id": 5,
  "name": "Coldplay",
  "genre": "Pop/Rock",
  "description": "British rock band from London",
  "bio": "Coldplay is a British rock band...",
  "photoUrl": "https://mainstage.vision/.../coldplay.jpg",
  "events": [
    {
      "id": 87, "name": "Suikerrock 2026",
      "eventType": "Festival",
      "startDate": "2026-07-25T14:00:00.000Z",
      "venueName": "Festivalpark",
      "city": "Tienen", "logoUrl": "...", "isPast": false
    }
  ],
  "albums": [
    { "id": 3, "title": "Suikerrock 2026 - Coldplay", "photoCount": 25, "featuredPhotoUrl": "..." }
  ],
  "news": [
    { "id": 12, "title": "Festival announces headliners", "summary": "...", "category": "Festival" }
  ],
  "videos": [
    {
      "id": 1, "title": "Coldplay Highlights",
      "youtubeVideoId": "abc123",
      "youtubeUrl": "https://www.youtube.com/watch?v=abc123",
      "thumbnailUrl": "https://img.youtube.com/vi/abc123/hqdefault.jpg"
    }
  ]
}

Headliner Announcements

New
GET /api/public/headliner-announcements Active headliner announcements

Returns all currently active headliner announcements, enriched with event and artist details. Announcements are time-bound and only returned when within their active window.

Response

[
  {
    "id": 1,
    "eventId": 87,
    "artistId": 5,
    "title": "Headliner Revealed!",
    "description": "Coldplay will headline Suikerrock 2026",
    "announcementDate": "2026-03-15T10:00:00.000Z",
    "isActive": true,
    "eventName": "Suikerrock 2026 - Day 3",
    "eventLogo": "https://mainstage.vision/.../event-logo.jpg",
    "eventStartDate": "2026-07-25T14:00:00.000Z",
    "artistName": "Coldplay",
    "artistPhoto": "https://mainstage.vision/.../coldplay.jpg"
  }
]

Newsletter API

New
POST /api/newsletter/subscribe Subscribe to newsletter

Subscribe an email address to the MainStage Vision newsletter. Duplicate subscriptions are handled gracefully.

Request Body

{
  "email": "john@example.com"
}

Validation

FieldTypeRequiredConstraints
emailstringYesValid email format

Success Response

{
  "message": "Succesvol ingeschreven!",
  "subscriber": {
    "id": 42,
    "email": "john@example.com"
  }
}

Error Response (400)

{ "message": "Ongeldig e-mailadres" }

Staff Applications

New
POST /api/public/staff-application Submit staff application

Submit a staff application to join the MainStage Vision team. Applications are reviewed by administrators. A confirmation email with terms & conditions PDF is sent automatically.

Request Body

{
  "firstName": "John",
  "lastName": "Doe",
  "email": "john@example.com",
  "phone": "+32 471 12 34 56",
  "address": "Hoofdstraat 1",
  "city": "Antwerpen",
  "postalCode": "2000",
  "motivation": "I'm passionate about concert photography...",
  "cameraEquipment": "Canon EOS R5, 70-200mm f/2.8",
  "portfolioUrl": "https://portfolio.example.com",
  "desiredRole": "photographer"
}

Validation

FieldTypeRequiredConstraints
firstNamestringYes-
lastNamestringYes-
emailstringYesValid email format
phonestringNo-
addressstringNo-
citystringNo-
postalCodestringNo-
motivationstringYes-
cameraEquipmentstringNo-
portfolioUrlstringNoValid URL
desiredRolestringYesphotographer, videographer, editor, or reporter

Success Response

{ "success": true, "message": "Aanmelding succesvol ontvangen!" }

Calendar API

GET /api/calendar/events.ics iCal calendar feed

Returns all active events in iCal format. Subscribe directly from any calendar application to stay updated with upcoming events.

Response Headers

Content-Type: text/calendar; charset=utf-8
Content-Disposition: attachment; filename="mainstage-events.ics"

Subscribe via Calendar App

ApplicationInstructions
Google CalendarOther calendars → From URL → paste the endpoint URL
Apple CalendarFile → New Calendar Subscription → paste the endpoint URL
OutlookAdd Calendar → From Internet → paste the endpoint URL

Contact API

POST /api/contact Submit contact form

Sends a contact form message to the MainStage Vision team. Messages are delivered via email.

Request Body

{
  "name": "John Doe",
  "email": "john@example.com",
  "subject": "Press accreditation inquiry",
  "message": "Hello, I would like to inquire about..."
}

Validation

FieldTypeRequiredConstraints
namestringYes-
emailstringYesValid email format
subjectstringYes-
messagestringYesMax 5000 characters

Success Response

{ "message": "Thank you! We will get back to you shortly." }

Error Response (400)

{ "error": "All fields are required." }

Sitemap & SEO

GET /api/sitemap.xml XML sitemap for search engines

Returns an XML sitemap containing all public pages, published news articles, approved albums, and artist pages for search engine indexing.

Response

Content-Type: application/xml

Included pages:

  • Static pages (homepage, news, albums, videos, events, artists)
  • All published news article detail pages
  • All approved album detail pages
  • All artist detail pages
GET /api/llms.txt LLM-friendly site summary

Returns a plain-text summary of the MainStage Vision website optimized for consumption by large language models (LLMs). Follows the llms.txt specification.

Response

Content-Type: text/plain; charset=utf-8
GET /api/llms-full.txt Full LLM context with all content

Returns a comprehensive plain-text export of all public content (events, news, albums, videos, artists) for deep LLM context. This is the extended version of /api/llms.txt.

Response

Content-Type: text/plain; charset=utf-8

Code Examples

Fetch Upcoming Events (JavaScript)

const API = 'https://mainstagevision.replit.app';

async function getEvents(limit = 10) {
  const res = await fetch(`${API}/api/events/public?limit=${limit}`);
  const data = await res.json();
  return data.events;
}

getEvents().then(events => {
  events.forEach(e => {
    console.log(`${e.name} @ ${e.venueName}, ${e.city}`);
    console.log(`Date: ${new Date(e.startDate).toLocaleDateString('en-GB')}`);
    if (e.lineupArtists?.length > 0) {
      console.log(`Lineup: ${e.lineupArtists.map(a => a.name).join(', ')}`);
    }
  });
});

Fetch News with Filters (JavaScript)

const API = 'https://mainstagevision.replit.app';

async function getNews(options = {}) {
  const params = new URLSearchParams();
  if (options.limit) params.append('limit', options.limit);
  if (options.category) params.append('category', options.category);
  if (options.breaking) params.append('breaking', 'true');

  const res = await fetch(`${API}/api/news/public?${params}`);
  const data = await res.json();
  return data.articles;
}

function getImageUrl(img) {
  if (!img) return null;
  if (img.startsWith('http')) return img;
  if (img.startsWith('/')) return `${API}${img}`;
  return `${API}/uploads/news/${img}`;
}

getNews({ category: 'Festival', limit: 5 }).then(articles => {
  articles.forEach(a => {
    console.log(`${a.title} - ${getImageUrl(a.featuredImage)}`);
  });
});

Search (JavaScript)

const API = 'https://mainstagevision.replit.app';

async function search(query) {
  const res = await fetch(`${API}/api/search?q=${encodeURIComponent(query)}`);
  const data = await res.json();
  return data.results;
}

search('coldplay').then(results => {
  results.forEach(r => console.log(`[${r.type}] ${r.title}`));
});

Subscribe to Newsletter (JavaScript)

const API = 'https://mainstagevision.replit.app';

async function subscribe(email) {
  const res = await fetch(`${API}/api/newsletter/subscribe`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email })
  });
  return await res.json();
}

subscribe('fan@example.com').then(result => {
  console.log(result.message);
});

HTML Events Widget (Embeddable)

<div id="events"></div>
<script>
  fetch('https://mainstagevision.replit.app/api/events/public?limit=10')
    .then(res => res.json())
    .then(data => {
      const el = document.getElementById('events');
      data.events.forEach(e => {
        const date = new Date(e.startDate).toLocaleDateString('en-GB');
        const lineup = (e.lineupArtists || []).map(a => a.name).join(', ');
        el.innerHTML += `
          <div style="padding:12px;border:1px solid #eee;margin:8px 0;border-radius:8px">
            <h3>${e.name}</h3>
            <p>${e.venueName}, ${e.city} &bull; ${date}</p>
            ${lineup ? `<p style="color:#666">Lineup: ${lineup}</p>` : ''}
          </div>`;
      });
    });
</script>

Python Example

import requests

API = 'https://mainstagevision.replit.app'

# Get upcoming events
events = requests.get(f'{API}/api/events/public?limit=10').json()
for event in events['events']:
    print(f"{event['name']} - {event['city']}")
    artists = [a['name'] for a in event.get('lineupArtists', [])]
    if artists:
        print(f"  Lineup: {', '.join(artists)}")

# Search
results = requests.get(f'{API}/api/search', params={'q': 'rock werchter'}).json()
for r in results['results']:
    print(f"[{r['type']}] {r['title']}")

cURL Examples

# Get upcoming events
curl -s "https://mainstagevision.replit.app/api/events/public?limit=5" | jq

# Get event detail
curl -s "https://mainstagevision.replit.app/api/events/public/87" | jq

# Search
curl -s "https://mainstagevision.replit.app/api/search?q=coldplay" | jq

# Subscribe to newsletter
curl -X POST "https://mainstagevision.replit.app/api/newsletter/subscribe" \
  -H "Content-Type: application/json" \
  -d '{"email":"fan@example.com"}'

# Get iCal feed
curl -s "https://mainstagevision.replit.app/api/calendar/events.ics" -o events.ics

No rate limiting is currently active. For high-volume usage, cache responses and avoid querying the same endpoint more than once per minute.

Support: planning@mainstage.vision