On this page
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
slugof the cinema. Search it by name:
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! */:
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
/**
* 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.
toand nofrom. Withoutfrom, the API starts from now in the cinema's local time, so screenings already started are left out;to=2026-10-28includes 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.
startsAtis 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;VOandVOSTare added next to the time. For a US theater, every time showsVO: 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()andesc_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:
<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:
.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,durationMinits running time,synopsisits summary. - Every cinema of a town: call
/v1/showtimes?city=…ornear=lat,lnginstead of a single cinema; see the guide for France.
Every field is described in the documentation.