API

A clean, read-only feed of what’s on at Theatre 118 — built for listings sites, partner venues, calendars and community projects.

Overview

The Theatre 118 API is a single, public endpoint that returns 118’s current programme as JSON. It carries everything an outside listings site needs to reproduce a show accurately: the prose, the poster and gallery, every performance with its curtain time and sold-out state, the ticket types, and the show’s press quotes.

There is no key and no account. The feed is read-only and deliberately limited to public information — no sales, no customers, no contracts. Requests are rate limited so a scraper can’t hammer the venue’s database, and CORS is open, so a browser can call it directly.

Base URL https://theatre118.com/api

Quick start

Fetch the show list and read the first title back out:

curl
curl -s -H "Accept: application/json" \
  https://theatre118.com/api/v1/shows | jq '.shows[0].title'

The response is an object with a little venue metadata, a generation timestamp, and the shows themselves:

200 OK
{
  "venue": {
    "name": "Theatre 118",
    "url": "https://theatre118.com",
    "pay_what_you_can": true,
    "pay_what_you_can_minimum": 300,
    "booking_fee": 150
  },
  "generated_at": "2026-10-15T20:43:22+00:00",
  "count": 2,
  "shows": [
    {
      "id": 26,
      "slug": "working-it-out",
      "url": "https://theatre118.com/whats-on/working-it-out",
      "title": "Working it Out - October 2026",
      "subtitle": "Theatre 118's monthly new material night",
      "credits": null,
      "company": "Theatre 118",
      "ownership": "venue",
      "type": "Show",
      "badge": null,
      "description": "Theatre 118 reopens with our highly popular new material night. Every month, a range of acts perform brand new play extracts, stand-up, poetry, music and pretty much any other kind of live performance you can imagine.",
      "start_date": "2026-11-16",
      "end_date": "2026-11-16",
      "runtime": null,
      "runtime_minutes": 75,
      "age_rating": "Suitable for all ages",
      "content_warnings": null,
      "accessibility": null,
      "cast": null,
      "image": {
        "url": "https://theatre118.com/storage/event-thumbnails/working-it-out-uar2ltsx.png",
        "alt": null
      },
      "gallery": [],
      "performances": [
        {
          "date": "2026-11-16",
          "time": "20:00",
          "cancelled": false,
          "sold_out": false,
          "booking_url": "https://theatre118.com/book/385"
        }
      ],
      "ticketing": {
        "on_sale": true,
        "pay_what_you_can": true,
        "pay_what_you_can_minimum": 300,
        "booking_fee": 150,
        "external_url": null,
        "book_url": "https://theatre118.com/whats-on/working-it-out",
        "ticket_types": [
          {
            "name": "Standard Admission",
            "kind": "fixed",
            "price": 300
          },
          {
            "name": "Pay What You Can",
            "kind": "pay_what_you_can",
            "price": 300
          }
        ]
      },
      "press_quotes": []
    },
    {
      "id": 25,
      "slug": "sleeping-beauty-the-new-republic-of-port-dundas",
      "url": "https://theatre118.com/whats-on/sleeping-beauty-the-new-republic-of-port-dundas",
      "title": "Sleeping Beauty & the New Republic of Port Dundas",
      "subtitle": null,
      "credits": null,
      "company": "Theatre 118",
      "ownership": "venue",
      "type": "Show",
      "badge": null,
      "description": "Theatre 118's irreverent take on panto returns: with an all-new script, absurd comedy, banging tunes and a twisted spin on a traditional fairytale!",
      "start_date": "2026-12-16",
      "end_date": "2026-12-19",
      "runtime": null,
      "runtime_minutes": 100,
      "age_rating": "Suitable for audiences aged 14+",
      "content_warnings": null,
      "accessibility": null,
      "cast": null,
      "image": {
        "url": "https://theatre118.com/storage/event-thumbnails/01M4Y7AG0W9QGZYXD932JHG8B1.jpg",
        "alt": null
      },
      "gallery": [],
      "performances": [
        {
          "date": "2026-12-16",
          "time": "20:00",
          "cancelled": false,
          "sold_out": false,
          "booking_url": "https://theatre118.com/book/380"
        },
        {
          "date": "2026-12-17",
          "time": "20:00",
          "cancelled": false,
          "sold_out": false,
          "booking_url": "https://theatre118.com/book/381"
        },
        {
          "date": "2026-12-18",
          "time": "20:00",
          "cancelled": false,
          "sold_out": false,
          "booking_url": "https://theatre118.com/book/382"
        },
        {
          "date": "2026-12-19",
          "time": "14:30",
          "cancelled": false,
          "sold_out": false,
          "booking_url": "https://theatre118.com/book/383"
        },
        {
          "date": "2026-12-19",
          "time": "20:00",
          "cancelled": false,
          "sold_out": false,
          "booking_url": "https://theatre118.com/book/384"
        }
      ],
      "ticketing": {
        "on_sale": true,
        "pay_what_you_can": true,
        "pay_what_you_can_minimum": 300,
        "booking_fee": 150,
        "external_url": null,
        "book_url": "https://theatre118.com/whats-on/sleeping-beauty-the-new-republic-of-port-dundas",
        "ticket_types": [
          {
            "name": "Standard Admission",
            "kind": "fixed",
            "price": 1200
          },
          {
            "name": "Pay What You Can",
            "kind": "pay_what_you_can",
            "price": 1200
          }
        ]
      },
      "press_quotes": []
    }
  ]
}

The endpoint

GET https://theatre118.com/api/v1/shows

Returns every show that is active, not cancelled, of a performing kind, and on now or still to come. Shows are ordered soonest first. The endpoint takes no parameters.

Filtering by date or type is left to the client — see the examples. Past shows and call-outs are not part of the feed.

Top-level fields

Field Type Description
venue object Venue-wide metadata and policy. See below.
generated_at string When the response was assembled, as ISO 8601 with an offset.
count integer The number of shows in shows.
shows array The shows, soonest first.

Venue

Venue-wide policy — Pay What You Can, its minimum and the booking fee — is repeated inside every show’s ticketing, so an individual show can be rendered on its own.

Field Type Description
name string The venue’s public name, e.g. Theatre 118.
url string The venue’s homepage, so a listing can link back.
pay_what_you_can boolean Always true. Pay What You Can is offered on every show at 118.
pay_what_you_can_minimum integer The least a buyer may pay for a Pay What You Can ticket, in pence.
booking_fee integer The flat booking fee added to every order, in pence. Charged per order, not per ticket.

Show

Each entry in shows describes one production.

Field Type Description
id integer The show’s internal ID. Stable, and safe to use as a key.
slug string A URL-safe identifier, unique across the site.
url string The show’s page on theatre118.com. The best place to send an audience.
title string The show’s title.
subtitle string|null A short subtitle or tagline, if the show has one.
credits string|null Writer, director and other credits, as plain text.
company string|null The visiting company presenting the show. null for a Theatre 118 production.
ownership <code>"company"</code> | <code>"venue"</code> Who presents the show: a visiting company, or Theatre 118 itself (venue).
type string The kind of show, e.g. Play, Comedy, Musical, Spoken Word. Call-outs (Opportunities) never appear in this feed.
badge string|null An optional short editorial label, e.g. Fringe preview.
description string|null The show’s prose as plain text, paragraphs separated by blank lines.
start_date "YYYY-MM-DD"|null The first day of the run, local to Glasgow.
end_date "YYYY-MM-DD"|null The last day of the run, local to Glasgow.
runtime string|null A human-readable running time, as written for audiences, e.g. 1 hour 15 minutes, no interval.
runtime_minutes integer|null The running time in minutes, for sorting or filtering. The structured counterpart to runtime.
age_rating string|null The recommended age, e.g. 14+.
content_warnings string|null Content notes, as plain text.
accessibility string|null Access information for this show, as plain text.
cast string|null The cast, as plain text.
image object|null The poster: { url, alt }. URLs are absolute.
gallery array Additional images, each { url, alt }. Often empty.
performances array Every dated sitting. See Performances.
ticketing object Prices, sale status and booking link. See Ticketing.
press_quotes array Reviews carried by the show. May be empty.

Image object

The poster in image, and each entry in gallery:

Field Type Description
url string An absolute URL to the image. Hotlinking is fine; please cache.
alt string|null Alternative text for the image, when 118 has written it.

Press quote object

Each entry in press_quotes:

Field Type Description
text string The quote itself.
source string|null Where it was published, e.g. The Herald.
rating integer|null A star rating out of five, when the source gave one.
context string|null When or where the review ran, e.g. Fringe 2025.
url string|null A link to the full review, when one is available.

Performances

A show’s performances are its dated sittings, earliest first — a matinee and an evening show can share a day. Tickets are sold against a performance, not a seat, so the feed reports only whether a performance is sold out, not how many tickets remain.

Field Type Description
date "YYYY-MM-DD" The performance date, local to Glasgow.
time "HH:MM"|null The curtain time as a 24-hour local clock time. null when 118 has no time on record.
cancelled boolean Whether this individual performance has been cancelled.
sold_out boolean true when the performance tracks ticket stock and none is left. Exact remaining counts are not exposed.
booking_url string|null A direct link into 118’s checkout. null when the show is not on sale or this performance is cancelled.

On sold-out state

sold_out is a live snapshot and can change as tickets sell. It is the flag to drive a badge or to hide a booking link; the checkout is always the source of truth. A performance that doesn’t track stock always reports false.

Ticketing

Every show carries a ticketing object describing how it can be booked and at what prices.

Field Type Description
on_sale boolean Whether tickets can be bought through Theatre 118 right now.
pay_what_you_can boolean Always true. Reported per show, reflecting 118’s venue-wide policy.
pay_what_you_can_minimum integer The least a buyer may pay for a Pay What You Can ticket, in pence. Read it from the response; it can change.
booking_fee integer The booking fee, in pence, added once per order.
external_url string|null Where tickets are sold when they are not sold through 118, e.g. an external box office. Usually null.
book_url string The show’s public page, where a visitor chooses a performance and pays.
ticket_types array What a buyer can choose between: fixed prices first, Pay What You Can last. See below.

Ticket type object

Each entry in ticket_types:

Field Type Description
name string The ticket name shown to buyers, e.g. Standard Admission or Concession.
kind <code>"fixed"</code> | <code>"pay_what_you_can"</code> A fixed price, or a Pay What You Can ticket the buyer prices themselves.
price integer The price in pence. For a Pay What You Can ticket, the suggested starting amount; buyers may pay more.

Pay What You Can, always

Theatre 118 offers Pay What You Can on every show, whatever the standard price. It is reported as true on the venue, on every show, and alongside the fixed ticket types. When you show a price to an audience, show the minimum and make clear they can pay more.

Conventions

Money is in pence

Every price is an integer number of pence in pounds sterling, never a float. 1400 is £14.00; 150 is £1.50. Divide by 100 for display.

The booking fee is per order

The booking fee is charged once per order, not once per ticket, and applies whether the buyer pays by card or in cash. Add it to the order total, not to each ticket.

Dates and times are local

Dates are plain YYYY-MM-DD and times are 24-hour clock times in Glasgow’s local time (Europe/London). There is no offset on time. Only generated_at carries an ISO 8601 offset.

Read the policy from the response

The Pay What You Can minimum and the booking fee can both change. Read them from the response rather than hard-coding today’s figures.

Refreshing politely

The feed is a snapshot, and sale states move. A five- or ten-minute cache is plenty for a listings page and keeps you well inside the rate limit. A link back to the show’s url is appreciated.

Examples

Flatten every performance into a listing

JavaScript
const response = await fetch('https://theatre118.com/api/v1/shows', {
  headers: { Accept: 'application/json' },
});
const { shows } = await response.json();

const listings = shows.flatMap((show) =>
  show.performances.map((performance) => ({
    title: show.title,
    company: show.company,
    venue: 'Theatre 118',
    date: performance.date,
    time: performance.time,
    soldOut: performance.sold_out,
    url: show.url,
  })),
);

console.log(listings);

Keep only shows with a performance in the next fortnight

JavaScript
const soon = shows.filter((show) =>
  show.performances.some((performance) => {
    const ahead = (new Date(performance.date) - Date.now()) / 86400000;
    return ahead >= 0 && ahead <= 14;
  }),
);

Read the feed from PHP

PHP
use Illuminate\Support\Facades\Http;

$response = Http::acceptJson()
    ->get('https://theatre118.com/api/v1/shows');

foreach ($response->json('shows') as $show) {
    foreach ($show['performances'] as $performance) {
        echo $show['title'].' - '.$performance['date'].PHP_EOL;
    }
}

Read the feed from Python

Python
import requests

response = requests.get(
    "https://theatre118.com/api/v1/shows",
    headers={"Accept": "application/json"},
    timeout=10,
)
response.raise_for_status()

for show in response.json()["shows"]:
    prices = [t["price"] for t in show["ticketing"]["ticket_types"]]
    print(show["title"], "from", min(prices), "pence")

Rate limits & errors

Send Accept: application/json so that every response, including errors, comes back as JSON.

Rate limit

The endpoint allows 120 requests per minute per IP address, which is far more than a listings site needs. Every response carries the usual headers:

Field Type Description
X-RateLimit-Limit integer The ceiling for the window, 120.
X-RateLimit-Remaining integer Requests left in the current window.
X-RateLimit-Reset integer When the window resets, as a Unix timestamp.

Status codes

Field Type Description
200 OK The feed was returned.
404 Not Found The path does not exist. Check the URL.
429 Too Many Requests The rate limit was exceeded. Wait for the Retry-After header (seconds), then try again.
5xx Something went wrong at our end. Retry with a backoff; it is almost always temporary.

There is no authentication, so there are no 401 or 403 responses from this endpoint.

Changelog

Changes are dated and versioned. Additive changes within a version are not breaking; anything that removes or renames a field, or changes its type, gets a new version and a fresh base path.

  1. 15 October 2026 · v1.0

    First public release

    • Added GET /api/v1/shows, unauthenticated and read-only.
    • Venue metadata: name, homepage, Pay What You Can policy and minimum, booking fee.
    • Show detail: prose as plain text, poster and gallery, cast, credits, age rating, content warnings and access notes.
    • Every performance with its local date, curtain time, cancellation and sold-out state.
    • Ticket types and prices in pence, plus a per-show booking link.
    • Press quotes with source, rating, context and link.

Contact

Building something on the feed, need a higher rate limit, or spotted a mistake? Email theatre118glasgow@gmail.com and tell us what you’re making. If you reproduce our listings, a credit to Theatre 118 and a link back to the show page is all we ask.