### ScanModuleParametersList example Source: https://docs.rapid7.com/appspider/attack-module-api-overview Example structure for ScanModuleParametersList in a scan configuration file. Each ScanModuleParameters element has a ModuleId and a CustomParameterList. Use the null Guid (all zeros) to make parameters available to all modules. ```xml FA48FA7E33B6407B8793D1993C3FF582 Module Specific Parameter 1 Specific Value 1 Module Specific Parameter 2 Specific Value 2 00000000000000000000000000000000 Global Parameter 1 Global Value 1 Global Parameter 2 Global Value 2 ``` -------------------------------- ### Account Clients Response Example Source: https://docs.rapid7.com/appspider/account-operations Response example for the account clients endpoint. Shows the structure of the returned object, including an Account object with a Clients array. Each client item contains Roles, NotInRoles, Groups, and ClientInfo properties. The example includes two client items with identical structure. ```json { "type": "object", "properties": { "Account": { "type": "object", "properties": { "Clients": { "type": "array", "items": [ { "type": "object", "properties": { "Roles": { "type": "array", "items": [ { "type": "object", "properties": { "RoleId": { "type": "string" }, "RoleName": { "type": "string" } }, "required": [ "RoleId", "RoleName" ] } ] }, "NotInRoles": { "type": "array", "items": [ { "type": "object", "properties": { "RoleId": { "type": "string" }, "RoleName": { "type": "string" } }, "required": [ "RoleId", "RoleName" ] } ] }, "Groups": { "type": "array", "items": {} }, "ClientInfo": { "type": "object", "properties": { "ClientId": { "type": "string" }, "ClientName": { "type": "string" } }, "required": [ "ClientId", "ClientName" ] } }, "required": [ "Roles", "NotInRoles", "Groups", "ClientInfo" ] }, { "type": "object", "properties": { "Roles": { "type": "array", "items": [ { "type": "object", "properties": { "RoleId": { "type": "string" }, "RoleName": { "type": "string" } }, "required": [ "RoleId", "RoleName" ] } ] }, "NotInRoles": { "type": "array", "items": [ { "type": "object", "properties": { "RoleId": { "type": "string" }, "RoleName": { "type": "string" } }, "required": [ "RoleId", "RoleName" ] } ] }, "Groups": { "type": "array", "items": {} }, "ClientInfo": { "type": "object", "properties": { "ClientId": { "type": "string" }, "ClientName": { "type": "string" } }, "required": [ "ClientId", "ClientName" ] } }, "required": [ "Roles", "NotInRoles", "Groups", "ClientInfo" ] } ] } }, "required": [ "Clients" ] } }, "required": [ "Account" ] } ``` -------------------------------- ### GET GetEngineGroupsForClient response example Source: https://docs.rapid7.com/appspider/scan-engine-group-operations Response example for GET /AppSpiderEnterprise/rest/v1/EngineGroup/GetEngineGroupsForClient. Returns engine groups restricted to the client the user has access to. Requires user permissions. ```json { "Type": "object", "properties": { "EngineGroups": { "type": "array", "properties": { "Id": { "type": "string", "required": true }, "Name": { "type": "string", "required": true, "maxLength": 128 }, "Description": { "type": ["string", "null"], "required": true, "maxLength": 256 }, "Monitoring": { "type": "boolean", "required": true } } }, "additionalProperties": false }, "IsSuccess": { "type": "boolean", "required": true }, "ErrorMessage": { "type": ["string", "null"], "required": true }, "Reason": { "type": ["string", "null"], "required": true } }, "additionalProperties": false } ``` -------------------------------- ### Get Scan Configs Response Example Source: https://docs.rapid7.com/appspider/scan-configuration-operations Response example for the get scan configs operation. Shows the JSON schema of the response object containing an array of scan configs with properties like Id, Name, ClientId, EngineGroupId, Monitoring, IsApproveRequired, DefendEnabled, MonitoringDelay, MonitoringTriggerScan, and Xml. ```json { "type": "object", "properties": { "Configs": { "type": "array", "items": { "type": "object", "properties": { "Id": { "type":"string", "required": true }, "Name": { "type":"string", "required": true }, "ClientId": { "type":"string", "required": true }, "EngineGroupId": { "type":"string", "required": true }, "Monitoring": { "type": "boolean", "required": true }, "IsApproveRequired": { "type": "boolean", "required": true }, "DefendEnabled": { "type": "boolean", "required": true }, "MonitoringDelay": { "type": "integer", "required": true }, "MonitoringTriggerScan": { "type": "boolean", "required": true }, "Xml": { "type": "string", "required": true } } }, "additionalProperties": false }, "IsSuccess": { "type": "boolean", "required": true }, "ErrorMessage": { "type":["string", "null"], "required": true }, "Reason": { "type":["string", "null"], "required": true } }, "additionalProperties": false } ``` -------------------------------- ### GetConfigs paged response example Source: https://docs.rapid7.com/appspider/scan-configuration-operations Response example for the GetConfigs paged endpoint. Returns a list of scan configs with the same fields as the non-paged version, plus TotalCount, TotalPages, and Links for pagination. ```json { "type": "object", "properties": { "Configs": { "type": "array", "items": { "type": "object", "properties": { "Id": { "type":"string", "required": true }, "Name": { "type":"string", "required": true }, "ClientId": { "type":"string", "required": true }, "EngineGroupId": { "type":"string", "required": true }, "Monitoring": { "type": "boolean", "required": true }, "IsApproveRequired": { "type": "boolean", "required": true }, "DefendEnabled": { "type": "boolean", "required": true }, "MonitoringDelay": { "type": "integer", "required": true }, "MonitoringTriggerScan": { "type": "boolean", "required": true }, "Xml": { "type": "string", "required": true } } }, "additionalProperties": false }, "IsSuccess": { "type": "boolean", "required": true }, "ErrorMessage": { "type":["string", "null"], "required": true }, "Reason": { "type":["string", "null"], "required": true }, "TotalCount": { "type": "integer", "required": true }, "TotalPages": { "type": "integer", "required": true }, "Links": { "type": "array", "items": { "Rel": { "type":["string", "null"], "required": true }, "Href": { "type":["string", "null"], "required": true } } } }, }, "additionalProperties": false } ``` -------------------------------- ### ScanConfig XML example Source: https://docs.rapid7.com/appspider/advanced-options A full example of a ScanConfig XML file with settings for crawling, logging, and browser behavior. It includes the root element and nested and sections. ```xml webscantest 7.0 1 0 0 0 0 0 1 Chrome 1073741824 0 100 5000 -1 500 50 100 300 100 300 250 400 2 15 -1 25 5 500 2500 500 500 500 500 60000 4000 4 600 400 100 400 2 100 10 7 Smart (page|resource) (you requested )?(was not|cannot be) found|Page not found|404(.0)? - ((File (or directory )?not found)|(Not Found))|HTTP Status 404|404 Not Found ]|\d([-+]|%2[bd])\d|repeat\(|alert\(|/x\w{7}\.txt|window.location|%20(AND|OR)%20|%3cscript|(ping|echo)%20|javascript(%3a|:)|%0d%0a]]> (auth|log[ -]?(in|on)|sign[ -]?(in|on)|profile|account|transfer|admin) 0 Case Sensitive 0 0 1 0 0 0 0 0 0 0 0 0 0.95 0.8 Disabled 1 1 1 1 1000 0 1 ReactJS 2 1 1 createReactRootIndex react ``` -------------------------------- ### GetClients Response Example Source: https://docs.rapid7.com/appspider/client-operations Response example for the GetClients endpoint. Shows the JSON schema returned on success, including the Clients array with ClientId and ClientName, plus IsSuccess, Reason, ErrorMessage, and Links fields. ```json { "type": "object", "properties": { "Clients": { "type": "array", "items": [ { "type": "object", "properties": { "ClientId": { "type": "string" }, "ClientName": { "type": "string" } }, "required": [ "ClientId", "ClientName" ] } ] }, "IsSuccess": { "type": "boolean" }, "Reason": { "type": "null" }, "ErrorMessage": { "type": "null" }, "Links": { "type": "array", "items": [ { "type": "object", "properties": { "Rel": { "type": "string" }, "Href": { "type": "string" } }, "required": [ "Rel", "Href" ] } ] } }, "required": [ "Clients", "IsSuccess", "Reason", "ErrorMessage", "Links" ] } ``` -------------------------------- ### GET GetAllEngineGroups response example Source: https://docs.rapid7.com/appspider/scan-engine-group-operations Response example for GET /AppSpiderEnterprise/rest/v1/GetAllEngineGroups. Returns a list of engine groups with Id, Name, Description, and Monitoring fields, plus IsSuccess, ErrorMessage, and Reason. Requires System Administrator credentials. ```json { "type": "object", "properties": { "EngineGroups": { "type": "array", "properties": { "Id": { "type": "string", "required": true }, "Name": { "type": "string", "required": true, "maxLength": 128 }, "Description": { "type": ["string", "null"] "required": true, "maxLength": 256 }, "Monitoring": { "type": "boolean", "required": true, } } }, "additionalProperties": false }, "IsSuccess": { "type": "boolean", "required": true }, "ErrorMessage": { "type": ["string", "null"], "required": true "Reason": { "type": ["string", "null"], "required": true } }, "additionalProperties": false } ``` -------------------------------- ### C# REST API workflow for AppSpider Enterprise Source: https://docs.rapid7.com/appspider/use-case-scenario-c This C# example automates a scan using the AppSpider Enterprise REST API. It logs in, checks for an existing config named 'WebscantestSqlInj', creates it if missing, runs a scan, polls until finished, and downloads the report as 'ReportAllFiles.zip'. Replace 'Resources.WebscantestSqlInj' with your config file. The code uses Newtonsoft.Json.Linq and includes helper methods for HTTP requests and file saving. ```csharp using System; using System.Diagnostics; using System.IO; using System.Net; using System.Text; using System.Threading; using Newtonsoft.Json.Linq; namespace NTOERestApiTest.Console { public class Program { private const string RootPath = "http://localhost:54073/AppSpiderEnterpriserest/v1/"; private const string Username = "wstclient"; private const string Password = "wstclient"; public static void Main(string[] args) { // login var authResult = PostRequest("authentication/login", string.Format("{{ name: \"{0}\", password: \"{1}\" }}", Username, Password)); if (authResult.IsSuccess == null || !authResult.IsSuccess.Value) { System.Console.WriteLine(authResult.ErrorMessage.Value); return; } string token = authResult.Token.Value; System.Console.WriteLine("Logged in successfully."); // get configs and check config with name WebscantestSqlInj doesn't exist(unable to create 2 configs with the same name) var configsResult = GetRequest("config/getconfigs", token); if (configsResult.IsSuccess == null || !configsResult.IsSuccess.Value) { System.Console.WriteLine(configsResult.ErrorMessage.Value); return; } Guid? configId = null; bool isConfigWithWebscantestSqlInjNameExist = false; foreach (dynamic config in configsResult.Configs) { if (config.Name == "WebscantestSqlInj") { isConfigWithWebscantestSqlInjNameExist = true; configId = config.Id; System.Console.WriteLine("Config with name WebscantestSqlInj found."); break; } } // Create new config if config with the same name doesn't exist if (!isConfigWithWebscantestSqlInjNameExist) { string errorMessage; configId = CreateConfig(token, out errorMessage); if (configId == null) { System.Console.WriteLine(errorMessage); return; } System.Console.WriteLine("Config with name WebscantestSqlInj created."); } // run scan var runScanResult = PostRequest("scan/runscan", string.Format("{{\"configId\":\n\"{0}\" }}", configId), token); if (runScanResult.IsSuccess == null || !runScanResult.IsSuccess.Value) { System.Console.WriteLine(runScanResult.ErrorMessage.Value); return; } var scanId = runScanResult.Scan.Id; System.Console.WriteLine("Scan started. Scan ID: {0}", scanId); // waiting for scan finished var stopwatch = new Stopwatch(); stopwatch.Start(); bool isScanFinished = false; while (!isScanFinished) { System.Console.Write("{0} minutes and {1} seconds elapsed.", stopwatch.Elapsed.Minutes, stopwatch.Elapsed.Seconds); System.Console.Write("\r"); Thread.Sleep(5000); var isScanFinishedResult = GetRequest(string.Format("scan/isscanfinished?scanId={0}", scanId), token); if (isScanFinishedResult.IsSuccess == null || !isScanFinishedResult.IsSuccess.Value) { System.Console.WriteLine(isScanFinishedResult.ErrorMessage.Value); return; } isScanFinished = isScanFinishedResult.Result; } stopwatch.Stop(); System.Console.WriteLine(); System.Console.WriteLine("Scan finished."); // check finished scan has report var scanHasReportResult = GetRequest(string.Format("scan/hasreport?scanId={0}", scanId), token); if (scanHasReportResult.IsSuccess == null || !scanHasReportResult.IsSuccess.Value) { System.Console.WriteLine(scanHasReportResult.ErrorMessage.Value); return; } if (!scanHasReportResult.Result.Value) { System.Console.WriteLine("Scan finished and has no report."); return; } SaveFile(string.Format("report/getreportzip?scanid={0}", scanId), "ReportAllFiles.zip", token); System.Console.WriteLine("Report saved with name ReportAllFiles.zip."); } private static Guid? CreateConfig(string token, out string errorMessage) { token); errorMessage = null; var engineGroupsResult = GetRequest("enginegroup/getenginegroupsforclient", if (engineGroupsResult.EngineGroups == null) { errorMessage = engineGroupsResult.ErrorMessage.Value; return null; } if (engineGroupsResult.EngineGroups.Count == 0) { errorMessage = "Where are no engine groups assigned to current client."; return null; } ``` -------------------------------- ### Ubuntu setup commands for Hackazon Source: https://docs.rapid7.com/appspider/hackazon-installation-guide Commands for installing and configuring Hackazon on Ubuntu: install Apache, MySQL, PHP, enable the site and rewrite module, set permissions, create the database, and restart services. Run them in order from a terminal. ```bash sudo apt-get install apache2 ``` ```bash sudo apt-get install mysql-server ``` ```bash sudo apt-get install php5 libapache2-mod-php5 ``` ```bash sudo /etc/init.d/apache2 restart ``` ```bash sudo chmod -R a+rwX /var/www/hackazon/assets/config/ ``` ```bash sudo chmod -R a+rwX /var/www/hackazon/web/upload/ ``` ```bash sudo a2ensite hackazon.lc.conf ``` ```bash sudo a2enmod rewrite ``` ```bash sudo apt-get install php5-gd php5-mysql ``` ```bash sudo service apache2 restart ``` ```bash mysql -u root -p ``` ```sql create database hackazon; ``` ```sql GRANT ALL ON hackazon.* TO hackazon@'localhost' IDENTIFIED BY 'password'; ``` ```bash sudo /etc/init.d/mysql restart ``` -------------------------------- ### ClientApproval Response Example Source: https://docs.rapid7.com/appspider/target-operations Response schema for the ClientApproval endpoint. Contains IsSuccess, ErrorMessage, Reason, and Links fields. ```json { "type": "object", "properties": { "IsSuccess": { "type": "boolean"", "required": true }, "ErrorMessage": { "type":["string", "null"]", "required": true }, "Reason": { "type": ["string", "null"]", "required": true }, "Links": { "type": "array", "items": { "Rel": { "type":["string", "null"], "required": true }, "Href": { "type":["string", "null"], "required": true } } } }, }, "additionalProperties": false } ``` -------------------------------- ### GET GetClientAccounts Endpoint Source: https://docs.rapid7.com/appspider/client-operations HTTP GET request to retrieve a client by its ID. The clientId parameter is a required GUID that must be inserted into the URL. A successful response returns HTTP 200 with the client data; a 400 status with InvalidJsonSchema indicates missing required parameters, and a 400 with NotFound indicates no client was found. ```http get **/AppSpiderEnterprise/rest/v1/Client/GetClientAccounts?clientId=``** ``` -------------------------------- ### XML File Example: AttackModulePolicy Configuration Source: https://docs.rapid7.com/appspider/advanced-options This XML snippet shows a partial configuration for attack module policies. It includes settings for enabled status, module ID, priority, severity, vulnerability limits, attack points, parameter locations, request originations, and display name. The example is truncated at the beginning and end, showing only a portion of the full XML file. ```XML 0 Parameter Path|Query|Post|Cookie HTML|Form|AJAX|Flash|Silverlight|WSDL|REST Server Side Template Injection 1 DB1340E1857540219DF84A6A9DC0494C High Medium 25 500 2 0 0 0 Web Resource HTML|Form|AJAX|Flash|Silverlight|WSDL|REST Session Fixation 1 6B1B2812012D41249BDEE83FFAEB523D Low Low 10 500 1 0 0 0 Parameter Directory|Path|Query HTML|Form|AJAX|Flash|Silverlight|WSDL HTTP Query Session Check 1 F729A70998064A1F99A8BEA8512D31AE Medium Low 10 500 4 0 0 0 Web Resource HTML|Form|AJAX|Flash|Silverlight|WSDL|REST Session Strength 1 0DB2C1311DA74B80A153A8733C74D6CC Medium Low 50 500 4 0 0 0 Response Analysis HTML|Form|AJAX|Flash|Silverlight|WSDL|REST Session Upgrade 1 7A7B3239AB8146839A8AEF170807ED1E Low Medium 100 500 4 0 0 0 File HTML|Form|AJAX|Flash|Silverlight|WSDL|REST Source Code Disclosure 1 59646365E0E44520BE4297C3ABAA7E75 High Low 250 500 4 1 0 0 Response Analysis ``` -------------------------------- ### GetGroup HTTP Request Source: https://docs.rapid7.com/appspider/group-operations HTTP GET request to retrieve a group by its GroupId. The GroupId parameter is required and should be inserted in the URL. The response contains group information as shown in the response example. ```http get **/AppSpiderEnterprise/rest/v1/Group/GetGroup?GroupId=``** ``` -------------------------------- ### GetClientAccounts Response Example Source: https://docs.rapid7.com/appspider/client-operations Response example for the GetClientAccounts endpoint. Shows the JSON schema of a successful client retrieval, including the Client object with fields like ClientId, ClientName, and DefendEnabled, plus IsSuccess, Reason, ErrorMessage, and Links. The required fields are listed for both the Client object and the top-level response. ```json { "type": "object", "properties": { "Client": { "type": "object", "properties": { "ClientId": { "type": "string" }, "ClientName": { "type": "string" }, "LoweredClientName": { "type": "string" }, "Address": { "type": "null" }, "Email": { "type": "string" }, "Phone": { "type": "null" }, "Mobile": { "type": "null" }, "Notes": { "type": "null" }, "DefaultTimeZone": { "type": "string" }, "DefendEnabled": { "type": "boolean" }, "EnhancedServices": { "type": "boolean" } }, "required": [ "ClientId", "ClientName", "LoweredClientName", "Address", "Email", "Phone", "Mobile", "Notes", "DefaultTimeZone", "DefendEnabled", "EnhancedServices" ] }, "IsSuccess": { "type": "boolean" }, "Reason": { "type": "null" }, "ErrorMessage": { "type": "null" }, "Links": { "type": "array", "items": [ { "type": "object", "properties": { "Rel": { "type": "string" }, "Href": { "type": "string" } }, "required": [ "Rel", "Href" ] } ] } }, "required": [ "Client", "IsSuccess", "Reason", "ErrorMessage", "Links" ] } ``` -------------------------------- ### Silent Install AppSpider with AppSpiderSetup.exe Source: https://docs.rapid7.com/appspider/install-appspider-pro Use the first command to display the available silent installation options. The second command installs the AppSpider engine, GUI, and command line tools. Additional components can be installed with the /REST, /WEB, and /NGINX flags. ```text AppSpiderSetup.exe /HELP ``` ```text AppSpiderSetup.exe /S install /UI /CMD ``` -------------------------------- ### FrameworkConfig XML example Source: https://docs.rapid7.com/appspider/advanced-options Defines multiple FrameworkConfig entries for frameworks such as React, AngularJS, KnockoutJS, AngularLib, ReactLib, ReactTwo, OpenUI5, Vue, and Ember. Each entry specifies detection regex, healthcheck string, hook file name, and event/version scripts. The example shows the XML structure used to configure framework detection in AppSpider. ```xml fm.react.min.js window.fm.react.getAllEvents('appspider'); 0 AngularJS 1 1 0 ng-controller angular angular-hook-bundle.min.js NG_HOOK.getAllNgEvents(); 0 NG_HOOK.getVersionString(); KnockoutJS 1 1 0 data-bind ko ko-hook-bundle.min.js KO_HOOK.getAllKOEvents(); 5000 KO_HOOK.getVersionString(); AngularLib 2 1 0 (?-i)angular(?!\w) angular-4-hook-bundle.min.js NG_HOOK_LIB.getAllAngularFmEvents(); 5000 NG_HOOK_LIB.getVersionString(); ReactLib 1 1 0 react-text react-hook-bundle.min.js REACT_HOOK_LIB.getAllReactEvents(); 5000 REACT_HOOK_LIB.getVersionString(); ReactTwo 1 1 0 (?-i)"react[.][a-df-z][a-z]+" react-hook-bundle.min.js REACT_HOOK_LIB.getAllReactTwoEvents(); 5000 REACT_HOOK_LIB.getVersionString(); OpenUI5 1 1 0 (?-i)sap-ui-debug sap-open-ui5-hook-bundle.min.js OPEN_UI5_HOOK_LIB.getAllOPENUI5Events(); 5000 OPEN_UI5_HOOK_LIB.getVersionString(); Vue 1 1 0 (?-i)__VUE_SSR_CONTEXT__ vue-hook-bundle.min.js VUE_HOOK_LIB.getAllVueEvents(); 5000 VUE_HOOK_LIB.getVersionString(); Ember 1 1 0 (?-i)EMBER_LOAD_HOOKS ember-hook-bundle.min.js EMBER_HOOK_LIB.getAllEmberEvents(); 5000 EMBER_HOOK_LIB.getVersionString(); ``` -------------------------------- ### GET /AppSpiderEnterprise/rest/v1/Scan/GetScanErrors Source: https://docs.rapid7.com/appspider/scan-management Gets scan error log. Requires a scanId parameter and returns a list of error events. ```APIDOC ## GET /AppSpiderEnterprise/rest/v1/Scan/GetScanErrors ### Description Gets scan error log. ### Method GET ### Endpoint /AppSpiderEnterprise/rest/v1/Scan/GetScanErrors ### Parameters #### Query Parameters - **scanId** (guid[]) - Required - Scan ID ### Response Messages | Response status code | Reason | Description | |----------------------|--------|-------------| | 200 | | Response contains boolean value | | 400 | InvalidJsonSchema | Required parameters not provided | | 400 | ScanNotFound | Scan not found | | 400 | ScanIsNotAllowed | User does not have permissions to access scan | ### Response Example ```json { "type": "object", "properties": { "Events": { "type": "array", "properties": { "Type": { "type": "string", "required": true }, "Date": { "type": ["string", "null"], "required": true }, "Message": { "type": ["string", "null"], "required": true } }, "additionalProperties": false } }, "IsSuccess": { "type": "boolean", "required": true }, "ErrorMessage": { "type": ["string", "null"], "required": true }, "Reason": { "type": ["string", "null"], "required": true } }, "additionalProperties": false } ``` ``` -------------------------------- ### Client Accounts List Response Example Source: https://docs.rapid7.com/appspider/client-operations Response example for the client accounts list endpoint. Shows the JSON schema returned when a client is found (HTTP 200). The response includes an Accounts array with UserId, UserName, and ClientId fields, plus IsSuccess, Reason, ErrorMessage, and Links properties. ```json { "type": "object", "properties": { "Accounts": { "type": "array", "items": [ { "type": "object", "properties": { "UserId": { "type": "string" }, "UserName": { "type": "string" }, "ClientId": { "type": "string" } }, "required": [ "UserId", "UserName", "ClientId" ] } ] }, "IsSuccess": { "type": "boolean" }, "Reason": { "type": "null" }, "ErrorMessage": { "type": "null" }, "Links": { "type": "array", "items": [ { "type": "object", "properties": { "Rel": { "type": "string" }, "Href": { "type": "string" } }, "required": [ "Rel", "Href" ] } ] } }, "required": [ "Accounts", "IsSuccess", "Reason", "ErrorMessage", "Links" ] } ``` -------------------------------- ### GET request with HttpWebRequest Source: https://docs.rapid7.com/appspider/use-case-scenario-c Performs a GET request to the AppSpider API with basic authentication and parses the JSON response. Handles WebException by reading the error response. ```csharp private static dynamic GetRequest(string url, string token) { string responseStr; try { var httpRequest = (HttpWebRequest)WebRequest.Create(RootPath + url); httpRequest.Accept = "application/json"; httpRequest.ContentType = "application/json"; httpRequest.Method = "GET"; httpRequest.Headers["Authorization"] = "Basic " + token; var response = httpRequest.GetResponse(); using (Stream data = response.GetResponseStream()) using (var reader = new StreamReader(data)) { responseStr = reader.ReadToEnd(); } } catch (WebException e) { using (WebResponse response = e.Response) { } } using (Stream data = response.GetResponseStream()) using (var reader = new StreamReader(data)) { responseStr = reader.ReadToEnd(); } return JObject.Parse(responseStr); } ```