diff --git a/README.txt b/README.txt
index 795d7fb..8812007 100644
--- a/README.txt
+++ b/README.txt
@@ -9,14 +9,15 @@ Information for users
 
 - Necessary server feature
 
-The server on which the search will be executed has to support the
-"search_api_autocomplete" feature in order for autocompletion to work. Searches
-on other servers won't be affected by this module.
+The default suggester plugin included in this module retrieves autocomplete
+suggestions from the server. For this to work, the server has to support the
+"search_api_autocomplete" feature. Having autocompletion on other servers is
+only possible if you install a module provide another suggester plugin.
 Currently, the Solr service class [1] and the Database Search [2] are known to
 support this feature.
 
-[1] https://drupal.org/project/search_api_solr
-[2] https://drupal.org/project/search_api_db
+[1] https://www.drupal.org/project/search_api_solr
+[2] https://www.drupal.org/project/search_api_db
 
 - Necessary setup
 
@@ -59,6 +60,13 @@ functionality. See the "Information for developers" for details.
 Information for developers
 --------------------------
 
+- Supporting a new method of creating suggestions
+
+You can add your own implementation for creating autocomplete suggestions by
+creating a so-called "suggester" plugin. For details, see the
+hook_search_api_autocomplete_suggester_info() documentation in
+search_api_autocomplete.api.php.
+
 - Supporting autocompletion with a service class
 
 To support autocompletion with a service class, the class has to support the
diff --git a/search_api_autocomplete.admin.inc b/search_api_autocomplete.admin.inc
index 81210e0..41ecd72 100644
--- a/search_api_autocomplete.admin.inc
+++ b/search_api_autocomplete.admin.inc
@@ -20,10 +20,13 @@ function search_api_autocomplete_admin_overview(array $form, array &$form_state,
   $form_state['index'] = $index;
   $index_id = $index->machine_name;
 
-  $server = $index->server();
-  if (!$server || !$server->supportsFeature('search_api_autocomplete')) {
-    drupal_set_message(t("The server this index currently lies on doesn't support autocompletion. " .
-        'To use autocompletion, you will have to move this index to a server supporting this feature.'), 'error');
+  $available_suggesters = search_api_autocomplete_suggesters_for_index($index);
+  if (!$available_suggesters) {
+    $args = array(
+      '@feature' => 'search_api_autocomplete',
+      '@service_classes_url' => url('https://www.drupal.org/node/1254698#service-classes'),
+    );
+    drupal_set_message(t('There are currently no suggester plugins installed that support this index. To solve this problem, you can either:<ul><li>move the index to a server which supports the "@feature" feature (see the <a href="@service_classes_url">available service class</a>)</li><li>or install a module providing a new suggester plugin that supports this index</li></ul>', $args), 'error');
     if (search_api_autocomplete_search_load_multiple(FALSE, array('index_id' => $index_id))) {
       $form['description'] = array(
         '#type' => 'item',
@@ -62,12 +65,13 @@ function search_api_autocomplete_admin_overview(array $form, array &$form_state,
         $search += array(
           'machine_name' => $id,
           'index_id' => $index_id,
+          'suggester_id' => key($available_suggesters),
           'type' => $type,
           'enabled' => 0,
           'options' => array(),
         );
         $search['options'] += array(
-          'result count' => TRUE,
+          'results' => TRUE,
           'fields' => array(),
         );
         $types[$type]['searches'][$id] = entity_create('search_api_autocomplete_search', $search);
@@ -222,7 +226,21 @@ function search_api_autocomplete_admin_overview_submit_delete(array $form, array
  * @see search_api_autocomplete_admin_search_edit_submit()
  */
 function search_api_autocomplete_admin_search_edit(array $form, array &$form_state, SearchApiAutocompleteSearch $search) {
-  drupal_set_title(t('Edit %search', array('%search' => $search->name)), PASS_THROUGH);
+  drupal_set_title(t('Configure autocompletion for %search', array('%search' => $search->name)), PASS_THROUGH);
+
+  // If this is a re-build (i.e., most likely an AJAX call due to a new
+  // suggester being selected), prepare the suggester sub-form state
+  // accordingly.
+  $selected_suggester_id = $search->getSuggesterId();
+  if (!empty($form_state['values']['suggester_id'])) {
+    $selected_suggester_id = $form_state['values']['suggester_id'];
+    // Don't let submitted values for a different suggester influence another
+    // suggester's form.
+    if ($selected_suggester_id != $form_state['values']['old_suggester_id']) {
+      unset($form_state['values']['options']['suggester_configuration']);
+      unset($form_state['input']['options']['suggester_configuration']);
+    }
+  }
 
   $form_state['search'] = $search;
   $form_state['type'] = $type = search_api_autocomplete_get_types($search->type);
@@ -236,6 +254,67 @@ function search_api_autocomplete_admin_search_edit(array $form, array &$form_sta
     '#title' => t('Enabled'),
     '#default_value' => $search->enabled,
   );
+  $form['suggester_id'] = array(
+    '#type' => 'radios',
+    '#title' => t('Suggester plugin'),
+    '#description' => t('Choose the suggester implementation to use.'),
+    '#options' => array(),
+    '#required' => TRUE,
+    '#default_value' => $selected_suggester_id,
+    '#ajax' => array(
+      'callback' => 'search_api_autocomplete_suggester_ajax_callback',
+      'wrapper' => 'search-api-suggester-options',
+    ),
+  );
+
+  foreach (search_api_autocomplete_suggesters_for_index($search->index()) as $suggester_id => $definition) {
+    // Load the suggester plugin. If the suggester is unchanged from the one on
+    // the saved version of the search, use the saved configuration.
+    $configuration = array();
+    if ($suggester_id == $search->getSuggesterId() && isset($search->options['suggester_configuration'])) {
+      $configuration = $search->options['suggester_configuration'];
+    }
+    $suggester = search_api_autocomplete_suggester_load($suggester_id, $search, $configuration);
+    if (!$suggester) {
+      continue;
+    }
+
+    // Add the suggester to the suggester options.
+    $form['suggester_id']['#options'][$suggester_id] = $suggester->label();
+
+    // Then, also add the configuration form for the selected suggester.
+    if ($suggester_id != $selected_suggester_id) {
+      continue;
+    }
+    $suggester_form_state = &search_api_autocomplete_get_plugin_form_state($form_state);
+    $suggester_form = $suggester->buildConfigurationForm(array(), $suggester_form_state);
+    if ($suggester_form) {
+      $form['options']['suggester_configuration'] = $suggester_form;
+      $form['options']['suggester_configuration']['#type'] = 'fieldset';
+      $form['options']['suggester_configuration']['#title'] = t('Configure the %suggester suggester plugin', array('%suggester' => $suggester->label()));
+      $form['options']['suggester_configuration']['#description'] = $suggester->getDescription();
+      $form['options']['suggester_configuration']['#collapsible'] = TRUE;
+    }
+    else {
+      $form['options']['suggester_configuration']['#type'] = 'item';
+    }
+    $form['options']['suggester_configuration']['#prefix'] = '<div id="search-api-suggester-options">';
+    $form['options']['suggester_configuration']['#suffix'] = '</div>';
+  }
+  $form['options']['suggester_configuration']['old_suggester_id'] = array(
+    '#type' => 'hidden',
+    '#value' => $selected_suggester_id,
+    '#tree' => FALSE,
+  );
+
+  // If there is only a single plugin available, hide the "suggester" option.
+  if (count($form['suggester_id']['#options']) == 1) {
+    $form['suggester_id'] = array(
+      '#type' => 'value',
+      '#value' => key($form['suggester_id']['#options']),
+    );
+  }
+
   $form['options']['min_length'] = array(
     '#type' => 'textfield',
     '#title' => t('Minimum length of keywords for autocompletion'),
@@ -247,26 +326,10 @@ function search_api_autocomplete_admin_search_edit(array $form, array &$form_sta
     '#type' => 'checkbox',
     '#title' => t('Display result count estimates'),
     '#description' => t('Display the estimated number of result for each suggestion. ' .
-        'This option might not have an effect for some servers or types of suggestion.'),
+      'This option might not have an effect for some servers or types of suggestion.'),
     '#default_value' => !empty($search->options['results']),
   );
 
-  // Add a list of fields to include for autocomplete searches.
-  $fields = $search->index()->getFields();
-  $fulltext_fields = $search->index()->getFulltextFields();
-  $options = array();
-  foreach ($fulltext_fields as $field) {
-    $options[$field] = $fields[$field]['name'];
-  }
-  $form['options']['fields'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Fields to use'),
-    '#description' => t('Select the fields which should be searched for matches when looking for autocompletion suggestions. Leave blank to use the same fields as the underlying search.'),
-    '#options' => $options,
-    '#default_value' => empty($search->options['fields']) ? array() : drupal_map_assoc($search->options['fields']),
-    '#attributes' => array('class' => array('search-api-checkboxes-list')),
-  );
-
   $custom_form = empty($form['options']['custom']) ? array() : $form['options']['custom'];
   if (!empty($type['config form']) && function_exists($type['config form']) && is_array($custom_form = $type['config form']($custom_form, $form_state, $search))) {
     $form['options']['custom'] = $custom_form;
@@ -295,19 +358,40 @@ function search_api_autocomplete_admin_search_edit(array $form, array &$form_sta
 }
 
 /**
+ * Form AJAX handler for search_api_autocomplete_admin_search_edit().
+ */
+function search_api_autocomplete_suggester_ajax_callback(array $form, array &$form_state) {
+  return $form['options']['suggester_configuration'];
+}
+
+/**
  * Validate callback for search_api_autocomplete_admin_search_edit().
  *
  * @see search_api_autocomplete_admin_search_edit()
  * @see search_api_autocomplete_admin_search_edit_submit()
  */
 function search_api_autocomplete_admin_search_edit_validate(array $form, array &$form_state) {
-  if (empty($form_state['type']['config form'])) {
-    return;
+  $values = &$form_state['values'];
+  // Call the config form validation method of the selected suggester plugin,
+  // but only if it was the same plugin that created the form.
+  if ($values['suggester_id'] == $values['old_suggester_id']) {
+    $configuration = array();
+    if (!empty($values['options']['suggester_configuration'])) {
+      $configuration = $values['options']['suggester_configuration'];
+    }
+    $suggester = search_api_autocomplete_suggester_load($values['suggester_id'], $form_state['search'], $configuration);
+    $suggester_form = $form['options']['suggester_configuration'];
+    unset($suggester_form['old_suggester_id']);
+    $suggester_form_state = &search_api_autocomplete_get_plugin_form_state($form_state);
+    $suggester->validateConfigurationForm($suggester_form, $suggester_form_state);
   }
-  $f = $form_state['type']['config form'] . '_validate';
-  if (function_exists($f)) {
-    $custom_form = empty($form['options']['custom']) ? array() : $form['options']['custom'];
-    $f($custom_form, $form_state, $form_state['values']['options']['custom']);
+
+  if (!empty($form_state['type']['config form'])) {
+    $f = $form_state['type']['config form'] . '_validate';
+    if (function_exists($f)) {
+      $custom_form = empty($form['options']['custom']) ? array() : $form['options']['custom'];
+      $f($custom_form, $form_state, $values['options']['custom']);
+    }
   }
 }
 
@@ -329,19 +413,69 @@ function search_api_autocomplete_admin_search_edit_submit(array $form, array &$f
 
   $search = $form_state['search'];
   $search->enabled = $values['enabled'];
-  $search->options['results'] = $values['options']['results'];
-  $search->options['min_length'] = $values['options']['min_length'];
-  $search->options['fields'] = array_keys(array_filter($values['options']['fields']));
-  $search->options['submit_button_selector'] = $form_state['values']['advanced']['submit_button_selector'];
+  $search->suggester_id = $values['suggester_id'];
+
+  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $search->index_id . '/autocomplete';
+
+  // Take care of custom options that aren't changed in the config form.
   if (isset($values['options']['custom'])) {
-    // Take care of custom options that aren't changed in the config form.
     $old = empty($search->options['custom']) ? array() : $search->options['custom'];
-    $search->options['custom'] = $values['options']['custom'] + $old;
+    $values['options']['custom'] += $old;
+  }
+
+  // Allow the suggester to decide how to save its configuration. If the user
+  // has disabled JS in the browser, or AJAX didn't work for some other reason,
+  // a different suggester might be selected than that which created the config
+  // form. In that case, we don't call the form submit method, save empty
+  // configuration for the plugin and stay on the page.
+  if ($values['suggester_id'] == $values['old_suggester_id']) {
+    $configuration = array();
+    if (!empty($values['options']['suggester_configuration'])) {
+      $configuration = $values['options']['suggester_configuration'];
+    }
+    $suggester = search_api_autocomplete_suggester_load($values['suggester_id'], $search, $configuration);
+    $suggester_form = $form['options']['suggester_configuration'];
+    unset($suggester_form['old_suggester_id']);
+    $suggester_form_state = &search_api_autocomplete_get_plugin_form_state($form_state);
+    $suggester->submitConfigurationForm($suggester_form, $suggester_form_state);
+    $values['options']['suggester_configuration'] = $suggester->getConfiguration();
+  }
+  else {
+    $values['options']['suggester_configuration'] = array();
+    $form_state['redirect'] = NULL;
+    drupal_set_message(t('The used suggester plugin has changed. Please review the configuration for the new plugin.'), 'warning');
   }
 
+  $search->options = $values['options'];
+
   $search->save();
   drupal_set_message(t('The autocompletion settings for the search have been saved.'));
-  $form_state['redirect'] = 'admin/config/search/search_api/index/' . $search->index_id . '/autocomplete';
+}
+
+/**
+ * Returns a new form state for the suggester configuration sub-form.
+ *
+ * @param array $form_state
+ *   The original form state.
+ *
+ * @return array
+ *   A form state for the sub-form.
+ */
+function &search_api_autocomplete_get_plugin_form_state(array &$form_state) {
+  $sub_form_state = &$form_state['suggester_form_state'];
+
+  foreach (array('values', 'input') as $key) {
+    if (!isset($form_state[$key]['options']['suggester_configuration'])) {
+      $form_state[$key]['options']['suggester_configuration'] = array();
+    }
+    $sub_form_state[$key] = &$form_state[$key]['options']['suggester_configuration'];
+  }
+
+  foreach (array('rebuild', 'rebuild_info', 'redirect') as $key) {
+    $sub_form_state[$key] = &$form_state[$key];
+  }
+
+  return $sub_form_state;
 }
 
 /**
diff --git a/search_api_autocomplete.api.php b/search_api_autocomplete.api.php
index e4e34bb..ff00a4e 100644
--- a/search_api_autocomplete.api.php
+++ b/search_api_autocomplete.api.php
@@ -56,6 +56,58 @@ function hook_search_api_autocomplete_types() {
 }
 
 /**
+ * Add new plugins for calculating autocomplete suggestions.
+ *
+ * @return array
+ *   An array of suggester plugin definitions, keyed by plugin ID (should be
+ *   unique, and thus be prefixed with your module's name). Each definition is
+ *   an associative array with the following keys:
+ *   - label: The human-readable, translated label of the plugin.
+ *   - description: (optional) A translated text describing the plugin in a bit
+ *     more detail.
+ *   - class: The plugin class. Must implement
+ *     SearchApiAutocompleteSuggesterInterface.
+ *   Additional keys will be retained and passed to the plugin as part of its
+ *   definition upon creation.
+ *
+ * @see SearchApiAutocompleteSuggesterInterface
+ * @see SearchApiAutocompleteSuggesterPluginBase
+ * @see hook_search_api_autocomplete_suggester_info_alter()
+ */
+function hook_search_api_autocomplete_suggester_info() {
+  // Source: search_api_autocomplete_search_api_autocomplete_suggester_info().
+  $suggesters['server'] = array(
+    'label' => t('Retrieve from server'),
+    'description' => t('For compatible servers, ask the server for autocomplete suggestions.'),
+    'class' => 'SearchApiAutocompleteServerSuggester',
+  );
+
+  return $suggesters;
+}
+
+/**
+ * Alter the plugins available for calculating autocomplete suggestions.
+ *
+ * @param array $suggesters
+ *   An array of suggester plugin definitions, keyed by plugin ID (should be
+ *   unique, and thus be prefixed with your module's name). Each definition is
+ *   an associative array with the following keys (and possibly others):
+ *   - label: The human-readable, translated label of the plugin.
+ *   - description: (optional) A translated text describing the plugin in a bit
+ *     more detail.
+ *   - class: The plugin class. Must implement
+ *     SearchApiAutocompleteSuggesterInterface.
+ *
+ * @see hook_search_api_autocomplete_suggester_info()
+ */
+function hook_search_api_autocomplete_suggester_info_alter(array &$suggesters) {
+  if (isset($suggesters['foobar'])) {
+    $suggesters['foobar']['my_custom_override_previous'] = $suggesters['foobar']['class'];
+    $suggesters['foobar']['class'] = 'MyCustomOverrideSuggester';
+  }
+}
+
+/**
  * Acts on searches being loaded from the database.
  *
  * This hook is invoked during search loading, which is handled by
@@ -67,9 +119,9 @@ function hook_search_api_autocomplete_types() {
  * @see hook_entity_load()
  */
 function hook_search_api_autocomplete_search_load(array $searches) {
-  $result = db_query('SELECT pid, foo FROM {mytable} WHERE pid IN(:ids)', array(':ids' => array_keys($entities)));
+  $result = db_query('SELECT pid, foo FROM {mytable} WHERE pid IN(:ids)', array(':ids' => array_keys($searches)));
   foreach ($result as $record) {
-    $entities[$record->pid]->foo = $record->foo;
+    $searches[$record->pid]->foo = $record->foo;
   }
 }
 
diff --git a/search_api_autocomplete.entity.php b/search_api_autocomplete.entity.php
index 236dd10..5b4690d 100644
--- a/search_api_autocomplete.entity.php
+++ b/search_api_autocomplete.entity.php
@@ -2,13 +2,11 @@
 
 /**
  * @file
- * Contains the SearchApiAutocompleteSearch class.
+ * Contains SearchApiAutocompleteSearch.
  */
 
-
 /**
- * Class describing the settings for a certain search for which autocompletion
- * is available.
+ * Describes the autocomplete settings for a certain search.
  */
 class SearchApiAutocompleteSearch extends Entity {
 
@@ -37,6 +35,11 @@ class SearchApiAutocompleteSearch extends Entity {
   /**
    * @var string
    */
+  public $suggester_id;
+
+  /**
+   * @var string
+   */
   public $type;
 
   /**
@@ -68,7 +71,14 @@ class SearchApiAutocompleteSearch extends Entity {
   protected $server;
 
   /**
-   * Constructor.
+   * The suggester plugin this search uses.
+   *
+   * @var SearchApiAutocompleteSuggesterInterface
+   */
+  protected $suggester;
+
+  /**
+   * Constructs a SearchApiAutocompleteSearch.
    *
    * @param array $values
    *   The entity properties.
@@ -116,17 +126,49 @@ class SearchApiAutocompleteSearch extends Entity {
   }
 
   /**
-   * @return boolean
-   *   TRUE if the server this search is currently associated with supports the
-   *   autocompletion feature; FALSE otherwise.
+   * Retrieves the ID of the suggester plugin for this search.
+   *
+   * @return string
+   *   This search's suggester plugin's ID.
    */
-  public function supportsAutocompletion() {
-    try {
-      return $this->server() && $this->server()->supportsFeature('search_api_autocomplete');
-    }
-    catch (Exception $e) {
-      return FALSE;
+  public function getSuggesterId() {
+    return $this->suggester_id;
+  }
+
+  /**
+   * Retrieves the suggester plugin for this search.
+   *
+   * @param bool $reset
+   *   (optional) If TRUE, clear the internal static cache and reload the
+   *   suggester.
+   *
+   * @return SearchApiAutocompleteSuggesterInterface|null
+   *   This search's suggester plugin, or NULL if it could not be loaded.
+   */
+  public function getSuggester($reset = FALSE) {
+    if (!isset($this->suggester) || $reset) {
+      $configuration = !empty($this->options['suggester_configuration']) ? $this->options['suggester_configuration'] : array();
+      $this->suggester = search_api_autocomplete_suggester_load($this->suggester_id, $this, $configuration);
+      if (!$this->suggester) {
+        $variables['@search'] = $this->machine_name;
+        $variables['@index'] = $this->index() ? $this->index()->label() : $this->index_id;
+        $variables['@suggester_id'] = $this->suggester_id;
+        watchdog('search_api_autocomplete', 'Autocomplete search @search on index @index specifies an invalid suggester plugin @suggester_id.', $variables, WATCHDOG_ERROR);
+        $this->suggester = FALSE;
+      }
     }
+    return $this->suggester ? $this->suggester : NULL;
+  }
+
+  /**
+   * Determines whether autocompletion is currently supported for this search.
+   *
+   * @return bool
+   *   TRUE if autocompletion is possible for this search with the current
+   *   settings; FALSE otherwise.
+   */
+  public function supportsAutocompletion() {
+    return $this->index() && $this->getSuggester() && $this->getSuggester()->supportsIndex($this->index());
   }
 
   /**
diff --git a/search_api_autocomplete.info b/search_api_autocomplete.info
index 9f25324..0d6ab24 100644
--- a/search_api_autocomplete.info
+++ b/search_api_autocomplete.info
@@ -1,7 +1,10 @@
 name = Search API autocomplete
-description = "Adds autocomplete functionality to most searches on servers supportings this feature."
+description = "Adds autocomplete functionality to searches."
 dependencies[] = search_api
 core = 7.x
 package = Search
 
 files[] = search_api_autocomplete.entity.php
+files[] = src/SearchApiAutocompleteSuggesterInterface.php
+files[] = src/SearchApiAutocompleteSuggesterPluginBase.php
+files[] = src/SearchApiAutocompleteServerSuggester.php
diff --git a/search_api_autocomplete.install b/search_api_autocomplete.install
index c3ce22b..8dd8101 100644
--- a/search_api_autocomplete.install
+++ b/search_api_autocomplete.install
@@ -35,6 +35,12 @@ function search_api_autocomplete_schema() {
         'length' => 50,
         'not null' => TRUE,
       ),
+      'suggester_id' => array(
+        'description' => 'The plugin ID of the suggester this search should use.',
+        'type' => 'varchar',
+        'length' => 50,
+        'not null' => TRUE,
+      ),
       'type' => array(
         'description' => 'The type of search, usually a module name.',
         'type' => 'varchar',
@@ -127,16 +133,17 @@ function search_api_autocomplete_update_7102() {
   if (!db_table_exists('search_api_page')) {
     return;
   }
-  try {
-    $tx = db_transaction();
 
+  $tx = db_transaction();
+
+  try {
     $pages = db_query('SELECT * FROM {search_api_page}')->fetchAllAssoc('id');
     $searches = db_query('SELECT * FROM {search_api_autocomplete_search} WHERE type = :type', array(':type' => 'search_api_page'));
     foreach ($searches as $search) {
       $page_id = (int) substr($search->machine_name, 16);
       if (isset($pages[$page_id])) {
         $machine_name = 'search_api_page_' . $pages[$page_id]->machine_name;
-        $options = unserialize($options);
+        $options = unserialize($search->options);
         $options['custom']['page_id'] = $pages[$page_id]->machine_name;
         db_update('search_api_autocomplete_search')
           ->fields(array(
@@ -153,3 +160,21 @@ function search_api_autocomplete_update_7102() {
     throw new DrupalUpdateException(t('A database error occurred during update: @msg', array('@msg' => $e->getMessage())));
   }
 }
+
+/**
+ * Add the {search_api_autocomplete_search}.suggester_id column.
+ */
+function search_api_autocomplete_update_7103() {
+  // Set "server" as the suggester for all existing searches, then remove the
+  // default again from the schema.
+  $spec = array(
+    'description' => 'The plugin ID of the suggester this search should use.',
+    'type' => 'varchar',
+    'length' => 50,
+    'not null' => TRUE,
+    'default' => 'server',
+  );
+  db_add_field('search_api_autocomplete_search', 'suggester_id', $spec);
+  unset($spec['default']);
+  db_change_field('search_api_autocomplete_search', 'suggester_id', 'suggester_id', $spec);
+}
diff --git a/search_api_autocomplete.interface.php b/search_api_autocomplete.interface.php
index 9f6dea2..bbf3686 100644
--- a/search_api_autocomplete.interface.php
+++ b/search_api_autocomplete.interface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains the SearchApiAutocompleteInterface.
+ * Contains SearchApiAutocompleteInterface.
  */
 
 
@@ -10,16 +10,17 @@
  * Interface describing the method a service class has to add to support autocompletion.
  *
  * Please note that this interface is purely documentational. You shouldn't, and
- * can't, implement it explicitly.
+ * can't, implement it explicitly (unless the module is depending on this one).
  */
 interface SearchApiAutocompleteInterface extends SearchApiServiceInterface {
 
   /**
-   * Get autocompletion suggestions for some user input.
+   * Retrieves autocompletion suggestions for some user input.
    *
    * For example, when given the user input "teach us", with "us" being
    * considered incomplete, the following might be returned:
-   * @code
+   *
+*@code
    *   array(
    *     array(
    *       'prefix' => t('Did you mean:'),
@@ -61,10 +62,10 @@ interface SearchApiAutocompleteInterface extends SearchApiServiceInterface {
    *   - user_input: The input entered by the user. Defaults to $user_input.
    *   - suggestion_suffix: A suggested suffix for the entered input.
    *   - results: If available, the estimated number of results for these keys.
-   *   One of "suggestion_prefix" and "suggestion_suffix" has to be present, all
-   *   other keys are optional. The search keys inserted for the suggestion will
-   *   be a direct concatenation (no spaces in between) of "suggestion_prefix",
-   *   "user_input" and "suggestion_suffix".
+   *   The search keys inserted for the suggestion will be a direct
+   *   concatenation (no spaces in between) of "suggestion_prefix", "user_input"
+   *   and "suggestion_suffix". Therefore, at least one of them has to have a
+   *   non-empty value. Apart from this, all the keys are optional.
    */
   public function getAutocompleteSuggestions(SearchApiQueryInterface $query, SearchApiAutocompleteSearch $search, $incomplete_key, $user_input);
 
diff --git a/search_api_autocomplete.module b/search_api_autocomplete.module
index 76b0da2..4670195 100644
--- a/search_api_autocomplete.module
+++ b/search_api_autocomplete.module
@@ -39,6 +39,19 @@ function search_api_autocomplete_search_api_autocomplete_types() {
 }
 
 /**
+ * Implements hook_search_api_autocomplete_suggester_info().
+ */
+function search_api_autocomplete_search_api_autocomplete_suggester_info() {
+  $suggesters['server'] = array(
+    'label' => t('Retrieve from server'),
+    'description' => t('For compatible servers, ask the server for autocomplete suggestions.'),
+    'class' => 'SearchApiAutocompleteServerSuggester',
+  );
+
+  return $suggesters;
+}
+
+/**
  * Implements hook_menu().
  */
 function search_api_autocomplete_menu() {
@@ -141,12 +154,12 @@ function search_api_autocomplete_permission() {
 /**
  * Access callback for search autocompletion.
  *
- * @param $search
+ * @param SearchApiAutocompleteSearch $search
  *   The autocomplete search which is being accessed.
- * @param $account
+ * @param object|null $account
  *   (optional) The account to check, if not given use currently logged in user.
  *
- * @return
+ * @return bool
  *   TRUE, if the search is enabled, supports autocompletion and the user has
  *   the necessary permission. FALSE otherwise.
  */
@@ -168,12 +181,12 @@ function search_api_autocomplete_search_api_index_delete(SearchApiIndex $index)
 }
 
 /**
- * Get information about all search types, or a specific one.
+ * Retrieves information about all search types, or a specific one.
  *
  * @param $type
  *   (optional) The name of a type.
  *
- * @return
+ * @return array|null
  *   If $type was not given, an array containing information about all search
  *   types. Otherwise, either information about the specified type, or NULL if
  *   the type is not known.
@@ -192,6 +205,83 @@ function search_api_autocomplete_get_types($type = NULL) {
 }
 
 /**
+ * Retrieves the definitions of all suggester plugins, or a specific one.
+ *
+ * @param string|null $suggester_id
+ *   (optional) The ID of the suggester plugin whose definition should be
+ *   retrieved. Or NULL to return all known definitions.
+ *
+ * @return array|null
+ *   If $suggester_id was given, either the definition of the given suggester
+ *   plugin, or NULL if it isn't known. Otherwise, an associative array of all
+ *   known suggester plugin definitions, keyed by ID.
+ */
+function search_api_autocomplete_suggester_info($suggester_id = NULL) {
+  $suggesters = &drupal_static(__FUNCTION__);
+
+  if (!isset($suggesters)) {
+    $suggesters = module_invoke_all('search_api_autocomplete_suggester_info');
+    drupal_alter('search_api_autocomplete_suggester_info', $suggesters);
+    foreach ($suggesters as $i => $definition) {
+      if (empty($definition['class']) || !class_exists($definition['class']) || !in_array('SearchApiAutocompleteSuggesterInterface', class_implements($definition['class']))) {
+        $variables['@suggester_id'] = $i;
+        watchdog('search_api_autocomplete', 'Autocomplete suggester plugin with ID @suggester_id does not specify a valid plugin class.', $variables, WATCHDOG_ERROR);
+        unset($suggesters[$i]);
+      }
+    }
+  }
+
+  if (!isset($suggester_id)) {
+    return $suggesters;
+  }
+  return isset($suggesters[$suggester_id]) ? $suggesters[$suggester_id] : NULL;
+}
+
+/**
+ * Retrieves all suggester plugin definitions that support the given index.
+ *
+ * @param SearchApiIndex $index
+ *   The index for which to check.
+ *
+ * @return array|null
+ *   If $suggester_id was given, either the definition of the given suggester
+ *   plugin, or NULL if it isn't known. Otherwise, an associative array of all
+ *   known suggester plugin definitions, keyed by ID.
+ */
+function search_api_autocomplete_suggesters_for_index(SearchApiIndex $index) {
+  $suggesters = search_api_autocomplete_suggester_info();
+
+  foreach ($suggesters as $suggester_id => $definition) {
+    if (!call_user_func(array($definition['class'], 'supportsIndex'), $index)) {
+      unset($suggesters[$suggester_id]);
+    }
+  }
+
+  return $suggesters;
+}
+
+/**
+ * Loads the specified suggester plugin.
+ *
+ * @param string $suggester_id
+ *   The ID of the suggester plugin to load.
+ * @param SearchApiAutocompleteSearch $search
+ *   The search for which to create a suggester.
+ * @param array $configuration
+ *   The configuration for the search.
+ *
+ * @return SearchApiAutocompleteSuggesterInterface|null
+ *   The loaded suggester plugin, or NULL if it could not be loaded.
+ */
+function search_api_autocomplete_suggester_load($suggester_id, SearchApiAutocompleteSearch $search, array $configuration) {
+  $definition = search_api_autocomplete_suggester_info($suggester_id);
+  if (!$definition) {
+    return NULL;
+  }
+  return call_user_func(array($definition['class'], 'create'), $search, $configuration, $suggester_id, $definition);
+}
+
+/**
  * Loads an autocomplete search entity.
  *
  * @param int|string $id
diff --git a/search_api_autocomplete.pages.inc b/search_api_autocomplete.pages.inc
index 13c0942..52ba151 100644
--- a/search_api_autocomplete.pages.inc
+++ b/search_api_autocomplete.pages.inc
@@ -7,12 +7,19 @@
 
 /**
  * Page callback for getting autocomplete suggestions.
+ *
+ * @param SearchApiAutocompleteSearch $search
+ *   The search for which to retrieve autocomplete suggestions.
+ * @param string $fields
+ *   A comma-separated list of fields on which to do autocompletion. Or "-" to
+ *   use all fulltext fields.
+ * @param string $keys
+ *   The user input so far.
  */
 function search_api_autocomplete_autocomplete(SearchApiAutocompleteSearch $search, $fields, $keys = '') {
   $ret = array();
   try {
     if ($search->supportsAutocompletion()) {
-      $server = $search->server();
       list($complete, $incomplete) = $search->splitKeys($keys);
       $query = $search->getQuery($complete, $incomplete);
       if ($query) {
@@ -27,7 +34,7 @@ function search_api_autocomplete_autocomplete(SearchApiAutocompleteSearch $searc
           $query->fields($fields);
         }
         $query->preExecute();
-        $suggestions = $server->getAutocompleteSuggestions($query, $search, $incomplete, $keys);
+        $suggestions = $search->getSuggester()->getAutocompleteSuggestions($query, $incomplete, $keys);
         if ($suggestions) {
           foreach ($suggestions as $suggestion) {
             // Convert suggestion strings into an array.
diff --git a/src/SearchApiAutocompleteServerSuggester.php b/src/SearchApiAutocompleteServerSuggester.php
new file mode 100644
index 0000000..ba27daf
--- /dev/null
+++ b/src/SearchApiAutocompleteServerSuggester.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Contains SearchApiAutocompleteServerSuggester.
+ */
+
+/**
+ * Provides a suggester plugin that retrieves suggestions from the server.
+ *
+ * The server needs to support the "search_api_autocomplete" feature for this to
+ * work.
+ */
+class SearchApiAutocompleteServerSuggester extends SearchApiAutocompleteSuggesterPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function supportsIndex(SearchApiIndex $index) {
+    try {
+      return $index->server() && $index->server()->supportsFeature('search_api_autocomplete');
+    }
+    catch (SearchApiException $e) {
+      return FALSE;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array(
+      'fields' => array(),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, array &$form_state) {
+    // Add a list of fields to include for autocomplete searches.
+    $search = $this->getSearch();
+    $fields = $search->index()->getFields();
+    $fulltext_fields = $search->index()->getFulltextFields();
+    $options = array();
+    foreach ($fulltext_fields as $field) {
+      $options[$field] = $fields[$field]['name'];
+    }
+    $form['fields'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Override used fields'),
+      '#description' => t('Select the fields which should be searched for matches when looking for autocompletion suggestions. Leave blank to use the same fields as the underlying search.'),
+      '#options' => $options,
+      '#default_value' => drupal_map_assoc($this->configuration['fields']),
+      '#attributes' => array('class' => array('search-api-checkboxes-list')),
+    );
+    $form['#attached']['css'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.css';
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array $form, array &$form_state) {
+    $values = $form_state['values'];
+    $values['fields'] = array_keys(array_filter($values['fields']));
+    $this->setConfiguration($values);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAutocompleteSuggestions(SearchApiQueryInterface $query, $incomplete_key, $user_input) {
+    if ($this->configuration['fields']) {
+      $query->fields($this->configuration['fields']);
+    }
+    return $query->getIndex()->server()->getAutocompleteSuggestions($query, $this->getSearch(), $incomplete_key, $user_input);
+  }
+
+}
diff --git a/src/SearchApiAutocompleteSuggesterInterface.php b/src/SearchApiAutocompleteSuggesterInterface.php
new file mode 100644
index 0000000..28da4e5
--- /dev/null
+++ b/src/SearchApiAutocompleteSuggesterInterface.php
@@ -0,0 +1,197 @@
+<?php
+
+/**
+ * @file
+ * Contains SearchApiAutocompleteSuggesterInterface.
+ */
+
+/**
+ * Represents a plugin for creating autocomplete suggestions.
+ *
+ * @see SearchApiAutocompleteSuggesterPluginBase
+ * @see hook_search_api_autocomplete_suggester_info()
+ */
+interface SearchApiAutocompleteSuggesterInterface {
+
+  /**
+   * Creates a new instance of this class.
+   *
+   * @param SearchApiAutocompleteSearch $search
+   *   The search to which this suggester is attached.
+   * @param array $configuration
+   *   An associative array containing the suggester's configuration, if any.
+   * @param string $plugin_id
+   *   The suggester's plugin ID.
+   * @param array $plugin_definition
+   *   The suggester plugin's definition.
+   *
+   * @return static
+   *   A new instance of this class.
+   */
+  public static function create(SearchApiAutocompleteSearch $search, array $configuration, $plugin_id, array $plugin_definition);
+
+  /**
+   * Determines whether this plugin class supports the given index.
+   *
+   * @param SearchApiIndex $index
+   *   The search index in question.
+   *
+   * @return bool
+   *   TRUE if this plugin supports the given search index, FALSE otherwise.
+   */
+  public static function supportsIndex(SearchApiIndex $index);
+
+  /**
+   * Retrieves the plugin's ID.
+   *
+   * @return string
+   *   The plugin's ID.
+   */
+  public function getPluginId();
+
+  /**
+   * Retrieves the plugin's definition.
+   *
+   * @return array
+   *   The plugin's definition.
+   */
+  public function getPluginDefinition();
+
+  /**
+   * Retrieves the search this plugin is configured for.
+   *
+   * @return SearchApiAutocompleteSearch
+   *   The search this plugin is configured for.
+   */
+  public function getSearch();
+
+  /**
+   * Retrieves the plugin's label.
+   *
+   * @return string
+   *   The plugin's human-readable and translated label.
+   */
+  public function label();
+
+  /**
+   * Retrieves the plugin's description.
+   *
+   * @return string|null
+   *   The plugin's translated description; or NULL if it has none.
+   */
+  public function getDescription();
+
+  /**
+   * Retrieves the plugin's configuration.
+   *
+   * @return array
+   *   An associative array containing the plugin's configuration.
+   */
+  public function getConfiguration();
+
+  /**
+   * Sets the plugin's configuration.
+   *
+   * @param array $configuration
+   *   An associative array containing the plugin's configuration.
+   *
+   * @return $this
+   */
+  public function setConfiguration(array $configuration);
+
+  /**
+   * Retrieves the default configuration for this plugin.
+   *
+   * @return array
+   *   An associative array containing the plugin's default configuration.
+   */
+  public function defaultConfiguration();
+
+  /**
+   * Constructs the plugin's configuration form.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   An associative array containing the structure of the form. An empty array
+   *   if the plugin has no configuration form.
+   */
+  public function buildConfigurationForm(array $form, array &$form_state);
+
+  /**
+   * Validates the plugin's configuration form.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   The current state of the form.
+   */
+  public function validateConfigurationForm(array $form, array &$form_state);
+
+  /**
+   * Submits the plugin's configuration form.
+   *
+   * Should take care of calling setConfiguration() with the new configuration
+   * values as appropriate.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   The current state of the form.
+   */
+  public function submitConfigurationForm(array $form, array &$form_state);
+
+  /**
+   * Retrieves autocompletion suggestions for some user input.
+   *
+   * For example, when given the user input "teach us", with "us" being
+   * considered incomplete, the following might be returned:
+   *
+   * @code
+   *   array(
+   *     array(
+   *       'prefix' => t('Did you mean:'),
+   *       'user_input' => 'reach us',
+   *     ),
+   *     array(
+   *       'user_input' => 'teach us',
+   *       'suggestion_suffix' => 'ers',
+   *     ),
+   *     array(
+   *       'user_input' => 'teach us',
+   *       'suggestion_suffix' => ' swimming',
+   *     ),
+   *     'teach users swimming',
+   *   );
+   * @endcode
+   *
+   * @param SearchApiQueryInterface $query
+   *   A query representing the completed user input so far.
+   * @param string $incomplete_key
+   *   The start of another fulltext keyword for the search, which should be
+   *   completed. Might be empty, in which case all user input up to now was
+   *   considered completed. Then, additional keywords for the search could be
+   *   suggested.
+   * @param string $user_input
+   *   The complete user input for the fulltext search keywords so far.
+   *
+   * @return array
+   *   An array of suggestion. Each suggestion is either a simple string
+   *   containing the whole suggested keywords, or an array containing the
+   *   following keys:
+   *   - prefix: For special suggestions, some kind of prefix describing them.
+   *   - suggestion_prefix: A suggested prefix for the entered input.
+   *   - user_input: The input entered by the user. Defaults to $user_input.
+   *   - suggestion_suffix: A suggested suffix for the entered input.
+   *   - results: If available, the estimated number of results for these keys.
+   *   The search keys inserted for the suggestion will be a direct
+   *   concatenation (no spaces in between) of "suggestion_prefix", "user_input"
+   *   and "suggestion_suffix". Therefore, at least one of them has to have a
+   *   non-empty value. Apart from this, all the keys are optional.
+   */
+  public function getAutocompleteSuggestions(SearchApiQueryInterface $query, $incomplete_key, $user_input);
+
+}
diff --git a/src/SearchApiAutocompleteSuggesterPluginBase.php b/src/SearchApiAutocompleteSuggesterPluginBase.php
new file mode 100644
index 0000000..e1c0938
--- /dev/null
+++ b/src/SearchApiAutocompleteSuggesterPluginBase.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * @file
+ * Contains SearchApiAutocompleteSuggesterPluginBase.
+ */
+
+/**
+ * Provides a base class for suggester plugins.
+ *
+ * @see SearchApiAutocompleteSuggesterInterface
+ * @see hook_search_api_autocomplete_suggester_info()
+ */
+abstract class SearchApiAutocompleteSuggesterPluginBase implements SearchApiAutocompleteSuggesterInterface {
+
+  /**
+   * The search this suggester is attached to.
+   *
+   * @var SearchApiAutocompleteSearch
+   */
+  protected $search;
+
+  /**
+   * The suggester plugin's ID.
+   *
+   * @var string
+   */
+  protected $pluginId;
+
+  /**
+   * The suggester plugin's definition.
+   *
+   * @var array
+   */
+  protected $pluginDefinition = array();
+
+  /**
+   * The suggester plugin's configuration.
+   *
+   * @var array
+   */
+  protected $configuration = array();
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(SearchApiAutocompleteSearch $search, array $configuration, $plugin_id, array $plugin_definition) {
+    // It seems there is no way to have "new static()"-like functionality in
+    // PHP 5.2, so we have to use this workaround instead.
+    $class = $plugin_definition['class'];
+    return new $class($search, $configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function supportsIndex(SearchApiIndex $index) {
+    return TRUE;
+  }
+
+  /**
+   * Constructs a SearchApiAutocompleteSuggesterPluginBase.
+   *
+   * @param SearchApiAutocompleteSearch $search
+   *   The search to which this suggester is attached.
+   * @param array $configuration
+   *   An associative array containing the suggester's configuration, if any.
+   * @param string $plugin_id
+   *   The suggester's plugin ID.
+   * @param array $plugin_definition
+   *   The suggester plugin's definition.
+   */
+  public function __construct(SearchApiAutocompleteSearch $search, array $configuration, $plugin_id, array $plugin_definition) {
+    $this->search = $search;
+    $this->pluginId = $plugin_id;
+    $this->pluginDefinition = $plugin_definition;
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginId() {
+    return $this->pluginId;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginDefinition() {
+    return $this->pluginDefinition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSearch() {
+    return $this->search;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function label() {
+    return $this->pluginDefinition['label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return isset($this->pluginDefinition['description']) ? $this->pluginDefinition['description'] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = $configuration + $this->defaultConfiguration();
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, array &$form_state) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array $form, array &$form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array $form, array &$form_state) {
+    if (!empty($form_state['values'])) {
+      $this->setConfiguration($form_state['values']);
+    }
+  }
+
+}
