diff --git mollom.module mollom.module
index 8bc968e..79bd859 100644
--- mollom.module
+++ mollom.module
@@ -1194,6 +1194,9 @@ function _mollom_status($reset = FALSE) {
   // If we have keys and are asked to reset, check whether keys are valid.
   if ($status['keys'] && $reset) {
     $status['keys valid'] = mollom('mollom.verifyKey', _mollom_get_version());
+    // @todo mollom.verifyKey doesn't exist yet; /rest/site returns full site
+    //   information on success.
+    $status['keys valid'] = is_array($status['keys valid']);
   }
 
   // In case of an error, indicate whether we have a non-empty server list.
@@ -1250,7 +1253,8 @@ function _mollom_fallback() {
   $servers = variable_get('mollom_servers', array());
   _mollom_watchdog(array(
     'All servers unavailable: %servers' => array('%servers' => $servers ? implode(', ', $servers) : '--'),
-    'Last error: @code %message' => array('@code' => xmlrpc_errno(), '%message' => xmlrpc_error_msg()),
+    // @todo No global error handler with REST.
+    //'Last error: @code %message' => array('@code' => xmlrpc_errno(), '%message' => xmlrpc_error_msg()),
   ), WATCHDOG_ERROR);
 }
 
@@ -1320,6 +1324,7 @@ function mollom_process_mollom($element, &$form_state, $complete_form) {
       'require_analysis' => $element['#mollom_form']['mode'] == MOLLOM_MODE_ANALYSIS,
       'require_captcha' => $element['#mollom_form']['mode'] == MOLLOM_MODE_CAPTCHA,
       'passed_captcha' => FALSE,
+      'captcha_type' => 'image',
       'require_moderation' => FALSE,
       'response' => array(
         'session_id' => '',
@@ -1353,25 +1358,19 @@ function mollom_process_mollom($element, &$form_state, $complete_form) {
 
   // Request and inject a CAPTCHA when required; but also in case validation
   // through textual analysis failed.
+  dpm($form_state['mollom']);
   if ($form_state['mollom']['require_captcha'] && !$form_state['mollom']['passed_captcha']) {
     $element['captcha']['#required'] = TRUE;
 
     // Prevent the page cache from storing a form containing a CAPTCHA element.
     $GLOBALS['conf']['cache'] = 0;
 
-    $data = array();
-    if (!empty($form_state['mollom']['response']['session_id'])) {
-      $data['session_id'] = $form_state['mollom']['response']['session_id'];
-    }
-    $captcha = mollom_get_captcha('image', $data);
-
+    $captcha = mollom_get_captcha($form_state);
     // If we get a response, add the image CAPTCHA to the form element.
-    if (isset($captcha['response']['session_id']) && !empty($captcha['markup'])) {
-      $element['captcha']['#field_prefix'] = $captcha['markup'];
-
+    if (!empty($captcha)) {
+      $element['captcha']['#field_prefix'] = $captcha;
       // Assign the session ID returned by Mollom.
-      $form_state['mollom']['response']['session_id'] = $captcha['response']['session_id'];
-      $element['session_id']['#value'] = $captcha['response']['session_id'];
+      $element['session_id']['#value'] = $form_state['mollom']['response']['session_id'];
     }
     // Otherwise, we have a communication or configuration error.
     // @todo Short-cut form processing entirely in this case; see also
@@ -1520,16 +1519,11 @@ function mollom_validate_analysis(&$form, &$form_state) {
           $form['mollom']['captcha']['#access'] = TRUE;
           $form['mollom']['captcha']['#required'] = TRUE;
 
-          $captcha_data = array(
-            'session_id' => $result['session_id'],
-          );
-          $captcha = mollom_get_captcha('image', $captcha_data);
-
+          $captcha = mollom_get_captcha($form_state);
           // If we get a response, add the image CAPTCHA to the form element.
-          if (isset($captcha['response']['session_id']) && !empty($captcha['markup'])) {
-            $form_state['mollom']['response']['session_id'] = $captcha['response']['session_id'];
-            $form['mollom']['session_id']['#value'] = $captcha['response']['session_id'];
-            $form['mollom']['captcha']['#field_prefix'] = $captcha['markup'];
+          if (!empty($captcha)) {
+            $form['mollom']['captcha']['#field_prefix'] = $captcha;
+            $form['mollom']['session_id']['#value'] = $form_state['mollom']['response']['session_id'];
           }
         }
         break;
@@ -1723,40 +1717,83 @@ function mollom($method, $data = array()) {
   $refresh = FALSE;
 
   // Enable testing mode.
+  // @todo Testing mode not implemented in REST backend yet.
   if (variable_get('mollom_testing_mode', 0)) {
     $data['testing'] = TRUE;
   }
 
-  // Retrieve the list of Mollom servers from the database.
-  $servers = variable_get('mollom_servers', array());
+  // Map XML-RPC methods to REST paths.
+  $original_method = $method;
+  switch ($method) {
+    case 'mollom.verifyKey':
+      $method = 'GET';
+      $path = 'rest/site/' . variable_get('mollom_public_key', '') . '.xml';
+      break;
 
-  if (empty($servers)) {
-    // Retrieve a new list of servers.
-    $servers = _mollom_retrieve_server_list();
-    // If API keys are invalid, a XML-RPC error code is returned.
-    if (!is_array($servers)) {
-      return $servers;
-    }
+    case 'mollom.checkContent':
+      $method = 'POST';
+      $path = 'rest/content';
+      break;
 
-    $messages[] = array(
-      'Refreshed servers: %servers' => array('%servers' => implode(', ', $servers)),
-    );
+    case 'mollom.getCaptcha':
+      $method = 'POST';
+      $path = 'rest/captcha/' . $data['type'];
+      break;
 
-    // Store the list of servers in the database.
-    variable_set('mollom_servers', $servers);
+    case 'mollom.checkCaptcha':
+      $method = 'POST';
+      $path = 'rest/captcha/verify/' . $data['session_id'] . '.xml';
+      break;
+
+    default:
+      $path = $method;
+      $method = 'GET';
+      break;
+  }
+
+  // Retrieve server list.
+  $servers = _mollom_get_servers();
+  // If we get no list, return the error code.
+  if (!is_array($servers)) {
+    return $servers;
   }
 
   if (is_array($servers)) {
+    $data += _mollom_authentication();
+
+    // Build the REST request path.
+    $rest_path = $path;
+    $query = NULL;
+    if ($data) {
+      $query = drupal_http_build_query($data);
+      if ($method != 'POST') {
+        $rest_path .= '?' . $query;
+      }
+    }
+
     // Send the request to the first server; if that fails, try the other
     // servers in the list.
     reset($servers);
     while ($server = current($servers)) {
-      $result = xmlrpc($server . '/' . MOLLOM_API_VERSION, array(
-        $method => array($data + _mollom_authentication()),
+      $result = drupal_http_request($server . '/' . $rest_path, array(
+        'method' => $method,
+        'headers' => array(
+          'Accept' => 'application/xml, application/json;q=0.8, */*;q=0.5',
+        ),
+        // Required for POST requests.
+        'data' => $query,
       ));
 
-      if ($result === FALSE && ($error = xmlrpc_error())) {
-        if ($error->code === MOLLOM_REFRESH) {
+      // @todo FIXME: Backend issues.
+      // /rest/captcha/{type} returns CAPTCHA URL as Location header.
+      if ($original_method == 'mollom.getCaptcha') {
+        $result->data = $result->headers['location'];
+      }
+//      dsm($result);
+
+      // @todo drupal_http_request() interprets any other code than 200 as error, stupid.
+      if (isset($result->error) && $result->code[0] != 2) {
+        if ($result->code === MOLLOM_REFRESH) {
           // Avoid endless loops.
           if (!$refresh) {
             $refresh = TRUE;
@@ -1782,7 +1819,7 @@ function mollom($method, $data = array()) {
             );
           }
         }
-        elseif ($error->code === MOLLOM_REDIRECT) {
+        elseif ($result->code === MOLLOM_REDIRECT) {
           // Try the next server in the list.
           $next = next($servers);
 
@@ -1793,16 +1830,16 @@ function mollom($method, $data = array()) {
         else {
           $messages[] = array(
             'Error @errno from %server for %method: %message' => array(
-              '@errno' => $error->code,
+              '@errno' => $result->code,
               '%server' => $server,
-              '%method' => $method,
-              '%message' => $error->message,
+              '%method' => "{$method} {$rest_path}",
+              '%message' => $result->error,
             ),
             'Data:<pre>@data</pre>' => array('@data' => $data),
           );
 
           // Instantly return upon a 'real' error.
-          if ($error->code === MOLLOM_ERROR) {
+          if ($result->code === MOLLOM_ERROR) {
             _mollom_watchdog_multiple($messages, WATCHDOG_ERROR);
             return MOLLOM_ERROR;
           }
@@ -1810,9 +1847,63 @@ function mollom($method, $data = array()) {
           next($servers);
         }
       }
+      // Positive result.
       else {
+        // Handle response data format.
+        if (isset($result->headers['content-type'])) {
+          if ($result->headers['content-type'] == 'application/json') {
+            $result->data = drupal_json_decode($result->data);
+          }
+          elseif ($result->headers['content-type'] == 'application/xml') {
+//            require_once DRUPAL_ROOT . '/includes/xmlrpc.inc';
+//            $message = xmlrpc_message($result->data);
+//            if (!xmlrpc_message_parse($message)) {
+//              xmlrpc_error(-32700, t('Parse error. Not well formed'));
+//            }
+//            else {
+//              $result->data = $message->params[0];
+//            }
+//            dpm(xmlrpc_error());
+
+            // Ensure the response is proper XML.
+            if (is_string($result->data) && $result->data[1] !== '?') {
+              $result->data = '<?xml version="1.0" encoding="utf-8" ?' . ">\n" . $result->data;
+            }
+
+            $result->elements = new SimpleXmlIterator($result->data);
+            $result->data = _mollom_parse_xml($result->elements);
+          }
+        }
+
+        // @todo FIXME: Backend issues.
+        // /rest/captcha/verify returns a strange data structure.
+        if ($original_method == 'mollom.checkCaptcha' && isset($result->data['params']['param']['value']['boolean'])) {
+          $result->data = (bool) $result->data['params']['param']['value']['boolean'];
+        }
+        // /rest/content returns text analysis details instead of spam classification integer.
+        elseif ($original_method == 'mollom.checkContent' && isset($result->data['spam']['classification'])) {
+          $map = array(
+            'SPAM' => MOLLOM_ANALYSIS_SPAM,
+            'HAM' => MOLLOM_ANALYSIS_HAM,
+            'UNSURE' => MOLLOM_ANALYSIS_UNSURE,
+            'UNKNOWN' => MOLLOM_ANALYSIS_UNKNOWN,
+          );
+          $result->data['spam'] = $map[$result->data['spam']['classification']];
+        }
+
+        $messages[] = array(
+          '%method to %server: @code' => array(
+            '@code' => $result->code,
+            '%server' => $server,
+            '%method' => "{$method} {$rest_path}",
+          ),
+          'Data:<pre>@data</pre>' => array('@data' => $data),
+          'Result:<pre>@result</pre>' => array('@result' => $result->data),
+        );
+
         _mollom_watchdog_multiple($messages, WATCHDOG_DEBUG);
-        return $result;
+        dpm($result);
+        return $result->data;
       }
     }
   }
@@ -1844,6 +1935,55 @@ function mollom($method, $data = array()) {
 }
 
 /**
+ * Retrieve the list of Mollom servers.
+ */
+function _mollom_get_servers() {
+  $servers = variable_get('mollom_servers', array());
+
+  // If the stored server list is empty, retrieve a new one.
+  if (empty($servers)) {
+    $servers = _mollom_retrieve_server_list();
+    // If API keys are invalid, a XML-RPC error code is returned.
+    if (!is_array($servers)) {
+      return $servers;
+    }
+
+    // @todo Move into calling code? But calling code doesn't know whether the
+    //   stored list was empty...
+    $messages[] = array(
+      'Refreshed servers: %servers' => array('%servers' => implode(', ', $servers)),
+    );
+
+    // Store the list of servers in the database.
+    variable_set('mollom_servers', $servers);
+  }
+
+  return $servers;
+}
+
+/**
+ * Converts a SimpleXmlIterator structure into an associative array.
+ */
+function _mollom_parse_xml($sxi) {
+  $a = array();
+  for ($sxi->rewind(); $sxi->valid(); $sxi->next()) {
+    $key = $sxi->key();
+    // Convert CamelCase to lowercase with underscores.
+    $key = strtolower(preg_replace('@(?<=[a-z])([A-Z])@', '_$1', $key));
+
+    // Recurse into scalar values.
+    if ($sxi->hasChildren()) {
+      $a[$key] = _mollom_parse_xml($sxi->current());
+    }
+    // Use a simple key/value pair for non-scalar values.
+    else {
+      $a[$key] = strval($sxi->current());
+    }
+  }
+  return $a;
+}
+
+/**
  * Log a Mollom system message.
  *
  * @param $parts
@@ -2080,53 +2220,62 @@ function mollom_field_extra_fields() {
  *     - 'response': An array with the response from Mollom.
  *     - 'markup': The markup of the CAPTCHA HTML.
  */
-function mollom_get_captcha($type, array $data = array()) {
-  $data += array(
-    'author_ip' => ip_address(),
-    'ssl' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on',
-  );
+function mollom_get_captcha(&$form_state) {
+  $key = 'captcha_url_' . $form_state['mollom']['captcha_type'];
+  if (empty($form_state['mollom']['response'][$key])) {
+    $data = array(
+      'type' => $form_state['mollom']['captcha_type'],
+      'author_ip' => ip_address(),
+      'ssl' => (int) isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on',
+    );
+    if (!empty($form_state['mollom']['response']['session_id'])) {
+      $data['session_id'] = $form_state['mollom']['response']['session_id'];
+    }
+    $url = mollom('mollom.getCaptcha', $data);
+
+    if (is_string($url)) {
+      $form_state['mollom']['response'][$key] = $url;
+      $form_state['mollom']['response']['session_id'] = substr(basename($url), 0, -4);
+    }
+    else {
+      return '';
+    }
+  }
+  else {
+    $url = $form_state['mollom']['response'][$key];
+  }
 
   // @todo Convert these to actual theme functions?
   $output = '';
-  switch ($type) {
+  switch ($form_state['mollom']['captcha_type']) {
     case 'audio':
-      $response = mollom('mollom.getAudioCaptcha', $data);
-      if ($response) {
-        $source = url(base_path() . drupal_get_path('module', 'mollom') . '/mollom-captcha-player.swf', array(
-          'query' => array('url' => $response['url']),
-          'external' => TRUE,
-        ));
-        $output = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="110" height="50">';
-        $output .= '<param name="allowFullScreen" value="false" />';
-        $output .= '<param name="movie" value="' . $source . '" />';
-        $output .= '<param name="loop" value="false" />';
-        $output .= '<param name="menu" value="false" />';
-        $output .= '<param name="quality" value="high" />';
-        $output .= '<param name="wmode" value="transparent" />';
-        $output .= '<param name="bgcolor" value="#ffffff" />';
-        $output .= '<embed src="' . $source . '" loop="false" menu="false" quality="high" wmode="transparent" bgcolor="#ffffff" width="110" height="50" align="baseline" allowScriptAccess="sameDomain" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer_de" />';
-        $output .= '</object>';
-
-        $output = '<span class="mollom-captcha-content mollom-audio-captcha">' . $output . '</span>';
-        $output .= ' (<a href="#" class="mollom-switch-captcha mollom-image-captcha">' . t('verify using image') . '</a>)';
-      }
+      $source = url(base_path() . drupal_get_path('module', 'mollom') . '/mollom-captcha-player.swf', array(
+        'query' => array('url' => $url),
+        'external' => TRUE,
+      ));
+      $output = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="110" height="50">';
+      $output .= '<param name="allowFullScreen" value="false" />';
+      $output .= '<param name="movie" value="' . $source . '" />';
+      $output .= '<param name="loop" value="false" />';
+      $output .= '<param name="menu" value="false" />';
+      $output .= '<param name="quality" value="high" />';
+      $output .= '<param name="wmode" value="transparent" />';
+      $output .= '<param name="bgcolor" value="#ffffff" />';
+      $output .= '<embed src="' . $source . '" loop="false" menu="false" quality="high" wmode="transparent" bgcolor="#ffffff" width="110" height="50" align="baseline" allowScriptAccess="sameDomain" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer_de" />';
+      $output .= '</object>';
+
+      $output = '<span class="mollom-captcha-content mollom-audio-captcha">' . $output . '</span>';
+      $output .= ' (<a href="#" class="mollom-switch-captcha mollom-image-captcha">' . t('verify using image') . '</a>)';
       break;
 
     case 'image':
-      $response = mollom('mollom.getImageCaptcha', $data);
-      if ($response) {
-        $captcha = theme('image', array('path' => url($response['url']), 'alt' => t('Type the characters you see in this picture.'), 'getsize' => FALSE));
-        $output = '<span class="mollom-captcha-content mollom-image-captcha">' . $captcha . '</span>';
-        $output .= ' (<a href="#" class="mollom-switch-captcha mollom-audio-captcha">' . t('verify using audio') . '</a>)';
-      }
+      $captcha = theme('image', array('path' => $url, 'alt' => t('Type the characters you see in this picture.'), 'getsize' => FALSE));
+      $output = '<span class="mollom-captcha-content mollom-image-captcha">' . $captcha . '</span>';
+      $output .= ' (<a href="#" class="mollom-switch-captcha mollom-audio-captcha">' . t('verify using audio') . '</a>)';
       break;
   }
 
-  return array(
-    'data' => $data,
-    'response' => $response,
-    'markup' => $output,
-  );
+  return $output;
 }
 
 /**
diff --git mollom.pages.inc mollom.pages.inc
index a7f02a5..8ca840d 100644
--- mollom.pages.inc
+++ mollom.pages.inc
@@ -23,7 +23,13 @@
  * @todo Add error handling.
  */
 function mollom_captcha_js($type, $form_build_id, $mollom_session_id) {
-  $captcha = mollom_get_captcha($type, array('session_id' => $mollom_session_id));
+  $dummy_state['mollom'] = array(
+    'captcha_type' => $type,
+    'response' => array(
+      'session_id' => $mollom_session_id,
+    ),
+  );
+  $captcha = mollom_get_captcha($dummy_state);
 
   // Update cached session id in the cached $form_state.
   // We rely on native form caching of Form API to store our Mollom session
@@ -36,19 +42,20 @@ function mollom_captcha_js($type, $form_build_id, $mollom_session_id) {
   // id. Therefore, we need to update the session id in the cached $form_state.
   // @todo Replace the entire CAPTCHA switch/refresh with new AJAX framework
   //   functionality.
-  if (!empty($captcha['response']['session_id'])) {
+  if (!empty($dummy_state['mollom']['response']['session_id'])) {
     if ($cache = cache_get('form_state_' . $form_build_id, 'cache_form')) {
       $form_state = $cache->data;
-      $form_state['mollom']['response']['session_id'] = $captcha['response']['session_id'];
+      $form_state['mollom']['response']['session_id'] = $dummy_state['mollom']['response']['session_id'];
+      // @todo Double-check $cid.
       cache_set('form_state_' . $form_build_id, $form_state, 'cache_form', REQUEST_TIME + 21600);
       // After successfully updating the cache, replace the original session id.
-      $mollom_session_id = $captcha['response']['session_id'];
+      $mollom_session_id = $form_state['mollom']['response']['session_id'];
     }
   }
 
   // Return new content and new session_id via JSON.
   $data = array(
-    'content' => $captcha['markup'],
+    'content' => $captcha,
     'session_id' => $mollom_session_id,
   );
   drupal_json_output($data);
