Run code once on each page load In the past there were some hooks, such as described here:
How to run check on each page load?

to execute arbitrary code on each page load.

Now this has been replaced with an event based approach. I wrote this to check it out:

class MyModuleEventSubscriber implements EventSubscriberInterface {
    public function onLoad(GetResponseEvent $event) {
        \Drupal::logger('my_module')-&gt;info('test');
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() {
        $events[KernelEvents::REQUEST][] = ['onLoad'];

        return $events;
    }
}


However, it seems like there is any number of requests on each page load—it actually captures each request, and in my case there are five (5). My intended code, however, has to call an outside API, and I'd like to avoid unnecessary calls.

Is there an event that is guaranteed to run just once on each page load, without saving a state on my side (that seems quite complicated for this problem)?
You can see an article on the subject here without using Events

https://chromatichq.com/blog/replacing-hookboot-and-hookinit-functionality-drupal-8

Possible that it will meet your needs.


  Replacing hook_boot and hook_init Functionality in Drupal 8
  
  The change notice page states:
  
  Using StackMiddleware
  
  This comment on the hook_boot change notice page provides an example
  of using StackMiddleware. It provides 95% of the functionality needed
  to run logic on cached pages by utilizing a tagged service with the
  http_middleware tag. Since the new class is a service, it will have
  full access to other core and contrib services, allowing for much
  greater functionality. The example shows the following for a module’s
  *.services.yml file:

services:   http_middleware.mymodule:
  class: Drupal\mymodule\StackMiddleware\MyModule
  tags:
    - { name: http_middleware, priority: 180, responder: true }

  
  This is a pretty standard service definition, but note the items added
  to the tags property that register our service with the
  http_middleware tag and also set a priority. In order to bypass the
  page cache, understanding the page_cache.services.yml file is helpful.
  There, a similar definition can be found, but with a higher priority
  value.

services:   http_middleware.page_cache:
    class: Drupal\page_cache\StackMiddleware\PageCache
    arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy']
    tags:
      - { name: http_middleware, priority: 200, responder: true }

  
  Higher priority services are run first. So to trigger logic before the
  page cache module takes over the request, a priority greater than 200
  is needed.

services:   http_middleware.mymodule:
    class: Drupal\mymodule\StackMiddleware\MyModule
    tags:
      - { name: http_middleware, priority: 210, responder: true }

  
  With this change in the services files, and proper setup of the
  service as described by the comment, the http_middleware.mymodule
  service should now be called on every page load, even on fully cached
  pages.

namespace Drupal\example\StackMiddleware;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
 * Performs a custom task.
 */
class ExampleStackMiddleware implements HttpKernelInterface {

  /**
   * The wrapped HTTP kernel.
   *
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
   */
  protected $httpKernel;

  /**
   * Creates a HTTP middleware handler.
   *
   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel
   *   The HTTP kernel.
   */
  public function __construct(HttpKernelInterface $kernel) {
    $this-&gt;httpKernel = $kernel;
  }

  /**
   * {@inheritdoc}
   */
  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
    // Custom logic goes here.

    return $this-&gt;httpKernel-&gt;handle($request, $type, $catch);
  }
}

  
  Verifying the Results
  
  A quick and easy way to test all of this is to simply add
  \Drupal::logger('test')-&gt;notice(‘not cached’); into the functions
  triggered by each of the approaches above. Ensure that the Drupal
  cache is enabled, and simply refresh a page while watching your log
  (drush ws --tail). Then verify the logic is being called as expected.