How to migrate content using a web service with multiple calls?
There is an issue with the UrlPerRow plugin above. 

The source data isn't populated until after the check for whether the source data has changed. Because of that fact, it's always looking at previous data, which means the hash will never change, so "rowChanged()" never returns true. It will import new data but not update if the data changes. 

A fixed version is below:

&lt;?php

namespace Drupal\fad_migration\Plugin\migrate\source;

use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
use Drupal\migrate_plus\Plugin\migrate\source\Url;
use Drupal\migrate\Row;

/**
 *
 * @MigrateSource(
 *   id = "url_per_row"
 * )
 */
class UrlPerRow extends Url {

  protected $dataNewParserPlugin;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
  }

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    $prepare_row = parent::prepareRow($row);

    $item_info_settings = $row-&gt;getSourceProperty('item_info');
    if (!empty($item_info_settings)) {
      $this-&gt;getItemData($row, $item_info_settings);
    }

    if (!$row-&gt;getIdMap() || $row-&gt;needsUpdate() || $this-&gt;aboveHighwater($row) || $this-&gt;rowChanged($row)) {
      return $prepare_row;
    }
    else {
      return false;
    }

  }

  protected function getItemData(Row &amp;$row, $settings) {

    $new_url = str_replace($settings['token'], $row-&gt;getSourceProperty($settings['token_replace']), $settings['url']);
    $this-&gt;getNewDataParserPlugin($new_url, $settings['item_selector'], $row-&gt;getSource());
    $this-&gt;dataNewParserPlugin-&gt;rewind();
    $xml_data = $this-&gt;dataNewParserPlugin-&gt;current();
    if (!empty($xml_data)) {
      $fields = $row-&gt;getSourceProperty('fields');
      foreach ($fields as $field) {
        if (!empty($field['item_info']) &amp;&amp; !empty($xml_data[$field['name']])) {
          $row-&gt;setSourceProperty($field['name'], $xml_data[$field['name']]);
        }
      }
    }    
  }

  protected function getNewDataParserPlugin($url, $item_selector, $settings) {
    $settings['urls']          = [$url];
    $settings['item_selector'] = $item_selector;

    $this-&gt;dataNewParserPlugin = \Drupal::service('plugin.manager.migrate_plus.data_parser')
                                        -&gt;createInstance($settings['data_parser_plugin'], $settings);

    return $this-&gt;dataNewParserPlugin;
  }

}