diff --git a/docs/rbtools/rbt/commands/api.rst b/docs/rbtools/rbt/commands/api.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ce3485376feab1ba0420be6af3328fb847bb5896
--- /dev/null
+++ b/docs/rbtools/rbt/commands/api.rst
@@ -0,0 +1,64 @@
+.. rbt-command:: rbtools.commands.api.API
+
+=======
+api
+=======
+
+:command:`rbt api` can do API GET/POST/PUT/PATCH/DELETE to the API resource found at the provided ``<path>``.
+
+
+Paths will be appended to the root of the API to generate a URL. For example,
+the ``/review-requests/123/`` path would result in a request to
+``http://example.com/api/review-requests/123/``.
+
+The path may also be replaced by a full URL. If :command:`rbt api-get`
+detects the path begins with ``http://`` or ``https://``, it will treat the
+path itself as the request URL.
+
+Query arguments may also be specified for the request. Each query argument
+takes the form of ``--query="field:value"``. For example::
+
+   $ # Make a request to http://example.com/api/review-requests/?counts-only=1
+   $ rbt api --get --query="counts-only:1" /review-requests/ 
+
+Everytime you use :command:`rbt api`, you can use exactly one of the options
+in ``--get``, ``--put``, ``--post``, ``--patch``and ``--delete`` as the main
+functionality.
+
+In order to get a pretty JSON output, you can add a ``--pretty`` option. For
+example::
+	$ # Make a API GET request to http://example.com/api/review-requests/
+	$ rbt api --get --pretty /review-requests/
+
+You can use ``--header`` option to set custom header for the request, the
+format is ``--header='header_name:content'``. You can use this option
+multiple times in an ``rbt`` command. For example::
+
+   $ # Make a GET request to http://example.com/api/review-requests/ using
+   $ # header User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0.
+   $ rbt api --get --header='User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0' /review-requests/
+	
+You can use ``--file`` option to upload local files, the format is:
+``--file='<local path>'``. You can use this option multiple times in an ``rbt`` command. For example::
+	
+	$ # Upload a local file at /path/test1.txt to http://example.com/api/review-requests/
+	$ rbt api --post --file=/path/test1.txt http://example.com/api/review-requests/
+
+You can use ``--data`` option to POST/PUT key-value pairs, the format is:
+``--data="key1=value1"``. You can use this option multiple times in
+an ``rbt`` command. For example::
+
+	$ # Post key-value pair "aa"="bb" to http://example.com/api/review-requests/
+	$ rbt api --post --data='aa=bb' http://example.com/api/review-requests/
+
+	$ # Put key-value pair "aa"="bb" to http://example.com/api/review-requests/aa
+	$ rbt api --put --data='aa=bb' http://example.com/api/review-requests/aa
+
+``--delete`` option is used for deleting content from the API resource. For example::
+
+	$ # Delete data at http://example.com/api/review-requests/aa
+	$ rbt api --delete http://example.com/api/review-requests/aa
+
+
+.. rbt-command-usage::
+.. rbt-command-options::
diff --git a/rbtools/api/client.py b/rbtools/api/client.py
index dcee61ceff7d17e36864101f917f368980e91a81..c85bcb18e4d97e3209666636e606a96dd349f512 100644
--- a/rbtools/api/client.py
+++ b/rbtools/api/client.py
@@ -20,11 +20,20 @@ class RBClient(object):
     def get_root(self, *args, **kwargs):
         return self._transport.get_root(*args, **kwargs)
 
-    def get_path(self, path, *args, **kwargs):
-        return self._transport.get_path(path, *args, **kwargs)
-
     def get_url(self, url, *args, **kwargs):
-        return self._transport.get_url(url, *args, **kwargs)
+        return self._transport.get_url(url, *args)
+
+    def post_url(self, url, *args, **kwargs):
+        return self._transport.post_url(url, *args)
+
+    def delete_url(self, url, *args, **kwargs):
+        return self._transport.delete_url(url, *args)
+
+    def put_url(self, url, *args, **kwargs):
+        return self._transport.put_url(url, *args)
+
+    def patch_url(self, url, *args, **kwargs):
+        return self._transport.patch_url(url, *args)
 
     def login(self, *args, **kwargs):
         return self._transport.login(*args, **kwargs)
diff --git a/rbtools/api/request.py b/rbtools/api/request.py
index befcbabf84d269f3a0c9b0418e7020af7f3cdde9..5fc82aeedc441b127a1528ee6ce80b20afcc863f 100644
--- a/rbtools/api/request.py
+++ b/rbtools/api/request.py
@@ -231,7 +231,7 @@ class HttpRequest(object):
         }
 
     def encode_multipart_formdata(self):
-        """Encode the request into a multi-aprt form-data payload.
+        """Encode the request into a multi-part form-data payload.
 
         Returns:
             tuple:
diff --git a/rbtools/api/transport/sync.py b/rbtools/api/transport/sync.py
index db65884f0679210dbdc1ff81fb45a7049381fc17..91a3610ab4d32daab398c52dd244a2b8498b73f4 100644
--- a/rbtools/api/transport/sync.py
+++ b/rbtools/api/transport/sync.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 import logging
+import six
 
 from rbtools.api.decode import decode_response
 from rbtools.api.factory import create_resource
@@ -53,21 +54,258 @@ class SyncTransport(Transport):
     def get_root(self):
         return self._execute_request(HttpRequest(self.server.url))
 
-    def get_path(self, path, *args, **kwargs):
-        if not path.endswith('/'):
-            path = path + '/'
+    def get_url(self, url, *args):
+        """Handle API GET request with given URL.
 
-        if path.startswith('/'):
-            path = path[1:]
+        Args:
+            url (unicode):
+                The URL being requested.
 
-        return self._execute_request(
-            HttpRequest(self.server.url + path, query_args=kwargs))
+            *args (tuple):
+                The elements of this tuple are the following:
 
-    def get_url(self, url, *args, **kwargs):
-        if not url.endswith('/'):
-            url = url + '/'
+                ``data_dict`` (dictionary):
+                    This dictionary have string keys and values. It contains
+                    data to be POST/PUT/PATCH to the API resource.
+                    Format: {"key":"value"}.
 
-        return self._execute_request(HttpRequest(url, query_args=kwargs))
+                ``custom_header`` (dictionary):
+                    This dictionary have string keys and values.
+                    It contains headers to be sent with the HTTP request.
+                    Format: {"header_field":"header_content"}.
+
+                ``query_arguments`` (dictionary):
+                    This dictionary have string keys and values.
+                    Format: {"field":"value"}.
+
+                ``send_file`` (list of dictionary):
+                    It contains zero or more dictionaries,
+                    each dictionary contains two keys:
+
+                        ``filename`` (:py:class:`unicode`):
+                            The file's name.
+
+                        ``content`` (byte):
+                            The file's byte content.
+
+        Returns:
+            An rbtools.api.resource object.
+        """
+        header_dict = args[1]
+        query_dict = args[2]
+
+        request = HttpRequest(url,
+                              query_args=query_dict,
+                              headers=header_dict)
+
+        return self._execute_request(request)
+
+    def post_url(self, url, *args):
+        """Handle API POST request with given URL.
+
+        Args:
+            url (unicode):
+                The URL being requested.
+
+            *args (tuple):
+                The elements of this tuple are the following:
+
+                ``data_dict`` (dictionary):
+                    This dictionary have string keys and values. It contains
+                    data to be POST/PUT/PATCH to the API resource.
+                    Format: {"key":"value"}.
+
+                ``custom_header`` (dictionary):
+                    This dictionary have string keys and values.
+                    It contains headers to be sent with the HTTP request.
+                    Format: {"header_field":"header_content"}.
+
+                ``query_arguments`` (dictionary):
+                    This dictionary have string keys and values.
+                    Format: {"field":"value"}.
+
+                ``send_file`` (list of dictionary):
+                    It contains zero or more dictionaries,
+                    each dictionary contains two keys:
+
+                        ``filename`` (:py:class:`unicode`):
+                            The file's name.
+
+                        ``content`` (byte):
+                            The file's byte content.
+
+        Returns:
+            An rbtools.api.resource object.
+        """
+        body_dict = args[0]
+        header_dict = args[1]
+        query_dict = args[2]
+        file_path_list = args[3]
+
+        request = HttpRequest(url,
+                              method='POST',
+                              query_args=query_dict,
+                              headers=header_dict)
+
+        for i, (k, v) in enumerate(six.iteritems(body_dict)):
+            request.add_field(k, v)
+
+        if file_path_list:
+            for each_file_dict in file_path_list:
+                request.add_file("path",
+                                 each_file_dict['filename'],
+                                 each_file_dict['content'],)
+
+        return self._execute_request(request)
+
+    def patch_url(self, url, *args):
+        """Handle API PATCH request with given URL.
+
+        Args:
+            url (unicode):
+                The URL being requested.
+
+            *args (tuple):
+                The elements of this tuple are the following:
+
+                ``data_dict`` (dictionary):
+                    This dictionary have string keys and values. It contains
+                    data to be POST/PUT/PATCH to the API resource.
+                    Format: {"key":"value"}.
+
+                ``custom_header`` (dictionary):
+                    This dictionary have string keys and values.
+                    It contains headers to be sent with the HTTP request.
+                    Format: {"header_field":"header_content"}.
+
+                ``query_arguments`` (dictionary):
+                    This dictionary have string keys and values.
+                    Format: {"field":"value"}.
+
+                ``send_file`` (list of dictionary):
+                    It contains zero or more dictionaries,
+                    each dictionary contains two keys:
+
+                        ``filename`` (:py:class:`unicode`):
+                            The file's name.
+
+                        ``content`` (byte):
+                            The file's byte content.
+
+        Returns:
+            An rbtools.api.resource object.
+        """
+        body_dict = args[0]
+        header_dict = args[1]
+        query_dict = args[2]
+
+        request = HttpRequest(url,
+                              method='PATCH',
+                              query_args=query_dict,
+                              headers=header_dict)
+
+        for i, (k, v) in enumerate(six.iteritems(body_dict)):
+            request.add_field(k, v)
+
+        return self._execute_request(request)
+
+    def put_url(self, url, *args):
+        """Handle API PUT request with given URL.
+
+        Args:
+            url (unicode):
+                The URL being requested.
+
+            *args (tuple):
+                The elements of this tuple are the following:
+
+                ``data_dict`` (dictionary):
+                    This dictionary have string keys and values. It contains
+                    data to be POST/PUT/PATCH to the API resource.
+                    Format: {"key":"value"}.
+
+                ``custom_header`` (dictionary):
+                    This dictionary have string keys and values.
+                    It contains headers to be sent with the HTTP request.
+                    Format: {"header_field":"header_content"}.
+
+                ``query_arguments`` (dictionary):
+                    This dictionary have string keys and values.
+                    Format: {"field":"value"}.
+
+                ``send_file`` (list of dictionary):
+                    It contains zero or more dictionaries,
+                    each dictionary contains two keys:
+
+                        ``filename`` (:py:class:`unicode`):
+                            The file's name.
+
+                        ``content`` (byte):
+                            The file's byte content.
+
+        Returns:
+            An rbtools.api.resource object.
+        """
+        body_dict = args[0]
+        header_dict = args[1]
+        query_dict = args[2]
+
+        request = HttpRequest(url,
+                              method='PUT',
+                              query_args=query_dict,
+                              headers=header_dict)
+
+        for i, (k, v) in enumerate(six.iteritems(body_dict)):
+            request.add_field(k, v)
+
+        return self._execute_request(request)
+
+    def delete_url(self, url, *args):
+        """Handle API DELETE request with given URL.
+
+        Args:
+            url (unicode):
+                The URL being requested.
+
+            *args (tuple):
+                The elements of this tuple are the following:
+
+                ``data_dict`` (dictionary):
+                    This dictionary have string keys and values. It contains
+                    data to be POST/PUT/PATCH to the API resource.
+                    Format: {"key":"value"}.
+
+                ``custom_header`` (dictionary):
+                    This dictionary have string keys and values.
+                    It contains headers to be sent with the HTTP request.
+                    Format: {"header_field":"header_content"}.
+
+                ``query_arguments`` (dictionary):
+                    This dictionary have string keys and values.
+                    Format: {"field":"value"}.
+
+                ``send_file`` (list of dictionary):
+                    It contains zero or more dictionaries,
+                    each dictionary contains two keys:
+
+                        ``filename`` (:py:class:`unicode`):
+                            The file's name.
+
+                        ``content`` (byte):
+                            The file's byte content.
+
+        Returns:
+            An rbtools.api.resource object.
+        """
+        header_dict = args[1]
+        query_dict = args[2]
+
+        request = HttpRequest(url,
+                              method='DELETE',
+                              query_args=query_dict,
+                              headers=header_dict)
+
+        return self._execute_request(request)
 
     def login(self, username, password):
         self.server.login(username, password)
diff --git a/rbtools/commands/__init__.py b/rbtools/commands/__init__.py
index 3a1ce8f26ea1b9e6a9c2d725bd91d087c468374f..e3cd7e3d0d260331865619234b352d0f401d850b 100644
--- a/rbtools/commands/__init__.py
+++ b/rbtools/commands/__init__.py
@@ -743,7 +743,6 @@ class Command(object):
         """
         parser = self.create_arg_parser(argv)
         self.options = parser.parse_args(argv[2:])
-
         args = self.options.args
 
         # Check that the proper number of arguments have been provided.
diff --git a/rbtools/commands/api.py b/rbtools/commands/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..d36fdc49fad3769b37a5dc4a9f661ee8c4c55ffa
--- /dev/null
+++ b/rbtools/commands/api.py
@@ -0,0 +1,287 @@
+from __future__ import print_function, unicode_literals
+
+import email
+import json
+import os
+import re
+
+from urllib.parse import urljoin
+
+from rbtools.api.errors import APIError
+from rbtools.commands import (Command,
+                              CommandError,
+                              CommandExit,
+                              Option,
+                              OptionGroup)
+
+
+class API(Command):
+    """Send HTTP request on API resources using Command line tools"""
+
+    name = 'api'
+    author = 'The Review Board Project'
+    description = ('Perform Review Board API requests.')
+    args = '<path>'
+
+    option_list = [
+        OptionGroup(
+            name='API Options',
+            description='Main functionality of API command. ',
+            option_list=[
+                Option('--get',
+                       action='store_true',
+                       dest='request_get',
+                       default=False,
+                       help='Send a HTTP GET request to an API. '
+                            'Usage: "rbt api --get".'),
+                Option('--post',
+                       action='store_true',
+                       dest='request_post',
+                       default=False,
+                       help=('Send a HTTP POST request to an API. '
+                             'Usage: "rbt api --post".')),
+                Option('--delete',
+                       action='store_true',
+                       dest='request_delete',
+                       default=False,
+                       help=('Send a HTTP DELETE request to an API. '
+                             'Usage: "rbt api --delete".')),
+                Option('--patch',
+                       action='store_true',
+                       dest='request_patch',
+                       default=False,
+                       help='Send a HTTP PATCH request to an API. '
+                            'Usage: "rbt api --patch".'),
+                Option('--put',
+                       action='store_true',
+                       dest='request_put',
+                       default=False,
+                       help='Send a HTTP PUT request to an API. '
+                            'Usage: "rbt api --put".'),
+            ]
+        ),
+        Option('--pretty',
+               action='store_true',
+               dest='pretty_print',
+               default=False,
+               help='Pretty prints the resulting API payload.'),
+        Option('--data',
+               action='append',
+               dest='store_data',
+               default=None,
+               help=('Specify what data to be sent to the server, '
+                     'used by POST/PUT. Usage: --data="key1=value1".')),
+        Option('--header',
+               action='append',
+               dest='custom_header',
+               default=None,
+               help=('Customize your request header. '
+                     'Usage: --header="header_name:content".')),
+        Option('--query',
+               action='append',
+               dest='query_arguments',
+               default=None,
+               help=('Specify your query arguments. '
+                     'Usage: --query="field:value".')),
+        Option('--file',
+               action='append',
+               dest='send_file',
+               default=None,
+               help=('Specify the path of the file to be sent to the server,'
+                     ' used by POST/PUT. Usage: --file="filepath".')),
+
+        Command.server_options,
+    ]
+
+    def main(self, path, *args):
+        """Run api command with give path and arguments.
+
+        Args:
+            path (unicode):
+                The URL or path being requested.
+
+            *args (tuple)
+                Query args to pass to
+                :py:meth:`~rbtools.api.request.HttpRequest.__init__`.
+
+        Raises:
+            rbtools.commands.ParseError:
+                Query argument string has unexpected format.
+        """
+        if self.options.server:
+            server_url = self.options.server
+        else:
+            repository_info, tool = self.initialize_scm_tool()
+            server_url = self.get_server_url(repository_info, tool)
+
+        # This command's main functionality can only be from
+        # GET, POST, DELETE, PATCH and PUT at this moment.
+        main_command_options = [self.options.request_get,
+                                self.options.request_post,
+                                self.options.request_delete,
+                                self.options.request_patch,
+                                self.options.request_put]
+        command_list = ['get', 'post', 'delete', 'patch', 'put']
+
+        sum_result = sum(main_command_options)
+
+        # This means too many options from our command list are provided.
+        if sum_result > 1:
+            print('Too many types of requests provided, '
+                  'need exactly one type of request command.')
+            return
+        # This means no options from our available http method list.
+        elif sum_result < 1:
+            print('You should provide exactly one type of request, '
+                  'no request command provided.')
+            return
+
+        command = command_list[main_command_options.index(True)]
+        self._handle_request_command(command, path, server_url)
+
+    def _dumps(self, payload):
+        """Convert payload to JSON format.
+
+        Args:
+            payload (dict):
+                The response that is needed to be converted to JSON format
+                and display in the terminal.
+
+                If the :option:`--pretty` option is passed to the command,
+                it will get a pretty appearance in the terminal, otherwise not.
+
+        Returns:
+            string:
+            The response in string format.
+        """
+        if self.options.pretty_print:
+            return json.dumps(payload, sort_keys=True, indent=4)
+        else:
+            return json.dumps(payload)
+
+    def _handle_request_command(self, command, path, server_url):
+        """Handle HTTP request.
+
+        This will first combine data, custom headers, file names into a
+        dictionary called argument_dict. Then handle the request corresponding
+        to the argument command by calling different request handlers.
+        This method then receives resources returned by these handlers,
+        gets the response inside, converts to JSON format and prints these
+        JSON out to the terminal.
+
+        Args:
+            command (unicode):
+                The command type being executed.
+
+            path (unicode):
+                The path being accessed.
+
+            server_url (unicode):
+                The server URL being used.
+        """
+        api_client, api_root = self.get_api(server_url)
+        data_dict = {}
+        header_dict = {}
+        query_dict = {}
+        send_file = []
+
+        try:
+            # If custom_header is used, process each string
+            # to get a dict of (key, value) pairs. Then, pass this dict
+            # to our request handler.
+            if self.options.custom_header:
+                header_string = '\n'.join(self.options.custom_header)
+                header_dict = dict(email.message_from_string(header_string))
+
+            # If query_arguments is used, process each string
+            # to get a dict of (key, value) pairs. Then, pass this dict
+            # to our request handler.
+            if self.options.query_arguments:
+                query_string = '\n'.join(self.options.query_arguments)
+                query_dict = dict(email.message_from_string(query_string))
+
+            # If store_data is used, first check whether each string
+            # has the correct format <key=value>. Then process each string
+            # to get a dict of (key, value) pairs. Then, pass this dict
+            # to our request handler.
+            if self.options.store_data:
+                temp = '[a-zA-Z0-9\s_\\.\-\(\):]'
+                regex = r'(%s)+=(%s)+' % (temp, temp)
+                data_arg_re = re.compile(regex)
+                data = self.options.store_data
+
+                for each_pair in data:
+                    if not data_arg_re.match(each_pair):
+                        raise ValueError('Data not supported.')
+
+                if data:
+                    for i in data:
+                        temp = i.split("=")
+                        data_dict[temp[0].strip()] = temp[1].strip()
+
+            # If send_file is used, that means we will add file to
+            # our request. First, check whether the file exists in
+            # your local, if exists, for each file path,
+            # read the content, conbine the filename and content
+            # into a dict, add that dict to the full_file_list.
+            # Then, pass this list to our request handler.
+            if self.options.send_file:
+                full_file_list = []
+
+                for each in self.options.send_file:
+                    if not os.path.exists(each):
+                        error_msg = 'File on path %s not exists.' % each
+                        raise CommandError(error_msg)
+
+                    file_name = each.split(os.sep)[-1]
+
+                    with open(each, 'rb') as f:
+                        content = f.read()
+
+                    full_file_list.append({
+                        'filename': file_name,
+                        'content': content,
+                    })
+
+                send_file = full_file_list
+
+            full_url = path
+            client_method_name = '%s_url' % command
+            if not path.startswith(('http://', 'https://')):
+                if path.startswith('/'):
+                    path = path[1:]
+
+                full_url = urljoin(server_url, path)
+
+            if not full_url.endswith('/'):
+                full_url = '%s/' % full_url
+
+            client_method = getattr(api_client, client_method_name)
+            assert client_method is not None
+
+            resource = client_method(full_url,
+                                     data_dict,
+                                     header_dict,
+                                     query_dict,
+                                     send_file)
+        except CommandError as e:
+            print(e)
+            raise CommandExit(1)
+
+        except APIError as e:
+            if e.rsp:
+                raise CommandExit(1)
+            else:
+                print(self._dumps(e.rsp))
+                raise CommandError('Could not finish request to the requested '
+                                   'resource: %s.' % e)
+        except ValueError as e:
+            print(e)
+            raise CommandExit(1)
+
+        except Exception as e:
+            print(e)
+            raise CommandExit(1)
+
+        if resource:
+            print(self._dumps(resource.rsp))
diff --git a/setup.py b/setup.py
index fbff0add17f6bf4b212dd12c3228730baa0cd9d4..71702b7c6457780b64ef439858d25afa339cb260 100755
--- a/setup.py
+++ b/setup.py
@@ -63,6 +63,7 @@ elif 0x03000000 <= sys.hexversion < 0x03060000:
 
 rb_commands = [
     'api-get = rbtools.commands.api_get:APIGet',
+    'api = rbtools.commands.api:API'
     'alias = rbtools.commands.alias:Alias',
     'attach = rbtools.commands.attach:Attach',
     'clear-cache = rbtools.commands.clearcache:ClearCache',
