Show a cinema’s showtimes on WordPress

A small WordPress plugin with a shortcode that displays a cinema’s programme for the coming days, kept up to date and cached, in about 80 lines of PHP.

3 min read

On this page
  1. What you need
  2. 1.Store the key in wp-config.php
  3. 2.Create the plugin
  4. 3.How it works
  5. 4.Use it
  6. 5.A little style
  7. Going further

Cinemas, film societies, local news sites and tourism offices often run WordPress and want to show a programme that stays up to date by itself. This tutorial builds a small plugin that adds a shortcode:

[cinema_showtimes cinema="le-champo-paris-5e-arrondissement"]

It displays the cinema's showtimes for the coming days, film by film, each time linking to the cinema's booking page. About 80 lines of PHP, no dependency.

What you need

  • A WordPress site where you can add a plugin (FTP, SFTP or the hosting file manager).
  • An API Cinéma key: create a free account, confirm your e-mail address, then create a key from the dashboard. The free plan (150 requests a week) is enough to try it or for a non-commercial site, thanks to the cache below; a cinema's or business website needs a paid plan, from €12 a month.
  • The slug of the cinema. Search it by name:
Terminal
curl "https://api.api-cinema.com/v1/cinemas?q=champo" -H "Authorization: Bearer ac_live_…"

The answer gives "slug": "le-champo-paris-5e-arrondissement". Any French or US cinema of the API works the same way.

Store the key in wp-config.php

The key must stay on the server. Add this line to wp-config.php, above /* That's all, stop editing! */:

PHP
define('API_CINEMA_KEY', 'ac_live_…');

Create the plugin

Create the folder wp-content/plugins/api-cinema-showtimes/ and, inside it, the file api-cinema-showtimes.php:

PHP
<?php
/**
 * Plugin Name: API Cinéma showtimes
 * Description: [cinema_showtimes cinema="le-champo-paris-5e-arrondissement"] shows a cinema's programme for the coming days.
 * Version: 1.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

add_shortcode('cinema_showtimes', 'api_cinema_showtimes_shortcode');

function api_cinema_showtimes_shortcode($atts)
{
    $atts = shortcode_atts(['cinema' => '', 'days' => 7], $atts, 'cinema_showtimes');
    $cinema = sanitize_title($atts['cinema']);
    $days = max(1, min(14, (int) $atts['days']));
    if ($cinema === '' || !defined('API_CINEMA_KEY')) {
        return '';
    }

    $showtimes = api_cinema_fetch_showtimes($cinema, $days);
    if ($showtimes === null) {
        return '<p class="cinema-showtimes">Programme unavailable for now.</p>';
    }
    if (!$showtimes) {
        return '<p class="cinema-showtimes">No showtimes in the coming days.</p>';
    }
    return api_cinema_render_showtimes($showtimes);
}

/** Upcoming showtimes of a cinema, cached two hours; null when the API cannot be reached. */
function api_cinema_fetch_showtimes($cinema, $days)
{
    $cache_key = 'api_cinema_' . md5($cinema . '|' . $days);
    $cached = get_transient($cache_key);
    if ($cached !== false) {
        return $cached;
    }

    $url = add_query_arg([
        'to' => wp_date('Y-m-d', strtotime('+' . ($days - 1) . ' days')),
        'include' => 'movie',
        'limit' => 1000,
    ], 'https://api.api-cinema.com/v1/cinemas/' . rawurlencode($cinema) . '/showtimes');

    $response = wp_remote_get($url, [
        'headers' => ['Authorization' => 'Bearer ' . API_CINEMA_KEY],
        'timeout' => 10,
    ]);
    if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
        return null;
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);
    $showtimes = $body['data'] ?? [];
    set_transient($cache_key, $showtimes, 2 * HOUR_IN_SECONDS);
    return $showtimes;
}

/** Day by day, film by film, each time linking to the booking page when there is one. */
function api_cinema_render_showtimes($showtimes)
{
    $days = [];
    foreach ($showtimes as $s) {
        $day = substr($s['startsAt'], 0, 10);
        $film = $s['movie']['title'];
        $days[$day][$film][] = $s;
    }

    $html = '<div class="cinema-showtimes">';
    foreach ($days as $day => $films) {
        $html .= '<h3>' . esc_html(wp_date('l j F', strtotime($day . 'T12:00:00'))) . '</h3><ul>';
        foreach ($films as $film => $times) {
            $html .= '<li><strong>' . esc_html($film) . '</strong> ';
            foreach ($times as $s) {
                $label = substr($s['startsAt'], 11, 5) . ($s['version'] !== 'VF' ? ' ' . $s['version'] : '');
                $html .= $s['bookingUrl']
                    ? '<a href="' . esc_url($s['bookingUrl']) . '" rel="nofollow noopener" target="_blank">' . esc_html($label) . '</a> '
                    : '<span>' . esc_html($label) . '</span> ';
            }
            $html .= '</li>';
        }
        $html .= '</ul>';
    }
    return $html . '</div>';
}

Then activate API Cinéma showtimes in Plugins.

How it works

  • One request every two hours at most. The answer is kept in a transient for two hours, so a page viewed 10,000 times a day still costs 12 requests a day, 84 a week: within the free plan.
  • to and no from. Without from, the API starts from now in the cinema's local time, so screenings already started are left out; to=2026-10-28 includes the whole of that day.
  • include=movie. The cinema is the same on every row: embedding only the film makes the answer lighter.
  • Local times. startsAt is the cinema's wall-clock time (2026-10-24T20:30:00): the plugin shows it as it is, whatever the server's time zone.
  • Versions. VF (dubbed in French) is the default in France and is not shown; VO and VOST are added next to the time. For a US theater, every time shows VO: change the test to !in_array($s['version'], ['VF', 'VO'], true) to hide it.
  • Escaping. Titles and links come from the cinemas' own websites: esc_html() and esc_url() keep them harmless.

Use it

In a page, a post or a Shortcode block:

[cinema_showtimes cinema="le-champo-paris-5e-arrondissement" days="3"]

days goes from 1 to 14 (7 by default). The output looks like this:

HTML
<div class="cinema-showtimes">
  <h3>Thursday 22 October</h3>
  <ul>
    <li><strong>Cris et Chuchotements</strong> <a href="…">11:50 VOST</a> <a href="…">21:50 VOST</a></li>
    <li><strong>Sonate d'automne</strong> <a href="…">13:40 VOST</a> …</li>
  </ul>
</div>

The day names follow the site language set in Settings › General.

A little style

Add to Appearance › Customize › Additional CSS:

CSS
.cinema-showtimes ul { list-style: none; padding: 0; }
.cinema-showtimes li { margin: 0.4em 0; }
.cinema-showtimes a, .cinema-showtimes span {
  display: inline-block; margin: 0.15em 0.3em 0.15em 0; padding: 0.15em 0.5em;
  border: 1px solid currentColor; border-radius: 4px; text-decoration: none; font-variant-numeric: tabular-nums;
}

Going further

  • Several cinemas: the shortcode works any number of times on a page; each cinema has its own cache.
  • Posters: $s['movie']['posterUrl'] holds the film's poster, durationMin its running time, synopsis its summary.
  • Every cinema of a town: call /v1/showtimes?city=… or near=lat,lng instead of a single cinema; see the guide for France.

Every field is described in the documentation.