static private function _sendRequest($method, $url, $options)
    {
        $client = new Client();
        $options['timeout'] = self::TIMEOUT_S;
        $rawData = ['method' => $method, 'url' => $url];

        // make the request, catching Guzzle errors (except most GuzzleException instances)
        try {
            $response = $client->request($method, $url, $options);
        } catch (BadResponseException $e) {
            $response = $e->getResponse();
        } catch (GuzzleException $e) {
            if (strpos($e->getMessage(), 'cURL error 6: Could not resolve host') !== false) {
                throw new HostException('Unable to resolve host', $rawData);
            }
            throw $e;
        }
        $code = $response->getStatusCode();
        $body = $response->getBody();
        $json = json_decode($body);
        $rawData['code'] = $code;
        $rawData['body'] = "$body";

        // debug logger
        if (getenv('DEBUG') == '1' || getenv('DEBUG') == '2') {
            echo "# $code $method $url\n";
        }
        if (getenv('DEBUG') == '2') {
            echo "  $body\n";
        }

        // handle bad response data
        if ($code != 204 && empty($body)) {
            throw new RemoteException('Empty Document', $rawData);
        } else if ($code == 401) {
            throw new AuthException('Unauthorized', $rawData);
        } else if ($code == 403) {
            throw new NotFoundException('Forbidden', $rawData);
        } else if ($code == 404) {
            throw new NotFoundException('Not Found', $rawData);
        } else if ($code < 200) {
            throw new RemoteException('Informational', $rawData);
        } else if ($code > 299 && $code < 400) {
            throw new RemoteException('Redirection', $rawData);
        } else if ($code > 399 && $code < 500) {
            throw new RemoteException('Client Error', $rawData);
        } else if ($code > 499) {
            throw new RemoteException('Server Error', $rawData);
        } else if ($code != 204 && is_null($json) && json_last_error() != JSON_ERROR_NONE) {
            throw new RemoteException('JSON decode error', $rawData);
        }

        // return status code, JSON decoded body, and raw request/response data
        return [$code, $json, $rawData];
    }