{
  "openapi": "3.0.1",
  "info": {
    "title": "UK Cinema API",
    "description": "Your affordable and comprehensive solution for UK cinema, showtimes, and movies data.",
    "contact": {
      "name": "UK Cinema API",
      "url": "https://uk-cinema-api.co.uk",
      "email": "support@uk-cinema-api.co.uk"
    },
    "version": "3.1.0"
  },
  "servers": [
    {
      "url": "https://uk-cinema-api.co.uk/api/v3/",
      "description": "Base URL"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "films",
      "description": "Operations about films"
    },
    {
      "name": "cinemas",
      "description": "Operations about cinemas"
    },
    {
      "name": "showtimes",
      "description": "Operations about showtimes"
    },
    {
      "name": "attributes",
      "description": "Operations about showtime attributes"
    },
    {
      "name": "chains",
      "description": "Operations about cinema chains"
    }
  ],
  "paths": {
    "/films": {
      "get": {
        "tags": ["films"],
        "description": "Get a paginated list of films currently showing in cinemas.",
        "summary": "Get films",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page offset to fetch.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "items",
            "in": "query",
            "description": "Number of items to return per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cinema_id",
            "in": "query",
            "description": "Filter by a specific cinema.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "include_outdated",
            "in": "query",
            "description": "Include films that are no longer showing in cinemas.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "search_query",
            "in": "query",
            "description": "Search for films by title, director name, TMDB ID, or IMDB ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tmdb_id",
            "in": "query",
            "description": "Filter by a specific TMDB ID. Example: `603`",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "imdb_id",
            "in": "query",
            "description": "Filter by a specific IMDB ID. Example: `tt0133093`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns films with pagination metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilmsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/films/{id}": {
      "get": {
        "tags": ["films"],
        "description": "Returns a film.",
        "summary": "Get film",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Film ID.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a film.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Film"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/cinemas": {
      "get": {
        "tags": ["cinemas"],
        "description": "Get a paginated list of cinemas.",
        "summary": "Get cinemas",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page offset to fetch.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "items",
            "in": "query",
            "description": "Number of items to return per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns cinemas with pagination metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CinemasResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/cinemas/{id}": {
      "get": {
        "tags": ["cinemas"],
        "description": "Return a cinema.",
        "summary": "Get cinema",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Cinema ID.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a cinema.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cinema"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/cinemas/{chain}/external/{external_id}": {
      "get": {
        "tags": ["cinemas"],
        "description": "Get a cinema by cinema chain and the chain's native external ID.",
        "summary": "Get cinema by chain and external ID",
        "parameters": [
          {
            "name": "chain",
            "in": "path",
            "description": "The cinema chain identifier.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ChainEnum"
            }
          },
          {
            "name": "external_id",
            "in": "path",
            "description": "The cinema chain's native cinema identifier.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a cinema matching the external ID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cinema"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/showtimes": {
      "get": {
        "tags": ["showtimes"],
        "description": "Get a paginated list of showtimes.",
        "summary": "Get showtimes",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page offset to fetch.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "items",
            "in": "query",
            "description": "Number of items to return per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Switches this request to cursor pagination.\n\nSend an empty `cursor` to start, then pass **`meta.next_cursor`** from each response as the `cursor` for the next request. The walk is complete when `next_cursor` is `null`.\n\nCursor pagination is stable when showtimes are added or removed during a walk. However, if a showtime's **`showing_at`** changes, it may move position and therefore be returned twice or not at all.\n\nWhen cursor pagination is used, `meta` contains `next_cursor` instead of `page`. The `page` parameter is ignored.",
            "schema": {
              "type": "string"
            },
            "example": ""
          },
          {
            "name": "cinema_id",
            "in": "query",
            "description": "Filter by a specific cinema.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "film_id",
            "in": "query",
            "description": "Filter by a specific film.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "tmdb_id",
            "in": "query",
            "description": "Filter by a specific TMDB ID.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "imdb_id",
            "in": "query",
            "description": "Filter by a specific IMDB ID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns showtimes with pagination metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShowtimesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequestError"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/showtimes/{id}": {
      "get": {
        "tags": ["showtimes"],
        "description": "Return a showtime.",
        "summary": "Get showtime",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Showtime ID.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a showtime.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Showtime"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/showtimes/{chain}/external/{external_id}": {
      "get": {
        "tags": ["showtimes"],
        "description": "Get a showtime by cinema chain and the chain's native external ID.",
        "summary": "Get showtime by chain and external ID",
        "parameters": [
          {
            "name": "chain",
            "in": "path",
            "description": "The cinema chain identifier.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ChainEnum"
            }
          },
          {
            "name": "external_id",
            "in": "path",
            "description": "The cinema chain's native showtime identifier.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a showtime matching the external ID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Showtime"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/attributes": {
      "get": {
        "tags": ["attributes"],
        "description": "Get all available showtime attribute codes with their descriptions and categories.",
        "summary": "Get showtime attributes",
        "responses": {
          "200": {
            "description": "Returns all available showtime attributes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttributesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/chains": {
      "get": {
        "tags": ["chains"],
        "description": "Get a list of all cinema chains that have at least one cinema, with cinema counts.",
        "summary": "Get chains",
        "responses": {
          "200": {
            "description": "Returns chains with metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChainsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/chains/{id}": {
      "get": {
        "tags": ["chains"],
        "description": "Returns a single cinema chain by its identifier.",
        "summary": "Get chain",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Chain identifier.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ChainEnum"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a chain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chain"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/chains/{id}/cinemas": {
      "get": {
        "tags": ["chains"],
        "description": "Get a paginated list of cinemas belonging to a specific chain.",
        "summary": "Get chain cinemas",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Chain identifier.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ChainEnum"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page offset to fetch.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "items",
            "in": "query",
            "description": "Number of items to return per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns cinemas with pagination metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CinemasResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenError"
          },
          "404": {
            "$ref": "#/components/responses/NotFoundError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitError"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "ChainEnum": {
        "type": "string",
        "enum": [
          "abbey_abingdon",
          "aberystwyth_arts_centre",
          "abbeygate_cinema",
          "act_one_cinema",
          "aldeburgh_cinema",
          "alnwick_playhouse",
          "apollo_leisure",
          "arc_cinema",
          "arthouse_crouch_end",
          "ashford_cinema",
          "baker_street_abergavenny",
          "barbican",
          "beam_hertford",
          "bijou_southport",
          "birks_cinema",
          "bishop_auckland_town_hall",
          "bfi",
          "bonington_theatre",
          "breweryarts",
          "broadway",
          "broadway_douglas",
          "broadway_letchworth",
          "brynamman_cinema",
          "brunswick_movie_bowl",
          "buxton_opera_house",
          "campbeltown_picture_house",
          "campus_west",
          "castle_cinema",
          "castle_theatre",
          "cellb",
          "central_cinema_fakenham",
          "chalmers_group",
          "chapter",
          "chiswick_cinema",
          "dorking_halls",
          "chichester_cinema",
          "chipping_norton_theatre",
          "cine_lumiere",
          "cinebowl",
          "cinema_co",
          "cinema_museum",
          "cinemac",
          "close_up_film_centre",
          "commodore_cinema",
          "commodore_ryde",
          "corn_exchange",
          "corn_exchange_newbury",
          "cineworld",
          "coliseum_brecon",
          "courtyard",
          "cromarty_cinema",
          "cross_hands_cinema",
          "cultplex",
          "cube_cinema",
          "curzon",
          "curzon_clevedon",
          "customs_house",
          "dartington",
          "david_lean_cinema",
          "derby_quad",
          "dominion_cinema",
          "dukeslancaster",
          "dundee_contemporary_arts",
          "east_coast_cinema",
          "eastgate_theatre",
          "eden_court",
          "electric_cinema",
          "elite_ashbourne",
          "em_forster_theatre",
          "electric_palace",
          "electric_palace_cannock",
          "electric_palace_hastings",
          "exeter_phoenix",
          "plymouth_arts_cinema",
          "glasgow_science_centre_imax",
          "empire_consett",
          "empire_holyhead",
          "empire_sandwich",
          "empire_theatre",
          "everyman",
          "farnham_maltings",
          "festival_drayton_centre",
          "filmhouse",
          "firstsite",
          "flower_bowl",
          "fraser_centre",
          "forum_bridlington",
          "forum_hexham",
          "futurist_cinema",
          "gaiety_cinema",
          "gala_durham",
          "galeri_caernarfon",
          "hebden_bridge",
          "helensburgh_cinema",
          "garden_cinema",
          "genesis_cinema",
          "glasgow_film_theatre",
          "gloucester_guildhall",
          "grove_theatre",
          "gulbenkian_cinema",
          "gwyn_hall",
          "hailsham_pavilion",
          "heart_of_hawick",
          "hpph",
          "highland_cinema",
          "hippodrome_cinema",
          "home_mcr",
          "ica",
          "jam_jar_cinema",
          "junction_goole",
          "jw3",
          "kavanagh_cinema",
          "imc_cinema",
          "ilkley_cinema",
          "keswick_alhambra",
          "kiln_theatre",
          "king_street",
          "kings_cinema_newmarket",
          "kings_lynn_corn_exchange",
          "kirkgate_arts",
          "kinema_in_the_woods",
          "kino_cinema",
          "kino_digital",
          "kino_teatr",
          "lanternhouse_cinema",
          "las_portree",
          "leatherhead_theatre",
          "leigh_film_factory",
          "leiston_film_theatre",
          "lewes_depot",
          "libanus_1877",
          "lexi_cinema",
          "mallard_cinema",
          "malt_cinema",
          "maltings_berwick",
          "maona_cinema",
          "market_pavilion",
          "marina_theatre",
          "market_hall_cinema",
          "lonsdale_alhambra",
          "lonsdale_annan",
          "magic_lantern",
          "lowther_pavilion",
          "lighthousepoole",
          "living_room_cinema",
          "loewen_cinema",
          "ludlow_assembly_rooms",
          "lumiere",
          "macrobert_arts_centre",
          "malvern_theatres",
          "lynton_cinema",
          "malton_palace",
          "merlin",
          "metro_cinema",
          "midlands_arts_centre",
          "mk_gallery",
          "mockingbird",
          "monmouth_savoy",
          "montrose_playhouse",
          "moray_playhouse",
          "movie_starr",
          "movie_house",
          "neo_cinema_woolacombe",
          "new_picture_house",
          "newcastle_community_cinema",
          "newlyn_filmhouse",
          "nickel_cinema",
          "newton_stewart_cinema",
          "northampton_filmhouse",
          "northern_light",
          "northern_morris",
          "nova_cinema",
          "number_30_huntly",
          "number_8_pershore",
          "odeon_gb",
          "old_market_hall",
          "odyssey_cinema",
          "totnes_cinema",
          "omniplex",
          "olympic_studios",
          "orion",
          "oxford_cinema",
          "palace_broadstairs",
          "palace_cinderford",
          "palace_douglas",
          "palace_haverfordwest",
          "palace_cinemas",
          "parade_cinema",
          "parkway_cinemas",
          "peckhamplex",
          "pdj_cinemas",
          "penarth_pier_pavilion",
          "penistone_paramount",
          "phoenix_cinema",
          "phoenix_leicester",
          "piccadilly_leicester",
          "pickaquoy",
          "picturedrome",
          "picturehouse",
          "plaza_cinema",
          "pontardawe_arts_centre",
          "pontio",
          "regal_evesham",
          "regal_fordingbridge",
          "regal_melton",
          "regal_stowmarket",
          "regal_tenbury",
          "regent_centre",
          "regent_cinema",
          "regent_newtown",
          "regent_street_cinema",
          "rex",
          "rosehill_theatre",
          "rex_wareham",
          "rex_wilmslow",
          "rheged",
          "ritz",
          "ritz_cookstown",
          "ritz_thirsk",
          "ritz_workington",
          "the_riverside",
          "theatr_gwaun",
          "tower_cinema",
          "towner_cinema",
          "prince_charles",
          "queens_film_theatre",
          "rct_theatres",
          "reading_biscuit_factory",
          "rebel_cinema",
          "red_carpet_cinema",
          "reel",
          "rich_mix",
          "rio_burnham",
          "rio_cinema",
          "riverfront_newport",
          "riverside_studios",
          "robert_burns_centre",
          "rooftop_cinema_club",
          "roses_theatre",
          "roxy_movies",
          "royal_faversham",
          "royal_spa_centre",
          "royalty_bowness",
          "saffron_screen",
          "sandb",
          "sandpit_theatre",
          "savoy",
          "scala_ilkeston",
          "science_and_media_museum",
          "science_museum_imax",
          "savoy_heaton_moor",
          "scott_cinema",
          "scotsman_group",
          "shaftesbury_arts_centre",
          "sherborne_cinema",
          "sheringham_little_theatre",
          "shetlandarts",
          "showcase",
          "showroom_cinema",
          "silver_screen_folkestone",
          "south_holland_centre",
          "southhill_park",
          "sjt",
          "st_georges_theatre",
          "spey_valley_cinema",
          "stag_theatre",
          "stamford_arts_centre",
          "star_and_shadow",
          "station_cinema",
          "storyhouse",
          "taliesin_arts_centre",
          "tavistock_wharf",
          "the_arzner",
          "the_atrium",
          "the_film_place",
          "the_flavel",
          "the_light",
          "theatr_clwyd",
          "theatr_colwyn",
          "theatr_mwldan",
          "the_poly",
          "torch_theatre",
          "tyneside",
          "uckfield_picture_house",
          "upp",
          "vault_cinema",
          "vue",
          "warwick_arts_centre",
          "watermans",
          "watershed",
          "wellington_orbit",
          "wells_film_centre",
          "wem_town_hall",
          "west_coast_cinemas",
          "wetherby_cinema",
          "weymouth_pavilion",
          "windmill_cinema",
          "winter_garden_bute",
          "wtw_cinema",
          "worthing_theatres",
          "wotton_electric_picture_house",
          "wyeside",
          "wyllyotts_theatre",
          "york_student_cinema",
          "zeffirellis"
        ]
      },
      "MetaInfo": {
        "type": "object",
        "required": ["total_count"],
        "properties": {
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "per_page": {
            "type": "integer",
            "description": "Number of items per page"
          },
          "total_count": {
            "type": "integer",
            "description": "Total number of items across all pages"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque cursor for the next page, or null on the last page. Present only when the request used cursor pagination. Pass it back as `cursor` to fetch the next page."
          }
        }
      },
      "Cinema": {
        "type": "object",
        "required": [
          "id",
          "chain",
          "name",
          "address",
          "latitude",
          "longitude",
          "link",
          "google_place_id",
          "closed"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "chain": {
            "$ref": "#/components/schemas/ChainEnum"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "latitude": {
            "type": "number",
            "format": "float"
          },
          "longitude": {
            "type": "number",
            "format": "float"
          },
          "link": {
            "type": "string"
          },
          "google_place_id": {
            "type": "string",
            "nullable": true
          },
          "closed": {
            "type": "boolean",
            "description": "Whether the cinema is permanently closed."
          }
        },
        "additionalProperties": true,
        "example": {
          "id": 495,
          "chain": "odeon_gb",
          "name": "London West End",
          "address": "38 Leicester Square, London, WC2H 7DX",
          "latitude": 51.5104,
          "longitude": -0.1293,
          "link": "https://www.odeon.co.uk/cinemas/london-west-end",
          "google_place_id": "ChIJc2nSALkEdkgRkuoJJBfzkUI",
          "closed": false
        }
      },
      "CinemasResponse": {
        "type": "object",
        "required": ["meta", "data"],
        "properties": {
          "meta": {
            "$ref": "#/components/schemas/MetaInfo"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Cinema"
            }
          }
        },
        "example": {
          "meta": {
            "page": 1,
            "per_page": 20,
            "total_count": 562
          },
          "data": [
            {
              "id": 495,
              "chain": "odeon_gb",
              "name": "London West End",
              "address": "38 Leicester Square, London, WC2H 7DX",
              "latitude": 51.5104,
              "longitude": -0.1293,
              "link": "https://www.odeon.co.uk/cinemas/london-west-end",
              "google_place_id": "ChIJc2nSALkEdkgRkuoJJBfzkUI",
              "closed": false
            }
          ]
        }
      },
      "Chain": {
        "type": "object",
        "required": ["id", "name", "cinemas_count"],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ChainEnum"
          },
          "name": {
            "type": "string"
          },
          "cinemas_count": {
            "type": "integer",
            "format": "int32",
            "description": "Number of cinemas belonging to this chain."
          }
        },
        "additionalProperties": true,
        "example": {
          "id": "arc_cinema",
          "name": "Arc Cinema",
          "cinemas_count": 8
        }
      },
      "ChainsResponse": {
        "type": "object",
        "required": ["meta", "data"],
        "properties": {
          "meta": {
            "$ref": "#/components/schemas/MetaInfo"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Chain"
            }
          }
        },
        "example": {
          "meta": {
            "total_count": 51
          },
          "data": [
            {
              "id": "arc_cinema",
              "name": "Arc Cinema",
              "cinemas_count": 8
            }
          ]
        }
      },
      "Film": {
        "type": "object",
        "required": [
          "id",
          "title",
          "original_title",
          "events_count",
          "imdb_id",
          "tmdb_id",
          "release_year",
          "runtime"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "title": {
            "type": "string"
          },
          "original_title": {
            "type": "string"
          },
          "events_count": {
            "type": "integer",
            "format": "int32"
          },
          "imdb_id": {
            "type": "string",
            "nullable": true
          },
          "tmdb_id": {
            "type": "integer",
            "nullable": true
          },
          "release_year": {
            "type": "integer",
            "nullable": true
          },
          "runtime": {
            "type": "integer",
            "nullable": true
          }
        },
        "additionalProperties": true,
        "example": {
          "id": 296557755,
          "title": "Scream 7",
          "original_title": "Scream 7",
          "events_count": 17308,
          "imdb_id": "tt27047903",
          "tmdb_id": 1159559,
          "release_year": 2026,
          "runtime": 114
        }
      },
      "FilmsResponse": {
        "type": "object",
        "required": ["meta", "data"],
        "properties": {
          "meta": {
            "$ref": "#/components/schemas/MetaInfo"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Film"
            }
          }
        },
        "example": {
          "meta": {
            "page": 1,
            "per_page": 20,
            "total_count": 1116
          },
          "data": [
            {
              "id": 296557755,
              "title": "Scream 7",
              "original_title": "Scream 7",
              "events_count": 17308,
              "imdb_id": "tt27047903",
              "tmdb_id": 1159559,
              "release_year": 2026,
              "runtime": 114
            }
          ]
        }
      },
      "Showtime": {
        "type": "object",
        "required": [
          "id",
          "cinema_id",
          "film_id",
          "showing_at",
          "booking_link",
          "movie_page_link",
          "sold_out",
          "chain",
          "latitude",
          "longitude",
          "on_sale_from",
          "on_sale_from_verified",
          "auditorium",
          "attribute_codes"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int32"
          },
          "cinema_id": {
            "type": "integer",
            "format": "int32"
          },
          "film_id": {
            "type": "integer",
            "format": "int32"
          },
          "showing_at": {
            "type": "string",
            "format": "date-time",
            "description": "Local time at the cinema, as ISO 8601 with its UTC offset: +00:00 in winter and +01:00 in summer."
          },
          "sold_out": {
            "type": "boolean"
          },
          "booking_link": {
            "type": "string",
            "nullable": true,
            "description": "Direct link to book this performance. Null when online booking is not available."
          },
          "movie_page_link": {
            "type": "string",
            "nullable": true,
            "description": "The cinema's page for this film, where available. May provide film information and ticketing, and can be used as a fallback when booking_link is null."
          },
          "chain": {
            "$ref": "#/components/schemas/ChainEnum"
          },
          "latitude": {
            "type": "number",
            "format": "float"
          },
          "longitude": {
            "type": "number",
            "format": "float"
          },
          "on_sale_from": {
            "type": "string",
            "format": "date-time",
            "description": "The venue's on-sale time when known, otherwise the time the film was first observed."
          },
          "on_sale_from_verified": {
            "type": "boolean",
            "description": "Whether on_sale_from was provided by the venue."
          },
          "auditorium": {
            "type": "string",
            "nullable": true,
            "description": "The screen or auditorium name/number where the film is showing."
          },
          "attribute_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Array of attribute codes describing format, accessibility, and event type (e.g., 'imax', '3d', 'audio-described')."
          }
        },
        "additionalProperties": true,
        "example": {
          "id": 24768133,
          "cinema_id": 495,
          "film_id": 496804583,
          "showing_at": "2026-03-08T20:40:00+00:00",
          "booking_link": "https://www.odeon.co.uk/ticketing/seat-picker/?showtimeId=155-12801",
          "movie_page_link": null,
          "sold_out": false,
          "chain": "odeon_gb",
          "latitude": 51.5104,
          "longitude": -0.1293,
          "on_sale_from": "2025-12-11T02:04:41+00:00",
          "on_sale_from_verified": false,
          "auditorium": "2",
          "attribute_codes": ["imax"]
        }
      },
      "ShowtimesResponse": {
        "type": "object",
        "description": "Showtimes with pagination metadata. In cursor mode `meta` carries `next_cursor` and omits `page`.",
        "required": ["meta", "data"],
        "properties": {
          "meta": {
            "$ref": "#/components/schemas/MetaInfo"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Showtime"
            }
          }
        },
        "example": {
          "meta": {
            "page": 1,
            "per_page": 20,
            "total_count": 156310
          },
          "data": [
            {
              "id": 24768133,
              "cinema_id": 495,
              "film_id": 496804583,
              "showing_at": "2026-03-08T20:40:00+00:00",
              "booking_link": "https://www.odeon.co.uk/ticketing/seat-picker/?showtimeId=155-12801",
              "movie_page_link": null,
              "sold_out": false,
              "chain": "odeon_gb",
              "latitude": 51.5104,
              "longitude": -0.1293,
              "on_sale_from": "2025-12-11T02:04:41+00:00",
              "on_sale_from_verified": false,
              "auditorium": "2",
              "attribute_codes": ["imax"]
            },
            {
              "id": 28324938,
              "cinema_id": 1231,
              "film_id": 579248882,
              "showing_at": "2026-08-20T20:00:00+01:00",
              "booking_link": null,
              "movie_page_link": "https://lyntoncinema.co.uk/movie/details/69a99274a0b75565827e2f43",
              "sold_out": false,
              "chain": "lynton_cinema",
              "latitude": 51.230407,
              "longitude": -3.83687,
              "on_sale_from": "2026-08-19T22:07:26+01:00",
              "on_sale_from_verified": false,
              "auditorium": null,
              "attribute_codes": []
            }
          ]
        }
      },
      "Attribute": {
        "type": "object",
        "required": ["id", "title", "description", "category"],
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the attribute (e.g., 'imax', '3d', 'audio-described')."
          },
          "title": {
            "type": "string",
            "description": "Human-readable title (e.g., 'IMAX', '3D', 'Audio Described')."
          },
          "description": {
            "type": "string",
            "description": "Description of what the attribute means."
          },
          "category": {
            "type": "string",
            "enum": ["accessibility", "event", "format"],
            "description": "Category of the attribute."
          }
        }
      },
      "AttributesResponse": {
        "type": "object",
        "required": ["meta", "data"],
        "properties": {
          "meta": {
            "$ref": "#/components/schemas/MetaInfo"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attribute"
            }
          }
        },
        "example": {
          "meta": {
            "total_count": 43
          },
          "data": [
            {
              "id": "imax",
              "title": "IMAX",
              "description": "Large-format IMAX presentation",
              "category": "format"
            }
          ]
        }
      },
      "APIError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "UnauthorizedError": {
        "description": "Unauthorized – the request requires valid authentication.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            },
            "example": {
              "status": 401,
              "message": "Unauthorized"
            }
          }
        }
      },
      "ForbiddenError": {
        "description": "Forbidden – authentication succeeded but access is denied (e.g., expired subscription).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            },
            "example": {
              "status": 403,
              "message": "Your subscription has expired. Please visit https://uk-cinema-api.co.uk/pricing"
            }
          }
        }
      },
      "RateLimitError": {
        "description": "Too Many Requests – you have exceeded the rate limit.",
        "headers": {
          "Retry-After": {
            "description": "Number of seconds to wait before making another request.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            },
            "example": {
              "status": 429,
              "message": "Rate limit exceeded. Maximum 5 requests per second."
            }
          }
        }
      },
      "NotFoundError": {
        "description": "Not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            },
            "example": {
              "status": 404,
              "message": "Not found"
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal Server Error – something went wrong on our end.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            },
            "example": {
              "status": 500,
              "message": "Internal server error"
            }
          }
        }
      },
      "BadRequestError": {
        "description": "Bad request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/APIError"
            },
            "example": {
              "status": 400,
              "message": "Invalid cursor"
            }
          }
        }
      }
    }
  }
}
