diff --git a/README.txt b/README.txt
index d00c767..abf2ad5 100644
--- a/README.txt
+++ b/README.txt
@@ -60,7 +60,8 @@ Design goals and concept
    as well as a JSON format. Modules may provide further resources and formats
    via hooks.
 
- * The module supports full CRUD (Create, Read, Update, Delete) for resources:
+ * The module supports full CRUD (Create, Read, Update, Delete) and quering for
+   resources:
  
      * Create: HTTP POST /<entity type name> (requires HTTP Content-Type header
        set to the MIME type of <format>)
@@ -75,6 +76,10 @@ Design goals and concept
 
      * Delete: HTTP DELETE /<entity type name>/<entity id>
 
+     * Query: HTTP GET /<entity type name>.<format>
+       or     HTTP GET /<entity type name> (requires HTTP Accept
+       header set to the MIME type of <format>)
+
  * The representation <format> can be json, xml etc.
 
  * The usual Drupal permission system is respected, thus permissions are checked
diff --git a/restws.entity.inc b/restws.entity.inc
index 1bda667..e4c07e2 100644
--- a/restws.entity.inc
+++ b/restws.entity.inc
@@ -70,6 +70,21 @@ interface RestWSResourceControllerInterface {
   public function delete($id);
 
   /**
+   * Query for a list of resources.
+   * If a resource doesn't wants to implement querying, then it should throw an
+   * RestWSException with the 501 HTTP status code.
+   *
+   * @param array $parameters
+   *   A list of parameter to query for, or NULL if all resources should be
+   *   returned.
+   * @param array $meta_controls
+   *   Control commands for sorting or paging.
+   * @return array
+   *   An array containing the ids of the resources;
+   */
+  public function query($filters = array(), $meta_controls = array());
+
+  /**
    * Determines access for a given operation and resource.
    *
    * @param string $op
@@ -86,7 +101,6 @@ interface RestWSResourceControllerInterface {
    */
   public function resource();
 }
-
 /**
  * Controller for entity-bases resources.
  */
@@ -156,6 +170,22 @@ class RestWSEntityResourceController implements RestWSResourceControllerInterfac
     entity_delete($this->entityType, $id);
   }
 
+  public function query($filters = array(), $meta_controls = array()) {
+    $entity_info = entity_get_info($this->resource());
+    $id_key = $entity_info['entity keys']['id'];
+    $limit = variable_get("restws_query_max_limit", 100);
+
+    $query =  new EntityFieldQuery();
+    $query->entityCondition('entity_type', $this->resource());
+    $query->pager($limit);
+    $query_result = $query->execute();
+    $query_result = isset($query_result[$this->resource()]) ? $query_result[$this->resource()] : array();
+
+    $result = array_keys($query_result);
+
+    return $result;
+  }
+
   public function access($op, $id) {
     return entity_access($op, $this->entityType, isset($id) ? $this->wrapper($id)->value() : NULL);
   }
diff --git a/restws.formats.inc b/restws.formats.inc
index dea8e83..1585fd3 100644
--- a/restws.formats.inc
+++ b/restws.formats.inc
@@ -58,6 +58,19 @@ interface RestWSFormatInterface {
    */
   public function deleteResource($resourceController, $id);
 
+  /**
+   * Query for a resource.
+   * If a format doesn't want to implement querying, then it should throw an
+   * RestWSException with the 501 HTTP status code.
+   *
+   * @param RestWSResourceControllerInterface $resourceController
+   *   The controller used to update the resource.
+   * @param array $parameters
+   *   The parameters for the query or NULL all existing resources should be
+   *   returned.
+   */
+  public function queryResource($resourceController, $parameters);
+
 
   /**
    * Returns the mime type of this format, e.g. 'application/json' or
@@ -117,6 +130,15 @@ abstract class RestWSBaseFormat implements RestWSFormatInterface {
     return $this->serialize(array());
   }
 
+  public function queryResource($resourceController, $parameters) {
+    $result = $resourceController->query($parameters, array());
+    $values = array();
+    foreach ($result as $id) {
+      $values['list'][] = self::getData($resourceController->wrapper($id));
+    }
+    return $this->serialize($values);
+  }
+
   public function mimeType() {
     return $this->formatInfo['mime type'];
   }
@@ -222,6 +244,21 @@ class RestWSFormatXML extends RestWSBaseFormat {
     return $xml->saveXML();
   }
 
+  public function queryResource($resourceController, $parameters) {
+    $xml = new DOMDocument('1.0', 'utf-8');
+    $element = $xml->createElement('list');
+
+    $values = $resourceController->query($parameters, array());
+    foreach ($values as $id) {
+      $item = $xml->createElement($resourceController->resource());
+      self::addToXML($xml, $item, $resourceController->wrapper($id));
+      $element->appendChild($item);
+    }
+
+    $xml->appendChild($element);
+    return $xml->saveXML();
+  }
+
   public function serialize($data) {
     // Return an empty XML document.
     $xml = new DOMDocument('1.0', 'utf-8');
@@ -340,6 +377,10 @@ class RestWSFormatRDF extends RestWSBaseFormat {
     throw new RestWSException('Not implemented', 501);
   }
 
+  public function queryResource($resourceController, $parameters) {
+    throw new RestWSException('Not implemented', 501);
+  }
+
   /**
    * Adds the data of the given wrapper to the given XML element.
    */
diff --git a/restws.module b/restws.module
index 8400999..5959a4c 100644
--- a/restws.module
+++ b/restws.module
@@ -119,13 +119,20 @@ function restws_handle_request($op, $format, $resource_name, $id = NULL, $payloa
       'payload' => &$payload,
     );
     drupal_alter('restws_request', $request);
-    if (user_access('access resource ' . $resource_name) && $resource->access($op, $id)) {
+
+    // Since there is now access callback for query we need to use view.
+    $access_op = $op == 'query' ? 'view' : $op;
+
+    if (user_access('access resource ' . $resource_name) && $resource->access($access_op, $id)) {
       try {
         $method = $op . 'Resource';
         if ($op == 'create') {
           print $format->$method($resource, $payload);
           drupal_add_http_header('Status', '201 Created');
         }
+        else if ($op == 'query') {
+          print $format->$method($resource, $payload);
+        }
         else {
           print $format->$method($resource, $id, $payload);
         }
@@ -216,6 +223,27 @@ function restws_menu_alter(&$items) {
         );
       }
     }
+
+    foreach (array_keys(restws_get_format_info()) as $format) {
+      // Resource base path urls with suffix.json and .xml for indexing.
+      if (isset($items["$resource.$format"])) {
+        // Prepend the page callback and the resource to the page arguments.
+        if (!isset($items["$resource.$format"]['page arguments'])) {
+          $items["$resource.$format"]['page arguments'] = array();
+        }
+        array_unshift($items["$resource.$format"]['page arguments'], $resource, $items["$resource.$format"]['page callback']);
+        $items["$resource.$format"]['page callback'] = 'restws_page_callback';
+
+      }
+      else {
+        $items["$resource.$format"] = array(
+          'page callback' => 'restws_page_callback',
+          'page arguments' => array($resource),
+          'access callback' => TRUE,
+          'type' => MENU_CALLBACK,
+        );
+      }
+    }
   }
 }
 
@@ -224,12 +252,17 @@ function restws_menu_alter(&$items) {
  */
 function restws_page_callback($resource, $page_callback = NULL) {
   $id_arg = arg(1);
+  $base_uri = arg(0);
   $format = FALSE;
+  $id = NULL;
   // Check for a appended .format string.
   if (($pos = strpos($id_arg, '.')) && $format_name = substr($id_arg, $pos + 1)) {
     $id = substr($id_arg, 0, $pos);
     $format = restws_format($format_name);
   }
+  elseif (($pos = strpos($base_uri, '.')) && $format_name = substr($base_uri, $pos + 1)) {
+    $format = restws_format($format_name);
+  }
   else {
     $id = $id_arg;
     switch ($_SERVER['REQUEST_METHOD']) {
@@ -276,7 +309,12 @@ function restws_page_callback($resource, $page_callback = NULL) {
         break;
 
       default:
-        $op = 'view';
+        if (!empty($id)) {
+          $op = 'view';
+        }
+        else {
+          $op  = 'query';
+        }
     }
     $payload = file_get_contents('php://input');
     if ($file = variable_get('restws_debug_log')) {
diff --git a/restws.test b/restws.test
index f6dd4bd..4630a2a 100644
--- a/restws.test
+++ b/restws.test
@@ -166,6 +166,37 @@ class RestWSTestCase extends DrupalWebTestCase {
   }
 
   /**
+   * Tests resource querying.
+   */
+  function testQuerying() {
+    $account = $this->drupalCreateUser(array('access content',
+        'bypass node access', 'access resource node')
+    );
+    $this->drupalLogin($account);
+    $nodes = array();
+    for($i = 0; $i < 5; $i++) {
+      $title = "node$i";
+      $nodes[$i] = $this->drupalCreateNode(array('title' => $title));
+    }
+
+    // Retrieve a list of nodes with json.
+    $result = $this->httpRequest('node.json', 'GET', $account);
+    $result_nodes = drupal_json_decode($result);
+    foreach ($result_nodes['list'] as $key => $node) {
+      $this->assertEqual($nodes[$key]->title, $node['title'], "Node title $key was received correctly.");
+    }
+    $this->assertResponse('200', 'HTTP response code is correct.');
+    $this->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');
+
+    // Retrieve a list of nodes with xml.
+    $result = $this->drupalGet("node.xml", array(), array('Accept: application/xml'));
+
+    for($i = 0; $i < 5; $i++) {
+      $this->assertRaw("<title>node$i</title>", 'XML has been generated.');
+    }
+  }
+
+  /**
    * Test that sensitive user data is hidden for the "access user profiles"
    * permission.
    */
