diff --git includes/index_entity.inc includes/index_entity.inc
index 7042b82..7ba3a8d 100644
--- includes/index_entity.inc
+++ includes/index_entity.inc
@@ -106,6 +106,11 @@ class SearchApiIndex extends Entity {
   public $enabled;
 
   /**
+   * @var integer
+   */
+  public $read_only;
+
+  /**
    * Constructor as a helper to the parent constructor.
    */
   public function __construct(array $values = array()) {
@@ -117,35 +122,7 @@ class SearchApiIndex extends Entity {
    * database, or for the first time loaded from code).
    */
   public function postCreate() {
-    // Remember items to index.
-    $entity_info = entity_get_info($this->entity_type);
-    if (!empty($entity_info['base table'])) {
-      // Use a subselect, which will probably be much faster than entity_load().
-      // We just assume that no module/entity type will be stupid enough to use "base table" and
-      // "entity_keys[id]" in a different way than the default controller.
-      $id_field = $entity_info['entity keys']['id'];
-      $table = $entity_info['base table'];
-      $query = db_select($table, 't');
-      $query->addField('t', $id_field, 'item_id');
-      $query->addExpression(':index_id', 'index_id', array(':index_id' => $this->id));
-      $query->addExpression('1', 'changed');
-
-      db_insert('search_api_item')->from($query)->execute();
-    }
-    else {
-      // We have to use the slow entity_load().
-      $entities = entity_load($this->entity_type, FALSE);
-      $query = db_insert('search_api_item')->fields(array('item_id', 'index_id', 'changed'));
-      foreach ($entities as $item_id => $entity) {
-        $query->values(array(
-          'item_id' => $item_id,
-          'index_id' => $this->id,
-          'changed' => 1,
-        ));
-      }
-      $query->execute();
-    }
-
+    $this->queueItems();
     $server = $this->server();
     if ($server) {
       // Tell the server about the new index.
@@ -177,7 +154,66 @@ class SearchApiIndex extends Entity {
       }
     }
 
-    db_delete('search_api_item')
+    // Stop tracking entities for indexing.
+    $this->dequeueItems();
+  }
+
+  /**
+   * Record entities to index.
+   */
+  public function queueItems() {
+		$this->dequeueItems();
+		
+    if (!$this->read_only) {
+      $entity_info = entity_get_info($this->entity_type);
+
+      if (!empty($entity_info['base table'])) {
+        // Use a subselect, which will probably be much faster than entity_load().
+
+        // Assumes that all entities use the "base table" property and the
+        // "entity keys[id]" in the same way as the default controller.
+        $id_field = $entity_info['entity keys']['id'];
+        $table = $entity_info['base table'];
+
+        // Select all entity ids.
+        $query = db_select($table, 't');
+        $query->addField('t', $id_field, 'item_id');
+        $query->addExpression(':index_id', 'index_id', array(':index_id' => $this->id));
+        $query->addExpression('1', 'changed');
+
+        // INSERT ... SELECT ...
+        db_insert('search_api_item')
+          ->from($query)
+          ->execute();
+      }
+      else {
+        // In the absence of a 'base table', use the slow entity_load().
+
+        // Get an array of all entities using entity_load().
+        $entities = entity_load($this->entity_type, FALSE);
+
+        $query = db_insert('search_api_item')
+          ->fields(array('item_id', 'index_id', 'changed'));
+
+        // Add each entity to the query.
+        foreach ($entities as $item_id => $entity) {
+          $query->values(array(
+            'item_id' => $item_id,
+            'index_id' => $this->id,
+            'changed' => 1,
+          ));
+        }
+
+        $query->execute();
+      }
+    }
+  }
+
+  /**
+   * Remove all records of entities to index.
+   */
+  public function dequeueItems() {
+    $query = db_delete('search_api_item')
       ->condition('index_id', $this->id)
       ->execute();
   }
@@ -221,7 +257,7 @@ class SearchApiIndex extends Entity {
    *   the specified values.
    */
   public function update(array $fields) {
-    $changeable = array('name' => 1, 'enabled' => 1, 'description' => 1, 'server' => 1, 'options' => 1);
+    $changeable = array('name' => 1, 'enabled' => 1, 'description' => 1, 'server' => 1, 'options' => 1, 'read_only' => 1);
     $changed = FALSE;
     foreach ($fields as $field => $value) {
       if (isset($changeable[$field]) && $value !== $this->$field) {
@@ -229,6 +265,7 @@ class SearchApiIndex extends Entity {
         $changed = TRUE;
       }
     }
+
     // If there are no new values, just return 0.
     if (!$changed) {
       return 0;
@@ -243,7 +280,7 @@ class SearchApiIndex extends Entity {
    *   TRUE on success, FALSE on failure.
    */
   public function reindex() {
-    if (!$this->server) {
+    if (!$this->server || $this->read_only) {
       return TRUE;
     }
     $ret = _search_api_index_reindex($this->id);
@@ -260,7 +297,7 @@ class SearchApiIndex extends Entity {
    *   TRUE on success, FALSE on failure.
    */
   public function clear() {
-    if (!$this->server) {
+    if (!$this->server || $this->read_only) {
       return TRUE;
     }
 
@@ -359,6 +396,9 @@ class SearchApiIndex extends Entity {
    *   An array of the IDs of all items that should be marked as indexed.
    */
   public function index(array $items) {
+    if ($this->read_only) {
+      return array();
+    }
     if (!$this->enabled) {
       throw new SearchApiException(t("Couldn't index values on '!name' index (index is disabled)", array('!name' => $this->name)));
     }
diff --git search_api.admin.inc search_api.admin.inc
index 0f46bd8..e16fbb3 100644
--- search_api.admin.inc
+++ search_api.admin.inc
@@ -661,6 +661,7 @@ function search_api_admin_index_view(SearchApiIndex $index = NULL, $action = NUL
     '#server' => $index->server(),
     '#options' => $index->options,
     '#status' => $index->status,
+    '#read_only' => $index->read_only,
   );
 
   return $ret;
@@ -684,9 +685,11 @@ function search_api_admin_index_view(SearchApiIndex $index = NULL, $action = NUL
  *   - total_items: The total number of items that have to be indexed for this
  *     index.
  *   - status: The entity configuration status (in database, in code, etc.).
+ *   - read_only: Boolean indicating whether this index is read only.
  */
 function theme_search_api_index(array $variables) {
   extract($variables);
+
   $output = '';
 
   $output .= '<h3>' . check_plain($name) . '</h3>' . "\n";
@@ -728,7 +731,7 @@ function theme_search_api_index(array $variables) {
     $output .= '</dd>' . "\n";
   }
 
-  if (!empty($options)) {
+  if (!$read_only && !empty($options)) {
     $output .= '<dt>' . t('Index options') . '</dt>' . "\n";
     $output .= '<dd><dl>' . "\n";
     $output .= '<dt>' . t('Cron limit') . '</dt>' . "\n";
@@ -762,6 +765,10 @@ function theme_search_api_index(array $variables) {
 
     $output .= '</dl></dd>' . "\n";
   }
+  elseif ($read_only) {
+    $output .= '<dt>' . t('Read only') . '</dt>' . "\n";
+    $output .= '<dd>' . t('This index is read-only.') . '</dd>' . "\n";
+  }
 
   $output .= '<dt>' . t('Configuration status') . '</dt>' . "\n";
   $output .= '<dd>' . "\n";
@@ -799,55 +806,68 @@ function search_api_admin_index_status_form(array $form, array &$form_state, Sea
     );
   }
 
-  $form['progress'] = array(
-    '#type' => 'item',
-    '#title' => t('Progress'),
-  );
-  $all = ($status['indexed'] == $status['total']);
-  if ($all) {
-    $form['progress']['#description'] = t('All items have been indexed (@total / @total).',
-        array('@total' => $status['total']));
-  }
-  elseif (!$status['indexed']) {
-    $form['progress']['#description'] = t('All items still need to be indexed (@total total).',
-        array('@total' => $status['total']));
-  }
-  else {
-    $percentage = (int) (100 * $status['indexed'] / $status['total']);
-    $form['progress']['#description'] = t('About @percentage% of all items have been indexed in their latest version (@indexed / @total).',
-        array('@indexed' => $status['indexed'], '@total' => $status['total'], '@percentage' => $percentage));
+  if ($index->read_only) {
+    $form['read_only'] = array(
+      '#type' => 'item',
+      '#title' => t('Read only'),
+      '#description' => t('The index is currently in read-only mode. ' .
+          'No new items will be indexed, nor will old ones be deleted.'),
+    );
+
+    return $form;
   }
 
-  if ($enabled && !$all) {
-    $form['index'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Index now'),
-      '#attributes' => array('class' => array('container-inline')),
-      '#collapsible' => TRUE,
-    );
-    $form['index']['limit'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Number of items:'),
-      '#default_value' => empty($index->options['cron_limit']) ? SEARCH_API_DEFAULT_CRON_LIMIT : $index->options['cron_limit'],
-      '#size' => 4,
-      '#attributes' => array('class' => array('search-api-cron-limit')),
-    );
-    $form['index']['button'] = array(
-      '#type' => 'submit',
-      '#value' => t('Index now'),
-    );
-    $form['index']['total'] = array(
-      '#type' => 'value',
-      '#value' => $status['total'],
-    );
-    $form['index']['remaining'] = array(
-      '#type' => 'value',
-      '#value' => $status['total'] - $status['indexed'],
+  if ($enabled) {
+    $form['progress'] = array(
+      '#type' => 'item',
+      '#title' => t('Progress'),
     );
+    $all = ($status['indexed'] == $status['total']);
+    if ($all) {
+      $form['progress']['#description'] = t('All items have been indexed (@total / @total).',
+          array('@total' => $status['total']));
+    }
+    elseif (!$status['indexed']) {
+      $form['progress']['#description'] = t('All items still need to be indexed (@total total).',
+          array('@total' => $status['total']));
+    }
+    else {
+      $percentage = (int) (100 * $status['indexed'] / $status['total']);
+      $form['progress']['#description'] = t('About @percentage% of all items have been indexed in their latest version (@indexed / @total).',
+          array('@indexed' => $status['indexed'], '@total' => $status['total'], '@percentage' => $percentage));
+    }
+
+    if (!$all) {
+      $form['index'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Index now'),
+        '#attributes' => array('class' => array('container-inline')),
+        '#collapsible' => TRUE,
+      );
+      $form['index']['limit'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Number of items:'),
+        '#default_value' => empty($index->options['cron_limit']) ? SEARCH_API_DEFAULT_CRON_LIMIT : $index->options['cron_limit'],
+        '#size' => 4,
+        '#attributes' => array('class' => array('search-api-cron-limit')),
+      );
+      $form['index']['button'] = array(
+        '#type' => 'submit',
+        '#value' => t('Index now'),
+      );
+      $form['index']['total'] = array(
+        '#type' => 'value',
+        '#value' => $status['total'],
+      );
+      $form['index']['remaining'] = array(
+        '#type' => 'value',
+        '#value' => $status['total'] - $status['indexed'],
+      );
+    }
   }
 
   if ($server) {
-    if ($status['indexed'] > 0) {
+    if ($enabled && $status['indexed'] > 0) {
       $form['reindex'] = array(
         '#type' => 'fieldset',
         '#title' => t('Re-indexing'),
@@ -1002,6 +1022,12 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
       $form['server']['#options'][$server->machine_name] = t('@server_name (disabled)', array('@server_name' => $server->name));
     }
   }
+  $form['read_only'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Read only'),
+    '#description' => t('Do not write to this index or track ids of entities in this index.'),
+    '#default_value' => $index->read_only,
+  );
   $form['cron_limit'] = array(
     '#type' => 'textfield',
     '#title' => t('Cron limit'),
@@ -1010,6 +1036,10 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
     '#default_value' => isset($index->options['cron_limit']) ? $index->options['cron_limit'] : SEARCH_API_DEFAULT_CRON_LIMIT,
     '#size' => 4,
     '#attributes' => array('class' => array('search-api-cron-limit')),
+    '#element_validate' => array('_element_validate_integer'),
+    '#states' => array(
+      'invisible' => array(':input[name="read_only"]' => array('checked' => TRUE)),
+    ),
   );
 
   $form['submit'] = array(
@@ -1021,17 +1051,6 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
 }
 
 /**
- * Validation callback for search_api_admin_index_edit.
- */
-function search_api_admin_index_edit_validate(array $form, array &$form_state) {
-  $cron_limit = $form_state['values']['cron_limit'];
-  if ($cron_limit != '' . ((int) $cron_limit)) {
-    // We don't enforce stricter rules and treat all negative values as -1.
-    form_set_error('cron_limit', t('The cron limit must be a number.'));
-  }
-}
-
-/**
  * Submit callback for search_api_admin_index_edit.
  */
 function search_api_admin_index_edit_submit(array $form, array &$form_state) {
diff --git search_api.install search_api.install
index 3b180ca..7af58fd 100644
--- search_api.install
+++ search_api.install
@@ -115,6 +115,13 @@ function search_api_schema() {
         'not null' => TRUE,
         'default' => 1,
       ),
+      'read_only' => array(
+        'description' => 'A flag indicating whether to write to this index.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
     ) + entity_exportable_schema_fields(),
     'indexes' => array(
       'entity_type' => array('entity_type'),
@@ -407,20 +414,58 @@ function search_api_enable() {
   $insert = db_insert('search_api_item')
     ->fields(array('item_id', 'index_id', 'changed'));
   foreach ($types as $type => $indexes) {
-    $info = entity_get_info($type);
-    $id_key = $info['entity keys']['id'];
-    foreach (entity_load($type, FALSE) as $entity) {
-      $id = $entity->$id_key;
-      foreach ($indexes as $index_id) {
-        $insert->values(array(
-          'item_id' => $id,
-          'index_id' => $index_id,
-          'changed' => 1,
-        ));
+    // Partly copied from SearchApiIndex::queueItems().
+
+    $entity_info = entity_get_info($type);
+
+    // If the entity info specifies a base table, use that to add all items
+    // directly in an INSERT ... SELECT. Otherwise, use entity_load() and add
+    // the values to the outer $insert query.
+    if (!empty($entity_info['base table'])) {
+      // Use a subselect, which will probably be much faster than entity_load().
+
+      // Assumes that all entities use the "base table" property and the
+      // "entity keys[id]" in the same way as the default controller.
+      $id_field = $entity_info['entity keys']['id'];
+      $table = $entity_info['base table'];
+
+      // Select all entity ids and all relevant indexes.
+      $query = db_select($table, 't');
+      $query->join('search_api_index', 'i', 'i.entity_type = :type AND i.read_only = :read_only AND i.enabled = :enabled',
+          array(':type' => $type, ':read_only' => 0, ':enabled' => 1));
+      $query->addField('t', $id_field, 'item_id');
+      $query->addField('i', 'id', 'index_id');
+      $query->addExpression('1', 'changed');
+
+      // INSERT ... SELECT ...
+      db_insert('search_api_item')
+        ->from($query)
+        ->execute();
+    }
+    else {
+      foreach (entity_load($type) as $id => $entity) {
+        foreach ($indexes as $index_id) {
+          $insert->values(array(
+            'item_id' => $id,
+            'index_id' => $index_id,
+            'changed' => 1,
+          ));
+        }
       }
     }
   }
-  $insert->execute();
+  // Only execute this query if we added any values to it.
+  if (isset($index_id)) {
+    $insert->execute();
+  }
+}
+
+/**
+ * Implements hook_disable().
+ */
+function search_api_disable() {
+  db_delete('search_api_item')
+    ->execute();
 }
 
 /**
@@ -678,3 +723,18 @@ function search_api_update_7106() {
     }
   }
 }
+
+/**
+ * Add "read only" property to Search API index entities.
+ */
+function search_api_update_7108() {
+  $db_field = array(
+    'description' => 'A flag indicating whether to write to this index.',
+    'type' => 'int',
+    'size' => 'tiny',
+    'not null' => TRUE,
+    'default' => 0,
+  );
+  db_add_field('search_api_index', 'read_only', $db_field);
+  return t('Added a "read only" property to index entities.');
+}
diff --git search_api.module search_api.module
index cfda65e..5bd1a6c 100644
--- search_api.module
+++ search_api.module
@@ -184,6 +184,7 @@ function search_api_theme() {
       'indexed_items' => 0,
       'total_items' => 0,
       'status' => ENTITY_CUSTOM,
+      'read_only' => 0,
     ),
     'file' => 'search_api.admin.inc',
   );
@@ -359,6 +360,11 @@ function search_api_entity_property_info() {
       'type' => 'boolean',
       'description' => t('A flag indicating whether the index is enabled.'),
     ),
+    'read_only' => array(
+      'label' => t('Read only'),
+      'type' => 'boolean',
+      'description' => t('A flag indicating whether the index is read-only.'),
+    ),
   );
 
   return $info;
@@ -511,6 +517,25 @@ function search_api_search_api_index_update(SearchApiIndex $index) {
       _search_api_index_reindex($index->id);
     }
   }
+
+  // If the index's enabled or read-only status is being changed, queue or
+  // dequeue entities for indexing.
+  if ($index->enabled != $index->original->enabled) {
+    if ($index->enabled) {
+      $index->queueItems();
+    }
+    else {
+      $index->dequeueItems();
+    }
+  }
+  elseif ($index->read_only != $index->original->read_only) {
+    if ($index->read_only) {
+      $index->dequeueItems();
+    }
+    else {
+      $index->queueItems();
+    }
+  }
 }
 
 /**
@@ -543,15 +568,17 @@ function search_api_entity_insert($entity, $type) {
   $id = $info['entity keys']['id'];
   $id = $entity->$id;
 
-  foreach (search_api_index_load_multiple(FALSE, array('entity_type' => $type)) as $index) {
-    db_insert('search_api_item')
-      ->fields(array(
-        'index_id' => $index->id,
-        'item_id' => $id,
-        'changed' => 1,
-      ))
-      ->execute();
-  }
+  $query = db_select('search_api_index', 'i')
+    ->condition('entity_type', $type)
+    ->condition('enabled', 1)
+    ->condition('read_only', 0);
+  $query->addField('i', 'id', 'index_id');
+  $query->addExpression(':item_id', 'item_id', array(':item_id' => $id));
+  $query->addExpression(':changed', 'changed', array(':changed' => 1));
+
+  db_insert('search_api_item')
+    ->from($query)
+    ->execute();
 }
 
 /**
@@ -674,7 +701,8 @@ function search_api_search_api_processor_info() {
 function search_api_mark_dirty($entity_type, array $ids) {
   $query = db_select('search_api_index', 'i')
     ->fields('i', array('id'))
-    ->condition('entity_type' , $entity_type);
+    ->condition('entity_type', $entity_type)
+    ->condition('read_only', 0);
   db_update('search_api_item')
     ->fields(array(
       'changed' => REQUEST_TIME,
@@ -707,6 +735,11 @@ function search_api_index_items(SearchApiIndex $index, $limit = -1) {
     throw new SearchApiException(t("Couldn't index values for '!name' index (unknown entity type '!type')", array('!name' => $index->name, '!type' => $index->entity_type)));
   }
 
+  // Don't try to index read-only indexes.
+  if ($index->read_only) {
+    return 0;
+  }
+
   $items = search_api_get_items_to_index($index, $limit);
   if (!$items) {
     return 0;
