diff --git a/README.txt b/README.txt
index 2365e1a..50108e3 100644
--- a/README.txt
+++ b/README.txt
@@ -170,6 +170,37 @@ created above. The user interface will also be the same, thus allowing you to
 define the start and end date, not a number.
 
 
+Migrate Integration (experimental)
+----------------------------------
+Availability Calendar embeds a Migrate field handler
+(https://www.drupal.org/node/1429096), which helps you migrate availability data
+from external sources. In order to use this feature:
+
+1. Your source field has to be declared and mapped in your migration class.
+
+2. Availability Calendar expects the data to be in a precise format. Depending
+   on your source data, you will probably have to alter them in the prepareRow()
+   method, so that they comply with the following format:
+
+     2,2016-01-21,2016-01-23
+     4,2016-02-12,2016-02-27
+     3,2016-03-10,2016-04-24
+     1,2016-06-09,2016-06-11
+
+   These are actually availability changes expressed in relation to the previous
+   state of the calendar (the default state being "not communicated"). The state
+   code is in the first column (columns are separated by commas); those are the
+   states specified in the availability_calendar_state table when the module is
+   installed. Beware that this data is dynamic (see interface at
+   admin/config/content/availability-calendar). Here, 2 means "available" and 3
+   "booked".
+
+Note that this field handler will not import expired intervals.
+
+For more information and an example of an actual migration, see this blog post:
+https://www.res-telae.cat/en/import-availability-data-availability-calendars-external-source-migrate#integrate-migrate
+
+
 Caching
 -------
 Caching pages with availability calendars is possible but keep in mind that the
diff --git a/availability_calendar.inc b/availability_calendar.inc
index 96e83dc..d4a46b6 100644
--- a/availability_calendar.inc
+++ b/availability_calendar.inc
@@ -644,7 +644,7 @@ function availability_calendar_update_availability($cid, $sid, $from, $to, $upda
  *
  * @param NULL|int $cid
  *   The calendar id. If empty, a new calendar will be created.
- * @param array $changes
+ * @param array[] $changes
  *   An array of changes each entry is an array with keys state, from and to.
  *
  * @return int
diff --git a/availability_calendar.info b/availability_calendar.info
index 8dd6348..2f6d0f3 100644
--- a/availability_calendar.info
+++ b/availability_calendar.info
@@ -28,3 +28,6 @@ files[] = views/availability_calendar_handler_filter_indexed_availability.inc
 files[] = views/availability_calendar_handler_filter_sql_date.inc
 files[] = views/availability_calendar_handler_sort_sql_date.inc
 files[] = views/availability_calendar_plugin_argument_validate_date_range.inc
+
+; Integration with Migrate
+files[] = availability_calendar_migrate_field_handlers.inc
diff --git a/availability_calendar.migrate.inc b/availability_calendar.migrate.inc
new file mode 100644
index 0000000..0b5b2d3
--- /dev/null
+++ b/availability_calendar.migrate.inc
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Integration with Migrate.
+ */
+
+/**
+ * Implements hook_migrate_api().
+ */
+function availability_calendar_migrate_api() {
+  return array(
+    'api' => 2,
+    'field handlers' => array(
+      'AvailabilityCalendarFieldHandler',
+    ),
+  );
+}
diff --git a/availability_calendar_migrate_field_handlers.inc b/availability_calendar_migrate_field_handlers.inc
new file mode 100644
index 0000000..a7a5fdb
--- /dev/null
+++ b/availability_calendar_migrate_field_handlers.inc
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Migrate field handlers.
+ *
+ * @see https://www.drupal.org/node/1429096.
+ */
+
+/**
+ * Migrates availability data to availability_calendar fields.
+ */
+class AvailabilityCalendarFieldHandler extends MigrateFieldHandler {
+  /**
+   * Registers the availability_calendar field type.
+   */
+  public function __construct() {
+    $this->registerTypes(array('availability_calendar'));
+  }
+
+  /**
+   * Prepares data accordingly for the field API.
+   */
+  public function prepare($entity, array $field_info, array $instance, array $values) {
+    module_load_include('inc', 'availability_calendar', 'availability_calendar.widget');
+    $language = $this->getFieldLanguage($entity, $field_info, array());
+    $delta = 0;
+    $return = array();
+    // In order to be compatible with the way Availability Calendars works, see
+    // prepareCalendar() method.
+    $element = array(
+      'availability_states' => array(
+        '#options' => availability_calendar_get_states(),
+      ),
+      'availability_calendar' => array(
+        '#settings' => $instance['widget']['settings'],
+      ),
+      '#field_name' => $instance['field_name'],
+      '#language' => $language,
+    );
+    foreach ($values as $value) {
+      $element['#delta'] = $delta;
+      $cid = $this->prepareCalendar($value, $element, $entity);
+      if (!$cid) {
+        return NULL;
+      }
+      $return[$language][$delta] = array(
+        'enabled' => 1,
+        'name' => '',
+        'cid' => $cid,
+      );
+      $delta++;
+    }
+
+    return $return;
+  }
+
+  /**
+   * Prepares an Availability Calendar.
+   *
+   * The actual availability data is not stored in field storage but in tables
+   * specific to the module. Therefore we have to prepare a calendar field by
+   * first storing the availability data in those module owned tables and then
+   * storing the reference to that data, the cid (= calendar ID), in the field.
+   *
+   * @param string|array[] $dates
+   *   Availability data, either in a string format, see README.txt, or in an
+   *   array as accepted by availability_calendar_update_multiple_availability()
+   * @param array $element
+   *   Field configuration expected by
+   *   availability_calendar_field_widget_month_form_validate_line().
+   * @param object $entity
+   *   Entity about to be created by Migrate.
+   *
+   * @return int
+   *   ID of the newly created availability calendar entity.
+   *
+   */
+  protected function prepareCalendar($dates, $element, $entity) {
+    if (is_string($dates)) {
+      $dates = $this->convertAvailabilityString($dates, $element);
+    }
+    $field_name = $element['#field_name'];
+    $language_code = $element['#language'];
+    $delta = $element['#delta'];
+
+    // Update or create calendar.
+    $cid = isset($entity->{$field_name}[$language_code][$delta]['cid']) ? $entity->{$field_name}[$language_code][$delta]['cid'] : NULL;
+    $cid = availability_calendar_update_multiple_availability($cid, $dates);
+    if (!isset($entity->{$field_name}[$language_code][$delta]['cid']) || $cid != $entity->{$field_name}[$language_code][$delta]['cid']) {
+      // New calendar: update field.
+      $entity->{$field_name}[$language_code][$delta]['cid'] = $cid;
+    }
+    return $cid;
+  }
+
+  /**
+   * Converts a string specifying the availability to a "multiple availability"
+   * array.
+   *
+   * @param string $dates
+   *   Availability data, see README.txt for a description of the expected
+   *   format.
+   * @param array $element
+   *   Field configuration expected by
+   *   availability_calendar_field_widget_month_form_validate_line().
+   *
+   * @return array
+   *   Array with the availability as accepted by
+   *   availability_calendar_update_multiple_availability().
+   *
+   * @see availability_calendar_field_widget_month_form_validate().
+   * @see availability_calendar_field_attach_submit_inc().
+   */
+  protected function convertAvailabilityString($dates, $element) {
+    $changes = array();
+    $lines = explode("\n", $dates);
+    foreach ($lines as $line) {
+      $line = trim($line);
+      if (!empty($line)) {
+        $change = availability_calendar_field_widget_month_form_validate_line($line, $element);
+        if ($change === FALSE) {
+          watchdog('availability_calendar', "Invalid availability data: %line", array('%line' => $line), WATCHDOG_ERROR);
+          return FALSE;
+        }
+        $changes[] = $change;
+      }
+    }
+    return $changes;
+  }
+
+}
