diff --git a/README.txt b/README.txt
index bff37fd..bfa71ab 100644
--- a/README.txt
+++ b/README.txt
@@ -61,6 +61,8 @@ FEATURES
  allows listing, sorting, and filtering of Salsa data such as supporters,
  events, etc.
 
+ * Supports migration through export / import drush scripts.
+
 
 REQUIREMENTS
 ------------
@@ -97,6 +99,35 @@ and enable the mollom module and add the forms you want to protect. For detailed
 instructions, please check the mollom module.
 
 
+DRUSH MIGRATION SCRIPT
+----------------------
+
+Before start using drush migration scripts, it is necessary to set the query
+timeout to at least 60 sec (/admin/config/services/salsa), so that script has
+enough time to draw up to 500 records what is the maximum number of records that
+Salsa API can return.
+
+To export data, use 'drush salsa-entity-export' script. Arguments type and directory
+are required. Type argument represents Salsa objects / tables that you want to export,
+tables can be comma-separated or 'all' can be used to export all tables. Directory
+argument represents the location where the exported files will be stored. Sub-directory
+will be created for each table and files will be splitted into chunks of 500 records.
+
+Example: drush salsa-entity-export donation public://salsa
+
+In addition, you can specify a --paging option if you want to split files into smaller
+chunks.
+
+Example: drush salsa-entity-export donation public://salsa --paging=100
+
+Importing is also easy, it is enough to run 'drush salsa-entity-import' script,
+with the same required arguments as for export script, type and directory, where type
+represents objects / tables that you want to import and directory represents location
+from where to import the exported files.
+
+Example: drush salsa-entity-import all public://salsa
+
+
 FOR MORE INFORMATION
 --------------------
 
diff --git a/salsa_entity.drush.inc b/salsa_entity.drush.inc
index 433a38c..c7b5990 100644
--- a/salsa_entity.drush.inc
+++ b/salsa_entity.drush.inc
@@ -13,105 +13,574 @@ function salsa_entity_drush_command() {
     'description' => dt('Generates a file with all translatable strings.'),
   );
   $items['salsa-entity-export'] = array(
-    'description' => dt('Export all objects of the given type'),
+    'description' => dt('Export salsa entities of the given types.'),
     'arguments' => array(
-      'type' => dt('The salsa type, e.g. donate_page'),
+      'types' => dt('The salsa types (comma separated), e.g. supporter,donate_page to be exported.'),
+      'directory' => dt('Destination folder where to export the files.'),
+    ),
+    'options' => array(
+      'paging' => dt('Number of records per file (max and default value is 500 records, how much salsa otherwise allows).'),
     )
   );
   $items['salsa-entity-import'] = array(
-    'description' => dt('Import all salsa entities from the provided export'),
+    'description' => dt('Import salsa entities from the provided export.'),
     'arguments' => array(
-      'file' => dt('The file that should be imported.'),
-    )
+      'types' => dt('The salsa types (comma separated), e.g. supporter,donate_page to be imported.'),
+      'directory' => dt('Source folder from where to import the files.'),
+    ),
   );
   return $items;
 }
 
 /**
- * Export salsa entities.
+ * Drush callback: Exports salsa entities.
+ *
+ * @param array $types
+ *   The salsa type, e.g. donate_page.
+ *
+ * @param string $directory
+ *   Destination folder where to export the files.
+ *
+ * @return string
+ *   Drush error messages if any, otherwise function provides output as JSON file.
  */
-function drush_salsa_entity_export($type = NULL) {
-  if (!$type) {
-    return drush_set_error(dt('You need to provide a salsa type.'));
+function drush_salsa_entity_export($types, $directory) {
+  static $exported = array();
+  static $called = array();
+
+  // Make sure export directory exists.
+  if (!isset($GLOBALS['salsa_export_dir'])) {
+    try {
+      $directory = new DirectoryIterator($directory);
+      if (!$directory->isWritable()) {
+        return drush_set_error(dt('Directory @directory is not writable. Please check permissions.'));
+      }
+      $GLOBALS['salsa_export_dir'] = $directory;
+    }
+    catch (RuntimeException $e) {
+      return drush_set_error(dt('The directory you entered could not be opened. Please check the path.'));
+    }
   }
 
-  $entities[$type] = array();
-  foreach (entity_load('salsa_' . $type) as $entity) {
-    // Extract all relevant properties, filter out empty ones.
-    $entities[$type][$entity->identifier()] = array_filter(get_object_vars($entity));
+  // Check paging option.
+  $paging = drush_get_option('paging', 500);
+  if ($paging > 500) {
+    return drush_set_error(dt('Number of records per file can not be bigger than 500.'));
   }
 
-  $header = '<?php
-// Exported Salsa entities for ' . $type . "\n\n";
+  // Get all salsa types / tables.
+  $salsa_types = array();
+  array_walk_recursive(salsa_mapping_dependencies(), function($item) use (&$salsa_types) {
+    $salsa_types[] = $item;
+  });
+  $salsa_types = array_unique($salsa_types);
 
-  drush_print($header);
+  // Export objects into .json files.
+  $types = $types == 'all' ? $salsa_types : explode(',', $types);
+  foreach ($types as $type) {
+    $called[] = $type;
 
-  // Export data.
-  drush_print('$entities = ' . var_export($entities, TRUE) . ';');
-}
+    // This check is necessary because of manually entered types (entry may be invalid type).
+    // Also, different types can have the same dependencies, skip exporting if records already exported.
+    if (in_array($type, $salsa_types) && !in_array($type, $exported)) {
+
+      // Check whether there are dependencies, if so, export that tables first (recursively).
+      $dependencies = salsa_mapping_dependencies($type);
+      foreach ($dependencies as $dependent_type) {
+        if (!in_array($dependent_type, $called)) {
+          drush_salsa_entity_export($dependent_type, NULL);
+        }
+      }
+
+      // Prepare sub-directory.
+      $subdirectory = $GLOBALS['salsa_export_dir']->getPath() . '/' . $type;
+      if (!file_prepare_directory($subdirectory, FILE_CREATE_DIRECTORY)) {
+        return drush_set_error(dt('You need to create and set writable permissions manually for @subdirectory directory.', array('@subdirectory' => $subdirectory)));
+      }
+
+      $offset   = 0;
+      $sequence = 0;
+
+      do {
+        $entities = salsa_api()->getObjects($type, array(), $offset . ',' . $paging);
+        $error = reset($entities);
+
+        // Get out of loop if $entities empty or error occurs, otherwise put data into JSON file.
+        if (empty($entities) || $error->result == 'error') {
+          $exported[] = $type;
+          break;
+        }
 
-function drush_salsa_entity_import($file = NULL) {
-  if (!$file) {
-    return drush_set_error(dt('You need to provide a file to import from.'));
+        $destination = $subdirectory . '/' . $type . '_' . $sequence . '.json';
+        file_put_contents($destination, json_encode($entities));
+
+        $sequence++;
+        $offset = $offset + $paging;
+
+        drush_print(dt('Successfully created @destination file.', array('@destination' => $destination)));
+      } while (TRUE);
+    }
   }
+}
 
-  if (!file_exists($file)) {
-    // Drush changes the current working directory to the drupal root directory.
-    // Also check the current directory.
-    if (!file_exists(drush_cwd() . '/' . $file)) {
-      return drush_set_error(dt('@name does not exists or is not accessible.', array('@name' => $file)));
+/**
+ * Drush callback: Imports salsa entities.
+ *
+ * @param string $types
+ *   The salsa type, e.g. donate_page.
+ *
+ * @param string $directory
+ *   Source folder from where to import the files.
+ *
+ * @return string
+ *   Error messages if any.
+ */
+function drush_salsa_entity_import($types, $directory) {
+  // Make sure import directory exists.
+  if (!isset($GLOBALS['salsa_export_dir'])) {
+    try {
+      $directory = new DirectoryIterator($directory);
+      if (!$directory->isReadable()) {
+        return drush_set_error(dt('Directory @directory is unreadable. Please check permissions.'));
+      }
+      $GLOBALS['salsa_export_dir'] = $directory;
     }
-    else {
-      // The path is relative to the current directory, update the variable.
-      $file = drush_cwd() . '/' . $file;
+    catch (RuntimeException $e) {
+      return drush_set_error(dt('The directory you entered could not be opened. Please check the path.'));
     }
   }
 
-  include $file;
-  if (empty($entities)) {
-    return drush_set_error(dt('Provided file is not valid'));
-  }
+  // Get all salsa types / tables.
+  $salsa_types = array();
+  array_walk_recursive(salsa_mapping_dependencies(), function($item) use (&$salsa_types) {
+    $salsa_types[] = $item;
+  });
+  $salsa_types = array_unique($salsa_types);
 
-  $mappings = variable_get('salsa_entity_id_mappings', array());
-  $new_mapping_definitions = array();
-
-  foreach ($entities as $type => $entities_type) {
-    drush_log(dt('Starting to process @type.', array('@type' => $type)));
-    foreach ($entities_type as $id => $values) {
-      // Check if this is a new entity.
-      $existing = FALSE;
-      $old_key = NULL;
-      if (isset($mappings[$type][$id])) {
-        drush_log(dt('Entity @id exists already with local mapped id @local_id.', array('@id' => $id, '@local_id' => $mappings[$type][$id])));
-
-        // Replace key with local mapping to update the existing record.
-        $values[$type . '_KEY'] = $mappings[$type][$id];
-        $values['key'] = $mappings[$type][$id];
-        $existing = TRUE;
-      }
-      else {
-        drush_log(dt('Entity @id is new.', array('@id' => $id)));
-        // Save as a new entity.
-        unset($values[$type . '_KEY']);
-        unset($values['key']);
+  // Scan and collect all files.
+  $types = $types == 'all' ? $salsa_types : explode(',', $types);
+  foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($GLOBALS['salsa_export_dir']->getPath(), RecursiveDirectoryIterator::KEY_AS_PATHNAME), RecursiveIteratorIterator::CHILD_FIRST) as $file => $info) {
+    $tok = strtok($info->getFilename(), '_');
+    if ($info->isFile() && in_array($tok, $types) && array_key_exists($tok, $salsa_types) && $info->getExtension() == 'json') {
+      $files[] = $file;
+    }
+  };
+
+  // Go through each file and import one by one record.
+  if (!empty($files)) {
+    foreach ($files as $file) {
+
+      $type = strtok(basename($file), '_');
+
+      // Check whether there are dependencies, if so, import that tables first.
+      // @todo What if some of the dependent table / files is missing?
+      $dependencies = salsa_mapping_dependencies($type);
+      foreach ($dependencies as $dependent_type) {
+        drush_salsa_entity_import($dependent_type, NULL);
       }
 
-      // Remove the organization_KEY
-      unset($values['organization_KEY']);
+      drush_print(dt('Importing file @file', array('@file' => $file)));
+
+      $json = json_decode(file_get_contents($file), TRUE);
+      foreach ($json as $record) {
+        // Check whether the record is already imported, if so, continue.
+        if (salsa_sync_map_key($type, $record['key'])) {
+          continue;
+        }
 
-      $entity = entity_create('salsa_' . $type, $values);
-      $entity->save();
+        $type_key = $type . '_KEY';
+        unset($record[$type_key]);
+        unset($record['key']);
+        unset($record['organization_KEY']);
 
-      drush_log(dt('Saved entity with id @id.', array('@id' => $entity->identifier())), 'success');
+        $entity = entity_create('salsa_' . $type, $record);
 
-      // Add a new mapping line if this is a new object.
-      if (!$existing) {
-        $new_mapping_definitions[] = "\$conf['salsa_entity_id_mappings']['$type'][$id] = " . $entity->identifier() . ';';
+        // Update references (foreign keys).
+        foreach ($salsa_types as $salsa_type) {
+          $property = $salsa_type . '_KEY';
+          if (property_exists($entity, $property)) {
+            $entity->$property = salsa_sync_map_key($salsa_type, $entity->$property);
+          }
+        }
+
+        $entity->save();
+
+        salsa_sync_map_key($type, $record['key'], $entity->key);
       }
     }
+
+    // Save mapping.
+    salsa_sync_map_key(NULL, NULL, NULL, TRUE);
+  }
+  else {
+    drush_set_error(dt('No files to import.'));
   }
-  drush_log(dt('Created @count new objects, add the following mapping definitions to local.settings.php', array('@count' => count($new_mapping_definitions))), 'success');
-  drush_print(implode("\n", $new_mapping_definitions));
+}
+
+/**
+ * Helper function: Maps salsa source <-> target keys.
+ *
+ * @param string $table
+ *   The salsa type (table), e.g. donate_page.
+ *
+ * @param string $source_key
+ *   Source KEY, record ID from the old database.
+ *
+ * @param string $target_key
+ *   Target KEY, record ID in the new database.
+ *
+ * @param bool $save
+ *   Flag, indicates whether to preserve the mapping array or not.
+ *
+ * @return array
+ *   Mapping array (source <-> target key pairs).
+ */
+function salsa_sync_map_key($table, $source_key, $target_key = NULL, $save = FALSE) {
+  static $mapping = array();
+
+  // Load default values on first call.
+  $mapping_file = $GLOBALS['salsa_export_dir']->getPath() . '/mapping.json';
+  if (empty($mapping)) {
+    $mapping = json_decode(file_get_contents($mapping_file), TRUE);
+  }
+
+  // Save call is triggered.
+  if ($save) {
+    if (file_put_contents($mapping_file, json_encode($mapping))) {
+      drush_print(dt('Mapping has been successfully preserved in @mapping_file', array('@mapping_file' => $mapping_file)));
+    }
+
+    return;
+  }
+
+  // 'Read' call.
+  if (is_null($target_key)) {
+    return $mapping[$table][$source_key];
+  }
+
+  // 'Set' call.
+  $mapping[$table][$source_key] = $target_key;
+}
+
+/**
+ * Helper function: Defines mapping dependencies.
+ *
+ * @param string $type
+ *   The salsa type (table), e.g. donate_page.
+ *
+ * @return array
+ *   Dependent tables for the requested type.
+ */
+function salsa_mapping_dependencies($type = NULL) {
+  $dependencies = array(
+    'action' => array(
+      'email_trigger',
+      // 'add_to_groups_KEYS',
+      // 'optionally_add_to_groups_KEYS',
+      'template',
+    ),
+    'action_target' => array(
+      'action',
+      'action_content',
+      // 'excluded_KEYS',
+      'target',
+    ),
+    'action_content' => array(
+      'action',
+    ),
+    'action_content_detail' => array(
+      'action_content',
+    ),
+    'supporter_action' => array(
+      'supporter',
+      'action',
+      'supporter_action_comment',
+    ),
+    'supporter_action_target' => array(
+      'action',
+      'supporter',
+      'supporter_action',
+      'supporter_action_content',
+      // 'target_key',
+      'action_result',
+    ),
+    'supporter_action_comment' => array(
+      'action',
+    ),
+    'recipient' => array(
+      'webform',
+    ),
+    'donate_page' => array(
+      'email_trigger',
+      'merchant_account',
+      'event',
+      'groups',
+      'donation_amount',
+    ),
+    'donation' => array(
+      'supporter',
+      'event',
+      'donate_page',
+      'recurring_donation',
+      'event_fee',
+      // 'membership_invoice_KEY',
+      // 'referral_supporter_KEY',
+      'merchant_account',
+    ),
+    'recurring_donation' => array(
+      'supporter',
+      'donation',
+      'donate_page',
+      'merchant_account',
+      // 'source_donation_KEY',
+      // 'current_reference_donation_KEY',
+    ),
+    'supporter' => array(),
+    'supporter_groups' => array(
+      'supporter',
+      'groups',
+    ),
+    'distributed_event' => array(
+      'merchant_account',
+      // 'signup_$email_trigger_KEYS',
+      // 'attend_$email_trigger_KEYS',
+      'template',
+    ),
+    'groups' => array(
+      // 'external_ID',
+      'query',
+      // 'join_email_trigger_KEYS',
+      // 'add_to_chapter_KEY',
+      // 'salesforce_id'
+
+    ),
+    'database_table' => array(
+      'package',
+    ),
+    'custom_column' => array(
+      'database_table',
+      // 'old_custom_field_KEY',
+    ),
+    'custom_column_option' => array(
+      'database_table',
+      'custom_column',
+    ),
+    'publish' => array(
+      'database_table',
+      // 'table_KEY',
+      'template',
+      'feed_archetype',
+    ),
+    'event' => array(
+      'supporter',
+      'supporter_my_donate_page',
+      'groups',
+      // 'national_event_KEY',
+      'distributed_event',
+      'merchant_account',
+      // 'required$groups_KEYS',
+      // 'event$email_trigger_KEYS',
+      // 'waiting_list$email_trigger_KEYS',
+      // 'upgrade_$email_trigger_KEYS',
+      // 'reminder_$email_trigger_KEYS',
+      'supporter_picture',
+    ),
+    'event_fee' => array(
+      'event',
+      'event_fee_group',
+      'membership_level',
+    ),
+    'event_email_trigger' => array(
+      'event',
+      'email_trigger',
+    ),
+    'supporter_event' => array(
+      'supporter',
+      'event',
+      'donation',
+      'event_fee',
+    ),
+    'supporter_invite' => array(
+      'supporter',
+      'tell_a_friend',
+      'supporter_event',
+      'event_fee',
+    ),
+    'my_donate_page' => array(
+      'email_trigger',
+      'groups',
+      'merchant_account',
+      // 'donor_groups_KEYS',
+      // 'donor$email_trigger_KEYS',
+    ),
+    'supporter_my_donate_page' => array(
+      'supporter',
+      'my_donate_page',
+    ),
+    'supporter_my_donate_page_donation' => array(
+      'supporter',
+      'donation',
+      'supporter_my_donate_page',
+      'recurring_donation',
+    ),
+    'questionnaire' => array(
+      'groups',
+      'email_trigger',
+      // 'add_to_groups_KEYS',
+      // 'optionally_add_to_groups_KEYS',
+    ),
+    'questionnaire_page' => array(
+      'supporter',
+      'questionnaire',
+    ),
+    'questionnaire_question' => array(
+      'questionnaire',
+      'questionnaire_page',
+    ),
+    'questionnaire_question_option' => array(
+      'questionnaire_question',
+    ),
+    'supporter_questionnaire_question' => array(
+      'supporter',
+      'questionnaire_question',
+    ),
+    'signup_page' => array(
+      'event',
+      'tag',
+      'groups',
+      'interest',
+      'email_trigger',
+      // 'add_to_groups_KEYS',
+      // 'optionally_add_to_groups_KEYS',
+      // 'use_new_groups_KEYS',
+    ),
+    'tag' => array(),
+    'tag_data' => array(
+      'database_table',
+      'tag',
+    ),
+    'tell_a_friend' => array(
+      'photo',
+    ),
+    'unsubscribe_page' => array(
+      'groups',
+      'tag',
+      'email_trigger',
+      'chapter',
+    ),
+    'unsubscribe' => array(
+      'supporter',
+      'email_blast',
+    ),
+    'email_trigger' => array(
+      'package',
+      'version',
+      'email_blast',
+    ),
+    'email_blast' => array(
+      'template',
+      'campaign_manager',
+      'query',
+      'campaign',
+      // 'last_processed_supporter_KEY',
+      'chapter',
+      // 'chapter_KEYS_Negate',
+    ),
+    'query' => array(
+      'campaign_manager',
+      'groups',
+      // 'groups_KEYS_Negate',
+      'interest',
+      // 'interest_KEYS_Negate',
+      'campaign',
+      // 'campaign_KEYS_Negate',
+      'letter',
+      // 'letter_KEYS_Negate',
+      'petition',
+      // 'petition_KEYS_Negate',
+      'event',
+      // 'event_KEYS_Negate',
+      'distributed_event',
+      // 'distributed_event_KEYS_Negate',
+      'email_blast',
+      // 'email_blast_KEYS_Negate',
+      'chapter',
+      // 'chapter_KEYS_Negate',
+    ),
+    'campaign' => array(
+      'photo',
+      // 'rep_KEYS',
+      'person_legislator',
+      // 'exclude_person_legislator_IDS',
+      'recipient',
+      'recipient_group',
+      // 'exclude_rep_KEYS',
+      'email_trigger',
+      'groups',
+    ),
+    'letter' => array(
+      'campaign',
+      'recipient',
+      'groups',
+      'email_trigger',
+    ),
+    'petition' => array(
+      'groups',
+      'email_trigger',
+    ),
+    'webform' => array(
+      'admin'
+    ),
+    'chapter' => array(
+      'chapter_type',
+    ),
+    'person_legislator' => array(
+      'person',
+      'webform',
+    ),
+    'recipient_group' => array(
+      'recipient'
+    ),
+    'target' => array(
+      'webform',
+    ),
+    'action_result' => array(
+      'webform',
+    ),
+    'merchant_account' => array(
+      // 'failed_recurring_donation_email_trigger_KEYS',
+      // 'successful_recurring_donation_email_trigger_KEYS',
+    ),
+    'feed_archetype' => array(
+      'database_table',
+    ),
+    'supporter_picture' => array(
+      'supporter',
+      'supporter_picture_album',
+      'groups',
+    ),
+    'supporter_picture_album' => array(
+      'supporter',
+      // 'Album_ID',
+    ),
+    'event_fee_group' => array(
+      'event',
+    ),
+    'version' => array(
+      'package',
+    ),
+    'package' => array(),
+    'template' => array(),
+    'donation_amount' => array(),
+    'membership_level' => array(),
+    'interest' => array(),
+    'photo' => array(),
+    'campaign_manager' => array(),
+    'admin' => array(),
+    'chapter_type' => array(),
+  );
+
+  return is_null($type) ? $dependencies : $dependencies[$type];
 }
 
 /**
@@ -145,23 +614,23 @@ function drush_salsa_entity_generate_translatables() {
       }
     }
     foreach ($salsa_info[$name]['item'] as $item) {
-     // Skip some internal/technical things.
-     foreach (array('READONLY', 'PRIVATE', 'KEY') as $ignore_pattern) {
-       if (strpos($item['name'], $ignore_pattern) !== FALSE) {
-         continue 2;
-       }
-     }
-     fwrite($file, "t('" . $item['label'] . "');\n");
-
-     // Write out enum values.
-     if ((strpos($item['type'], 'enum') === 0 || strpos($item['type'], 'set') === 0) && !empty($item['values'])) {
-       foreach (explode(',', $item['values']) as $value) {
-         // Ignore empty values, those with underscores and uppercase values.
-         if (!empty($value) && strpos($value, '_') === FALSE && $value != strtoupper($value)) {
-           fwrite($file, "t('" . $value . "');\n");
-         }
-       }
-     }
+      // Skip some internal/technical things.
+      foreach (array('READONLY', 'PRIVATE', 'KEY') as $ignore_pattern) {
+        if (strpos($item['name'], $ignore_pattern) !== FALSE) {
+          continue 2;
+        }
+      }
+      fwrite($file, "t('" . $item['label'] . "');\n");
+
+      // Write out enum values.
+      if ((strpos($item['type'], 'enum') === 0 || strpos($item['type'], 'set') === 0) && !empty($item['values'])) {
+        foreach (explode(',', $item['values']) as $value) {
+          // Ignore empty values, those with underscores and uppercase values.
+          if (!empty($value) && strpos($value, '_') === FALSE && $value != strtoupper($value)) {
+            fwrite($file, "t('" . $value . "');\n");
+          }
+        }
+      }
     }
     fwrite($file, "\n");
   }
