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.
Quick start
Fetch the show list and read the first title back out:
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:
{
"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
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
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
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
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
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.
-
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.
- Added
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.