diff --git a/README.rst b/README.rst
index 426c9492..c06dbbd5 100644
--- a/README.rst
+++ b/README.rst
@@ -175,7 +175,9 @@ override ``settings.REST_FRAMEWORK``
         'DEFAULT_FILTER_BACKENDS': (
             'rest_framework_json_api.filters.OrderingFilter',
             'rest_framework_json_api.django_filters.DjangoFilterBackend',
+            'rest_framework.filters.SearchFilter',
         ),
+        'SEARCH_PARAM': 'filter[search]',
         'TEST_REQUEST_RENDERER_CLASSES': (
             'rest_framework_json_api.renderers.JSONRenderer',
         ),
diff --git a/docs/usage.md b/docs/usage.md
index 7591c2ab..39575202 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -35,7 +35,9 @@ REST_FRAMEWORK = {
     'DEFAULT_FILTER_BACKENDS': (
         'rest_framework_json_api.filters.OrderingFilter',
         'rest_framework_json_api.django_filters.DjangoFilterBackend',
+        'rest_framework.filters.SearchFilter',
     ),
+    'SEARCH_PARAM': 'filter[search]',
     'TEST_REQUEST_RENDERER_CLASSES': (
         'rest_framework_json_api.renderers.JSONRenderer',
     ),
@@ -102,7 +104,8 @@ class MyLimitPagination(JsonApiLimitOffsetPagination):
 
 ### Filter Backends
 
-_There are several anticipated JSON:API-specific filter backends in development. The first two are described below._
+Following are descriptions for two JSON:API-specific filter backends and documentation on suggested usage
+for a standard DRF keyword-search filter backend that makes it consistent with JSON:API.
 
 #### `OrderingFilter`
 `OrderingFilter` implements the [JSON:API `sort`](http://jsonapi.org/format/#fetching-sorting) and uses
@@ -151,12 +154,12 @@ Filters can be:
 - A related resource path can be used:
     `?filter[inventory.item.partNum]=123456` (where `inventory.item` is the relationship path)
 
-If you are also using [`rest_framework.filters.SearchFilter`](https://django-rest-framework.readthedocs.io/en/latest/api-guide/filtering/#searchfilter)
-(which performs single parameter searchs across multiple fields) you'll want to customize the name of the query
+If you are also using [`SearchFilter`](#searchfilter)
+(which performs single parameter searches across multiple fields) you'll want to customize the name of the query
 parameter for searching to make sure it doesn't conflict with a field name defined in the filterset.
 The recommended value is: `search_param="filter[search]"` but just make sure it's
 `filter[_something_]` to comply with the JSON:API spec requirement to use the filter
-keyword. The default is "search" unless overriden.
+keyword. The default is `REST_FRAMEWORK['SEARCH_PARAM']` unless overriden.
 
 The filter returns a `400 Bad Request` error for invalid filter query parameters as in this example
 for `GET http://127.0.0.1:8000/nopage-entries?filter[bad]=1`:
@@ -173,6 +176,15 @@ for `GET http://127.0.0.1:8000/nopage-entries?filter[bad]=1`:
     ]
 }
 ```
+#### `SearchFilter`
+
+To comply with JSON:API query parameter naming standards, DRF's
+[SearchFilter](https://django-rest-framework.readthedocs.io/en/latest/api-guide/filtering/#searchfilter) should
+be configured to use a `filter[_something_]` query parameter. This can be done by default by adding the
+SearchFilter to `REST_FRAMEWORK['DEFAULT_FILTER_BACKENDS']` and setting `REST_FRAMEWORK['SEARCH_PARAM']` or
+adding the `.search_param` attribute to a custom class derived from `SearchFilter`.  If you do this and also
+use [`DjangoFilterBackend`](#djangofilterbackend), make sure you set the same values for both classes.
+
 
 #### Configuring Filter Backends
 
@@ -182,11 +194,19 @@ in the [example settings](#configuration) or individually add them as `.filter_b
  ```python
 from rest_framework_json_api import filters
 from rest_framework_json_api import django_filters
+from rest_framework import SearchFilter
+from models import MyModel
 
 class MyViewset(ModelViewSet):
     queryset = MyModel.objects.all()
     serializer_class = MyModelSerializer
     filter_backends = (filters.OrderingFilter, django_filters.DjangoFilterBackend,)
+    filterset_fields = {
+        'id': ('exact', 'lt', 'gt', 'gte', 'lte', 'in'),
+        'descriptuon': ('icontains', 'iexact', 'contains'),
+        'tagline': ('icontains', 'iexact', 'contains'),
+    }
+    search_fields = ('id', 'description', 'tagline',)
 ```
 
 
diff --git a/example/settings/dev.py b/example/settings/dev.py
index e0a3e726..9e70aeb5 100644
--- a/example/settings/dev.py
+++ b/example/settings/dev.py
@@ -92,7 +92,9 @@
     'DEFAULT_FILTER_BACKENDS': (
         'rest_framework_json_api.filters.OrderingFilter',
         'rest_framework_json_api.django_filters.DjangoFilterBackend',
+        'rest_framework.filters.SearchFilter',
     ),
+    'SEARCH_PARAM': 'filter[search]',
     'TEST_REQUEST_RENDERER_CLASSES': (
         'rest_framework_json_api.renderers.JSONRenderer',
     ),
diff --git a/example/tests/test_filters.py b/example/tests/test_filters.py
index 273fe9ac..befba5e2 100644
--- a/example/tests/test_filters.py
+++ b/example/tests/test_filters.py
@@ -338,3 +338,143 @@ def test_filter_missing_rvalue_equal(self):
         dja_response = response.json()
         self.assertEqual(dja_response['errors'][0]['detail'],
                          "missing filter[headline] test value")
+
+    def test_search_keywords(self):
+        """
+        test for `filter[search]="keywords"` where some of the keywords are in the entry and
+        others are in the related blog.
+        """
+        response = self.client.get(self.url, data={'filter[search]': 'barnard field research'})
+        expected_result = {
+            'data': [
+                {
+                    'type': 'posts',
+                    'id': '7',
+                    'attributes': {
+                        'headline': 'ANTH3868X',
+                        'bodyText': 'ETHNOGRAPHIC FIELD RESEARCH IN NYC',
+                        'pubDate': None,
+                        'modDate': None},
+                    'relationships': {
+                        'blog': {
+                            'data': {
+                                'type': 'blogs',
+                                'id': '1'
+                            }
+                        },
+                        'blogHyperlinked': {
+                            'links': {
+                                'self': 'http://testserver/entries/7/relationships/blog_hyperlinked',  # noqa: E501
+                                'related': 'http://testserver/entries/7/blog'}
+                        },
+                        'authors': {
+                            'meta': {
+                                'count': 0
+                            },
+                            'data': []
+                        },
+                        'comments': {
+                            'meta': {
+                                'count': 0
+                            },
+                            'data': []
+                        },
+                        'commentsHyperlinked': {
+                            'links': {
+                                'self': 'http://testserver/entries/7/relationships/comments_hyperlinked',  # noqa: E501
+                                'related': 'http://testserver/entries/7/comments'
+                            }
+                        },
+                        'suggested': {
+                            'links': {
+                                'self': 'http://testserver/entries/7/relationships/suggested',
+                                'related': 'http://testserver/entries/7/suggested/'
+                            },
+                            'data': [
+                                {'type': 'entries', 'id': '1'},
+                                {'type': 'entries', 'id': '2'},
+                                {'type': 'entries', 'id': '3'},
+                                {'type': 'entries', 'id': '4'},
+                                {'type': 'entries', 'id': '5'},
+                                {'type': 'entries', 'id': '6'},
+                                {'type': 'entries', 'id': '8'},
+                                {'type': 'entries', 'id': '9'},
+                                {'type': 'entries', 'id': '10'},
+                                {'type': 'entries', 'id': '11'},
+                                {'type': 'entries', 'id': '12'}
+                            ]
+                        },
+                        'suggestedHyperlinked': {
+                            'links': {
+                                'self': 'http://testserver/entries/7/relationships/suggested_hyperlinked',  # noqa: E501
+                                'related': 'http://testserver/entries/7/suggested/'}
+                        },
+                        'tags': {
+                            'data': []
+                        },
+                        'featuredHyperlinked': {
+                            'links': {
+                                'self': 'http://testserver/entries/7/relationships/featured_hyperlinked',  # noqa: E501
+                                'related': 'http://testserver/entries/7/featured'
+                            }
+                        }
+                    },
+                    'meta': {
+                        'bodyFormat': 'text'
+                    }
+                }
+            ]
+        }
+        assert response.json() == expected_result
+
+    def test_search_multiple_keywords(self):
+        """
+        test for `filter[search]=keyword1...` (keyword1 [AND keyword2...])
+
+        See the four search_fields defined in views.py which demonstrate both searching
+        direct fields (entry) and following ORM links to related fields (blog):
+            `search_fields = ('headline', 'body_text', 'blog__name', 'blog__tagline')`
+
+        SearchFilter searches for items that match all whitespace separated keywords across
+        the many fields.
+
+        This code tests that functionality by comparing the result of the GET request
+        with the equivalent results used by filtering the test data via the model manager.
+        To do so, iterate over the list of given searches:
+        1. For each keyword, search the 4 search_fields for a match and then get the result
+           set which is the union of all results for the given keyword.
+        2. Intersect those results sets such that *all* keywords are represented.
+        See `example/fixtures/blogentry.json` for the test content that the searches are based on.
+        The searches test for both direct entries and related blogs across multiple fields.
+        """
+        for searches in ("research", "chemistry", "nonesuch",
+                         "research seminar", "research nonesuch",
+                         "barnard classic", "barnard ethnographic field research"):
+            response = self.client.get(self.url, data={'filter[search]': searches})
+            self.assertEqual(response.status_code, 200, msg=response.content.decode("utf-8"))
+            dja_response = response.json()
+            keys = searches.split()
+            # dicts keyed by the search keys for the 4 search_fields:
+            headline = {}      # list of entry ids where key is in entry__headline
+            body_text = {}     # list of entry ids where key is in entry__body_text
+            blog_name = {}     # list of entry ids where key is in entry__blog__name
+            blog_tagline = {}  # list of entry ids where key is in entry__blog__tagline
+            for key in keys:
+                headline[key] = [str(k.id) for k in
+                                 self.entries.filter(headline__icontains=key)]
+                body_text[key] = [str(k.id) for k in
+                                  self.entries.filter(body_text__icontains=key)]
+                blog_name[key] = [str(k.id) for k in
+                                  self.entries.filter(blog__name__icontains=key)]
+                blog_tagline[key] = [str(k.id) for k in
+                                     self.entries.filter(blog__tagline__icontains=key)]
+            union = []  # each list item is a set of entry ids matching the given key
+            for key in keys:
+                union.append(set(headline[key] + body_text[key] +
+                                 blog_name[key] + blog_tagline[key]))
+            # all keywords must be present: intersect the keyword sets
+            expected_ids = set.intersection(*union)
+            expected_len = len(expected_ids)
+            self.assertEqual(len(dja_response['data']), expected_len)
+            returned_ids = set([k['id'] for k in dja_response['data']])
+            self.assertEqual(returned_ids, expected_ids)
diff --git a/example/views.py b/example/views.py
index 98907902..d0980f01 100644
--- a/example/views.py
+++ b/example/views.py
@@ -104,6 +104,7 @@ class NonPaginatedEntryViewSet(EntryViewSet):
         'blog__tagline': rels,
     }
     filter_fields = filterset_fields  # django-filter<=1.1  (required for py27)
+    search_fields = ('headline', 'body_text', 'blog__name', 'blog__tagline')
 
 
 class EntryFilter(filters.FilterSet):