public function request($method, $path, $data = array(), $headers = array())
    {
        $ch = curl_init(sprintf('https://%s%s', $this->api_host, $path));
        $this->__setRequestType($ch, $method);
        $this->__setHeaders($ch, $headers, $data);

        $response = curl_exec($ch);
        if ($response === false) {
            throw new \Exception(curl_error($ch));
        }
        $ch_info = curl_getinfo($ch);

        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $header = substr($response, 0, $header_size);
        $body = substr($response, $header_size);

        $error = false;

        if ($method == 'DELETE') {
            if ($ch_info['http_code'] != 302 && $ch_info['http_code'] != 200) {
                $error = true;
            }
        } else {
            if (!(($ch_info['http_code'] >= 200) && ($ch_info['http_code'] < 300))) {
                $error = true;
            }
        }

        if ($ch_info['http_code'] == 429) {
            $exception = new ThrottledRequestException();
            $response_headers = Helper::parseHttpHeaders($header);
            $exception->setResponseHeaders($response_headers);
            throw $exception;
        }

        if ($error) {
            $errorInfo = array_filter(array(curl_error($ch), $body));

            throw new \Exception('Request returned unexpected http code '. $ch_info['http_code'] . '. ' . join(', ', $errorInfo));
        }

        curl_close($ch);
        if (!defined('PHPUNIT_UPLOADCARE_TESTSUITE') && ($this->public_key == 'demopublic_key' || $this->secret_key == 'demoprivatekey')) {
            trigger_error('You are using the demo account. Please get an Uploadcare account at https://uploadcare.com/accounts/create/', E_USER_WARNING);
        }

        return json_decode($body);
    }