🚀 A new era: Kirby 4 Get to know
Skip to content

Content from an API

Enrich your site with content fetched from any API

For this example, we use a freely accessible API, in this case the New York Times Movie Review API. To follow this example, create an account and an example app by following the instructions.

First, create a parent page, e.g. reviews in the /content folder and inside it, a reviews.txt text file.

Title: Movie reviews
Intro: This page lists all available movie reviews from the NYT movie review API.

This page will serve as our model for child pages.

The page model

In the Reviews page model, we redefine the children() method to get the subpages from the API instead of from the file system:


use Kirby\Uuid\Uuid;

class ReviewsPage extends Page
    public function children(): Pages
        if ($this->children instanceof Pages) {
            return $this->children;

        $results = [];
        $pages   = [];
        $apiKey  = 'put-your-api-key-here';
        $request = Remote::get('https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=' . $apiKey);

        if ($request->code() === 200) {
            $results = $request->json(false)->results;

        foreach ($results as $key => $review) {
            $pages[] = [
                'slug'     => Str::slug($review->display_title),
                'num'      => $key+1,
                'template' => 'review',
                'model'    => 'review',
                'content'  => [
                    'title'    => $review->display_title,
                    'headline' => $review->headline,
                    'byline'   => $review->byline,
                    'summary'  => $review->summary_short,
                    'date'     => $review->publication_date,
                    'link'     => $review->link->url,
                    'linkText' => $review->link->suggested_link_text,
                    'cover'    => $review->multimedia->src,
                    'uuid'     => Uuid::generate(),


        return $this->children = Pages::factory($pages, $this);

Replace api-key with the API key you got for your app.

Unless you have disabled UUIDs in your config, you have to pass a uuid field in the content array to prevent Kirby from generating the page in the file system when the $page->uuid() method is called.

If you generate the UUIDs automatically like in the example above, they will change at every load. However, if you want to reference your virtual pages anywhere with their UUID, make sure to use a unique string that does not change.

Using the Remote::get() method, you connect to the API and fetch the results. Within the foreach loop, you feed the results into the $pages array and finally pass it all to the Pages::factory() method.

The overview template

The pages are now accessible in the template and you can loop through them like through a normal set of Kirby pages, using the fields defined in the content array:

<?php snippet('header') ?>

    <h1><?= $page->title() ?></h1>
    <?php foreach ($page->children() as $review): ?>
      <h2><?= $review->title() ?></h2>
      <a href="<?= $review->url() ?>">Read review summary</a>
    <?php endforeach ?>

<?php snippet('footer') ?>

The child page template

For the children themselves, you can create their own review.php template to access more details:

<?php snippet('header') ?>

  <article class="review">
      <h1><?= $page->title() ?></h1>
      <time><?= $page->date()->toDate('d F Y') ?></time>

    <h2><?= $page->headline() ?></h2>
    <p>by <?= $page->byline() ?></p>
    <?= $page->summary() ?>

    <?php if ($page->cover()->isNotEmpty()): ?>
    <img src="<?= $page->cover() ?>" alt="">
    <?php endif ?>

<?php snippet('footer') ?>

The result

The result could then look something like this: