def geocode(self, pq):
        """
        :arg PlaceQuery pq: PlaceQuery instance
        :rtype: tuple
        :returns: post-processed list of Candidate objects and
                  and UpstreamResponseInfo object if an API call was made.

                  Examples:

                  Preprocessor throws out request::

                      ([], None)

                  Postprocessor throws out some candidates::

                      ([<Candidate obj>, <Candidate obj>, ...], <UpstreamResponseInfo obj>)

                  Postprocessor throws out all candidates::

                      ([], <UpstreamResponseInfo obj>)

                  An exception occurs while making the API call::

                      ([], <UpstreamResponseInfo obj>)

        """
        processed_pq = copy.copy(pq)
        for p in self._preprocessors:
            processed_pq = p.process(processed_pq)
            if not processed_pq:
                return [], None
        upstream_response_info = UpstreamResponseInfo(self.get_service_name(),
                                                      processed_pq)
        try:
            start = datetime.now()
            candidates = self._geocode(processed_pq)
            end = datetime.now()
            response_time_sec = (end - start).total_seconds()
            upstream_response_info.set_response_time(1000 * response_time_sec)
        except Exception:
            upstream_response_info.set_success(False)
            upstream_response_info.errors.append(format_exc())
            return [], upstream_response_info
        if len(candidates) > 0:
            for p in self._postprocessors:  # apply universal candidate postprocessing
                candidates = p.process(candidates)  # merge lists
        return candidates, upstream_response_info