Documentation

Table Of Contents

1 API Credentials

Access to API is provided using

  • API Key – Generated key provided to partner (e.g. BWA-XXX), and
  • API Secret – Generated password used for authentication.

Before API can be used, the user has to register an Bulk Whois API account and obtain API credentials. When you are logged in Bulk Whois API , under My Account section, click API Credentials link in the left side menu.


2 API General Mechanisms

2.1 Calls and Authentication

All calls to Bulk Whois API are authenticated HTTPS POST requests, using application/x-www-form-urlencoded content type. All calls are made to URL in a generic format:

http://api.bulk-whois-api.com/api/$ACTION

Where $ACTION is specific to the request operation. Note that although our primary API endpoint does not run on HTTPS, it is still perfectly secure even if a third party can capture the network traffic between this endpoint and your application. The authentication protocol is designed not to send the API secret at any given time over the network, and it also mitigates other possible attacks. If you still need access via HTTPS, please contact support for an alternative API endpoint.

The authentication is implemented using three HTTP headers:

  • Key - API Key.
  • Sign - Lower case hex representation of HMAC SHA512 of the request.
  • Time - Source server UTC date time specification in format YYYY-MM-DD hh:mm:ss. 24-hour format with leading zeros is required.

Time is considered valid, if it is not off by more than 15 minutes compared to our server time. Sign is a hash calculated from a concatenation of Key, Time and request Body using partner's API secret:

$SIGN := hmac_sha512($SECRET, $KEY + $TIME + $BODY).tolower()

See below section 5 for a working sample code.

2.2 Calling Modes

Each API call can operate either in synchronous mode, asynchronous mode, polling mode, or any combination of these modes.

2.2.1 Synchronous Mode

The default mode is synchronous mode and API calls that support synchronous mode do not need to specify extra parameters to use synchronous mode. In synchronous mode, the API server replies just after it obtains the requested data. This is usually fast and only take a couple of seconds, but in extreme cases, this can take up to 140 seconds, so setting a timeout to up to 150 seconds is a good idea. When you are using the synchronous mode, you can only do one query at the time. This is why we do not recommend using synchronous mode for large volume querying – use Asynchronous Mode instead, which supports performing multiple queries in parallel.

2.2.2 Asynchronous Mode

API calls that support asynchronous mode can be called with extra POST parameter asyncCallback that forces the call to run in asynchronous mode.

  • POST body arguments:
    • asyncCallback (optional) - If present, it forces the API call to be processed in asynchronous mode and it must be set to HTTP or HTTPS URL that is called by Bulk Whois API server once the operation completes. The callback URL is called only in case the return value success is 1 (see section 2.3) in the call API response.

The target script specified in the asyncCallback argument receives a POST request with Content-Type header set to application/json.

The callback processing code must finish as soon as possible and send the response to the server callback request that contains a single text line:

BWA: OK

Callback targets that repeatedly do not respond with this line within 5 seconds can be blacklisted.

Many API calls support testMode parameter for development purposes. If both testMode and asyncCallback arguments are set, validation is performed as in synchronous mode, but callback URL is not called.

2.2.3 Polling Mode

API calls that support polling mode can be called with extra POST parameter polling that forces the call to run in polling mode.

  • POST body arguments:
    • polling (optional) - If set to 1, it forces the API call to be processed in polling mode. In this mode, the API server replies immediately with a response, which informs the caller that the operation is in progress and provides a URL that the caller is expected to check periodically to obtain the operation result. The result URL is provided in return value resultUrl, and it is provided only if the value success is 1 (see section 2.3) in the call API response. See section 2.3.1 below for more information.

Many API calls support testMode parameter for development purposes. If both testMode and polling arguments are set, validation is performed as in synchronous mode, but no target result URL is provided and the operation is not executed.

The documentation of each API that supports polling mode contains information about the reasonable frequency of polling. Callers polling with much higher frequencies can be temporarily banned.

The caller can access the resultUrl using both GET and POST methods, the authentication is not required in this case.

2.3 Response

A generic response to a Partner API request is in JSON format. In case of a successful call, the format is

{
"success":1, ...
}

In case of an error, the format is

{
"success":0,
"message":$MESSAGE
}

where $MESSAGE is a string.

2.3.1 Polling Response

In polling mode, the response format of a successful call is

{
"success":1,
"resultUrl":"https://...", ...
}

The resultUrl is the URL that the caller is expected to poll to obtain the operation result. If the result is not available when the caller checks, the value success is 0 and the error message is set to Pending. All other values means that the task is completed (successfully or not) and the caller should not check the resultUrl anymore. Callers that keep polling after the task is complete can be blacklisted.

2.3.2 General Errors

API calls can generate following general errors:

Value of returned $MESSAGE Description
POST method is required. The request used other than HTTP POST method.
Authentication failed. Key header is missing. The request did not contain HTTP header Key. Authentication failed.
Authentication failed. Sign header is missing. The request did not contain HTTP header Sign.
Authentication failed. Time header is missing. The request did not contain HTTP header Time.
Authentication failed. Invalid signature. The signature was invalid.
Authentication failed. Invalid time. Server time is YYYY-MM-DD hh:mm:ss. The value of Time header was invalid. Either the time was sent in an invalid format, or the provided time differed for more than 15 minutes compared to our server's UTC time. Our server's time in UTC is returned in the response.
No requests left. API call was not executed because the calling account does not have any requests left.
Blacklisted. The caller does not conform to the API specification and used IP or URL was blacklisted.
Slow down. The caller executes too many requests too quickly.
Pending. The polling mode result URL informs the caller that the results are not available yet and it should keep polling.
Invalid argument. ARG is invalid. The value of ARG argument is invalid. The argument's value was either in invalid format, too long, or not unique as required.
Invalid argument. ARG is missing. The request did not contain ARG argument, which was required.
MODE mode is not supported. Unsupported MODE mode was used with action that does not supported.
Invalid request. An error occurred during parsing the request.
Unsupported. Unsupported domain was queried. Currently, this is returned for .es and .my domains.
Service error. The request could not be completed due to error in the service. This could happen when a service is in a maintenance mode or due to an unexpected error. Please try again in a few minutes. If the problem persists for more than 30 minutes, please contact support.

3 Account API Calls

3.1 Authentication Test

Supported modes: synchronous only

This action is implemented for the purpose of testing the authentication mechanism.

  • $ACTION - authTest
  • POST body arguments are optional and can be arbitrary. A developer can send any POST arguments in order to tests an implementation of the authentication mechanism.

3.1.1 Success

No additional values are returned.

3.1.2 Failure

Only general errors are returned.

3.2 Account Information

Supported modes: synchronous only

Returns basic information about the account.

  • $ACTION - info
  • No POST body arguments.

3.2.1 Success

Following values are returned:

Name Type Description
email string Email address of the account owner.
requestsLeft int Number of allowed whois requests until next billing date or end of trial.

3.2.2 Failure

Only general errors are returned.

4 Whois API Calls

4.1 Query

Supported modes: synchronous + asynchronous + polling (5 seconds)

Gets WHOIS information for a domain or an IP address.

  • $ACTION – query
  • POST body arguments:
    • testMode (optional) – If set to 1, a successful API call does not perform a WHOIS query. It just validates the input and checks that the caller is able to perform the call (e.g. has enough requests). Returned response may be incomplete.
    • query – A second-level domain name for all top-level domains that allow Second-level domain registration such as .com, .net, .info, .org, .eu, .de, etc. Or, a third-level domain for all other top-level domains such as .uk, .br, .au, etc. Or, an IPv4 address. No subdomains, such as www, are allowed.

4.1.1 Success

Following values are returned:

Name Type Description
output struct WHOIS_OUTPUT structure, see below.
rawOutput array Array of strings. Each string represents a complete response from WHOIS server. To get a final WHOIS record, 1-3 WHOIS requests are usually needed. This array contains responses to all requests

WHOIS_OUTPUT structure is defined as follows. However, there is no single standard for WHOIS records format. Each WHOIS response is parsed and as many values as possible are extracted and provided in this structure, but structures for different domains can contain and miss different values.

Name Type Description
domain string Name of the domain described by the record.
domain_id string Unique domain ID used by some registrars.
status array Array of strings that describe the status of the domain. Values can vary for different registrars. Common values are registered, available, CLIENT TRANSFER PROHIBITED, CLIENT DELETE PROHIBITED, CLIENT UPDATE PROHIBITED, clientDeleteProhibited, clientTransferProhibited, clientUpdateProhibited, redemptionPeriod.
registered bool Set if the domain is registered.
available bool Set if the domain is available.
created_on string Date and time when the domain record was created. Typically in format YYYY-MM-DD hh:mm:ss TZ, where TZ is timezone information in format +XXXX or timezone abbreviation.
updated_on string Date and time of the last update of the domain record. Typically in format YYYY-MM-DD hh:mm:ss TZ, where TZ is timezone information in format +XXXX or timezone abbreviation.
expires_on string Date and time of the domain expiration. Typically in format YYYY-MM-DD hh:mm:ss TZ, where TZ is timezone information in format +XXXX or timezone abbreviation. registrar struct Information about domain registrar. Format is WHOIS_REGISTRAR structure, see below.
registrar struct Information about domain registrar. Format is WHOIS_REGISTRAR structure, see below.
registrant_contact struct Information about entity that registered the domain. Format is WHOIS_CONTACT structure, see below.
admin_contact struct Information about domain administrator. Format is WHOIS_CONTACT structure, see below.
technical_contact struct Information about domain technical contact. Format is WHOIS_CONTACT structure, see below.
nameservers array Array of WHOIS_NAMESERVER structures, see below

WHOIS_REGISTRAR structure is defined as follows (again, not all values are always available):

Name Type Description
id string Identifier of the registrar.
name string Name of the registrar.
organization string Name of the registrar's organization.
url string Web address of the registrar.

WHOIS_CONTACT structure is defined as follows (again, not all values are always available):

Name Type Description
id string Name of the domain described by the record.
name string Unique domain ID used by some registrars.
organization string Contact's organization.
address string Contact's address. Usually a street or a full address including a city and a state.
city string Contact's city.
zip string Contact's ZIP code.
state string Contact's state.
country string Contact's country.
country_code string Contact's country code.
phone string Contact's phone number.
fax string Contact's FAX number.
email string Contact's email.
created_on string Date and time when the contact information was created. Typically in format YYYY-MM-DD hh:mm:ss TZ, where TZ is timezone information in format +XXXX or timezone abbreviation.
updated_on string Date and time when the contact information was updated. Typically in format YYYY-MM-DD hh:mm:ss TZ, where TZ is timezone information in format +XXXX or timezone abbreviation.
url string Web address of the contact.
ref_url string Reference URL where it is possible to get additional information about the entity.
handle string Contact's NIC handle.

WHOIS_NAMESERVER structure is defined as follows (again, not all values are always available):

Name Type Description
name string Host name of the name server.
ipv4 string IPv4 address of the name server.
ipv6 string IPv6 address of the name server.

4.1.2 Failure

Only general errors are returned.

4.1.3 Rate Limits

Bulk Whois API does have rate limits, which are currently set to 15 requests per 15 seconds. If a user attempts to perform more requests, "Slow down." error message will be returned (see section 2.3.2 above). If you need higher limits for your application, feel free to contact support. Most of the time we increase the limits free of charge per customer's needs.

5 Sample Implementations

5.1 C# Implementation – Basic API Calls

A basic C# implementation that demonstrates how to call API in different modes. Please see section 2.2 for more information about the calling modes. Note that the synchronous mode should not be used for large volume querying.

Download sample

Program.cs
using System;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;

namespace BWA_API
{
  class Program
  {
    static void Main(string[] args)
    {
      if (args.Length != 2)
      {
        Console.WriteLine("Usage: bwa-api <API Key> <API Secret>");
        return;
      }

      // API initialization
      BwaApi api = new BwaApi(args[0], args[1]);

      // Test authentication
      BwaApiAuthTest authTest;
      if (api.AuthTest(out authTest))
      {
        Console.WriteLine("Authentication test succeeded.\n");
      }
      else Console.WriteLine("Authentication test failed: {0}", authTest.message);

      // Account information
      BwaApiInfo accountInfo;
      if (api.AccountInfo(out accountInfo))
      {
        Console.WriteLine("Account info:");
        Console.WriteLine("  Email: {0}", accountInfo.email);
        Console.WriteLine("  Requests left: {0}\n", accountInfo.requestsLeft);
      }
      else Console.WriteLine("Getting account information failed: {0}", accountInfo.message);


      // Synchronous Whois Query on domain
      string domain = "woot.com";
      BwaApiWhoisQuery whoisQuerySync;
      if (api.WhoisQuerySynchronous(domain, out whoisQuerySync))
      {
        Console.WriteLine("\nResult of synchronous whois query:");
        PrintDomainResults(domain, whoisQuerySync);
      }
      else Console.WriteLine("Synchronous whois query failed: {0}", whoisQuerySync.message);
      
      BwaApiWhoisQuery whoisQueryPoll;
      if (api.WhoisQueryPolling(domain, out whoisQueryPoll))
      {
        Console.WriteLine("\n\n\nResult of polling whois query:");
        PrintDomainResults(domain, whoisQueryPoll);
      }
      else Console.WriteLine("Polling whois query failed: {0}", whoisQueryPoll.message);
    }

    static void PrintDomainResults(string Domain, BwaApiWhoisQuery WhoisQuery)
    {
      Console.WriteLine("Whois query ({0}):", Domain);

      Console.WriteLine("  Output:");
      Console.WriteLine("    Domain: {0}", WhoisQuery.output.domain);
      Console.WriteLine("    Domain ID: {0}", WhoisQuery.output.domain_id);
      Console.WriteLine("    Status: {0}", WhoisQuery.output.status);
      Console.WriteLine("    Registered: {0}", WhoisQuery.output.registered);
      Console.WriteLine("    Available: {0}", WhoisQuery.output.available);
      Console.WriteLine("    Created on: {0}", WhoisQuery.output.created_on);
      Console.WriteLine("    Updated on: {0}", WhoisQuery.output.updated_on);
      Console.WriteLine("    Expires on: {0}", WhoisQuery.output.expires_on);

      if (WhoisQuery.output.registrar != null)
      {
        Console.WriteLine("\n    Registrar:");
        Console.WriteLine("      ID: {0}", WhoisQuery.output.registrar.id);
        Console.WriteLine("      Name: {0}", WhoisQuery.output.registrar.name);
        Console.WriteLine("      Organization: {0}", WhoisQuery.output.registrar.organization);
        Console.WriteLine("      URL: {0}", WhoisQuery.output.registrar.url);
      }

      if (WhoisQuery.output.registrant_contact != null)
      {
        Console.WriteLine("\n    Registrant contact:");
        Console.WriteLine("      ID: {0}", WhoisQuery.output.registrant_contact.id);
        Console.WriteLine("      Name: {0}", WhoisQuery.output.registrant_contact.name);
        Console.WriteLine("      Organization: {0}", WhoisQuery.output.registrant_contact.organization);
        Console.WriteLine("      Address: {0}", WhoisQuery.output.registrant_contact.address);
        Console.WriteLine("      City: {0}", WhoisQuery.output.registrant_contact.city);
        Console.WriteLine("      ZIP code: {0}", WhoisQuery.output.registrant_contact.zip);
        Console.WriteLine("      State: {0}", WhoisQuery.output.registrant_contact.state);
        Console.WriteLine("      Country: {0}", WhoisQuery.output.registrant_contact.country);
        Console.WriteLine("      Country code: {0}", WhoisQuery.output.registrant_contact.country_code);
        Console.WriteLine("      Phone: {0}", WhoisQuery.output.registrant_contact.phone);
        Console.WriteLine("      Fax: {0}", WhoisQuery.output.registrant_contact.fax);
        Console.WriteLine("      Email: {0}", WhoisQuery.output.registrant_contact.email);
        Console.WriteLine("      Url: {0}", WhoisQuery.output.registrant_contact.url);
        Console.WriteLine("      Created on: {0}", WhoisQuery.output.registrant_contact.created_on);
        Console.WriteLine("      Updated on: {0}", WhoisQuery.output.registrant_contact.updated_on);
        Console.WriteLine("      URL: {0}", WhoisQuery.output.registrant_contact.url);
        Console.WriteLine("      Ref URL: {0}", WhoisQuery.output.registrant_contact.ref_url);
        Console.WriteLine("      Handle: {0}", WhoisQuery.output.registrant_contact.handle);
      }

      if (WhoisQuery.output.admin_contact != null)
      {
        Console.WriteLine("\n    Administrative contact:");
        Console.WriteLine("      ID: {0}", WhoisQuery.output.admin_contact.id);
        Console.WriteLine("      Name: {0}", WhoisQuery.output.admin_contact.name);
        Console.WriteLine("      Organization: {0}", WhoisQuery.output.admin_contact.organization);
        Console.WriteLine("      Address: {0}", WhoisQuery.output.admin_contact.address);
        Console.WriteLine("      City: {0}", WhoisQuery.output.admin_contact.city);
        Console.WriteLine("      ZIP code: {0}", WhoisQuery.output.admin_contact.zip);
        Console.WriteLine("      State: {0}", WhoisQuery.output.admin_contact.state);
        Console.WriteLine("      Country code: {0}", WhoisQuery.output.admin_contact.country_code);
        Console.WriteLine("      Phone: {0}", WhoisQuery.output.admin_contact.phone);
        Console.WriteLine("      Fax: {0}", WhoisQuery.output.admin_contact.fax);
        Console.WriteLine("      Email: {0}", WhoisQuery.output.admin_contact.email);
        Console.WriteLine("      Url: {0}", WhoisQuery.output.admin_contact.url);
        Console.WriteLine("      Created on: {0}", WhoisQuery.output.admin_contact.created_on);
        Console.WriteLine("      Updated on: {0}", WhoisQuery.output.admin_contact.updated_on);
        Console.WriteLine("      URL: {0}", WhoisQuery.output.admin_contact.url);
        Console.WriteLine("      Ref URL: {0}", WhoisQuery.output.admin_contact.ref_url);
        Console.WriteLine("      Handle: {0}", WhoisQuery.output.admin_contact.handle);
      }

      if (WhoisQuery.output.technical_contact != null)
      {
        Console.WriteLine("\n    Technical contact:");
        Console.WriteLine("      ID: {0}", WhoisQuery.output.technical_contact.id);
        Console.WriteLine("      Name: {0}", WhoisQuery.output.technical_contact.name);
        Console.WriteLine("      Organization: {0}", WhoisQuery.output.technical_contact.organization);
        Console.WriteLine("      Address: {0}", WhoisQuery.output.technical_contact.address);
        Console.WriteLine("      City: {0}", WhoisQuery.output.technical_contact.city);
        Console.WriteLine("      ZIP code: {0}", WhoisQuery.output.technical_contact.zip);
        Console.WriteLine("      State: {0}", WhoisQuery.output.technical_contact.state);
        Console.WriteLine("      Country code: {0}", WhoisQuery.output.technical_contact.country_code);
        Console.WriteLine("      Phone: {0}", WhoisQuery.output.technical_contact.phone);
        Console.WriteLine("      Fax: {0}", WhoisQuery.output.technical_contact.fax);
        Console.WriteLine("      Email: {0}", WhoisQuery.output.technical_contact.email);
        Console.WriteLine("      Url: {0}", WhoisQuery.output.technical_contact.url);
        Console.WriteLine("      Created on: {0}", WhoisQuery.output.technical_contact.created_on);
        Console.WriteLine("      Updated on: {0}", WhoisQuery.output.technical_contact.updated_on);
        Console.WriteLine("      URL: {0}", WhoisQuery.output.technical_contact.url);
        Console.WriteLine("      Ref URL: {0}", WhoisQuery.output.technical_contact.ref_url);
        Console.WriteLine("      Handle: {0}", WhoisQuery.output.technical_contact.handle);
      }

      if (WhoisQuery.output.nameservers != null)
      {
        Console.WriteLine("\n    Name servers:");
        foreach (BwaApiWhoisNameServer ns in WhoisQuery.output.nameservers)
        {
          Console.WriteLine("      Server: {0}", ns.name);
          Console.WriteLine("        IPv4: {0}", ns.ipv4);
          Console.WriteLine("        IPv6: {0}", ns.ipv6);
        }
      }

      Console.WriteLine("\n\n  Raw outputs:");
      foreach (string ra in WhoisQuery.rawOutput)
        Console.WriteLine("-------------------------------------------------------------\n{0}\n", ra);
    }
  }
}

BwaApi.cs
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Web;
using Newtonsoft.Json;

namespace BWA_API
{
  public class BwaApi
  {
    private string key;
    private string secret;
    private HMACSHA512 hashMaker;

    public const string ApiUrl = "http://api.bulk-whois-api.com/api/{0}";

    public BwaApi(string Key, string Secret)
    {
      key = Key;
      secret = Secret;
      hashMaker = new HMACSHA512(Encoding.ASCII.GetBytes(secret));
    }

    private static string ByteArrayToString(byte[] Bytes)
    {
      string hex = BitConverter.ToString(Bytes);
      return hex.Replace("-", "");
    }

    private string Sign(string Message)
    {
      byte[] bytes = Encoding.UTF8.GetBytes(Message);
      byte[] hash = hashMaker.ComputeHash(bytes);
      return ByteArrayToString(hash).ToLower();
    }

    private string SendRequest(string Action, NameValueCollection Params)
    {
      string responseData = null;
      try
      {
        string requestStr = string.Format(ApiUrl, Action);
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestStr);
        request.Method = "POST";

        string timeStr = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
        request.Headers["Key"] = key;
        request.Headers["Time"] = timeStr;

        string postData = "";
        foreach (string pKey in Params)
          postData += string.Format("{0}{1}={2}", postData.Length > 0 ? "&" : "",
            HttpUtility.UrlEncode(pKey), HttpUtility.UrlEncode(Params[pKey]));

        string payloadToSign = string.Format(key + timeStr + postData);
        request.Headers["Sign"] = Sign(payloadToSign);

        byte[] postDataBytes = Encoding.UTF8.GetBytes(postData);
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = postDataBytes.Length;
        Stream dataStream = request.GetRequestStream();
        dataStream.Write(postDataBytes, 0, postDataBytes.Length);

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();

        StreamReader respStream = new StreamReader(response.GetResponseStream());
        responseData = respStream.ReadToEnd();
        respStream.Close();
      }
      catch
      {
      }

      return responseData;
    }

    private bool ProcessResponse<T>(string Json, ref T Response) where T : BwaApiResponse
    {
      bool result = false;

      if (!string.IsNullOrEmpty(Json))
      {
        try
        {
          Response = JsonConvert.DeserializeObject<T>(Json);
          result = Response.success == 1;
        }
        catch
        {
          Response.message = "Invalid response received from API server.";
        }
      }
      else Response.message = "Unable to get response from API server.";

      return result;
    }

    public bool AuthTest(out BwaApiAuthTest Response)
    {
      Response = new BwaApiAuthTest();
      string json = SendRequest("authTest", new NameValueCollection());
      return ProcessResponse(json, ref Response);
    }

    public bool AccountInfo(out BwaApiInfo Response)
    {
      Response = new BwaApiInfo();
      string json = SendRequest("info", new NameValueCollection());
      return ProcessResponse(json, ref Response);
    }

    public bool WhoisQuerySynchronous(string Query, out BwaApiWhoisQuery Response)
    {
      Response = new BwaApiWhoisQuery();
      NameValueCollection nvc = new NameValueCollection();
      nvc.Add("query", Query);
      string json = SendRequest("query", nvc);
      return ProcessResponse(json, ref Response);
    }

    public bool WhoisQueryPolling(string Query, out BwaApiWhoisQuery Response)
    {
      bool result = false;
      Response = new BwaApiWhoisQuery();

      BwaApiPollResponse pollingResponse = new BwaApiPollResponse();
      NameValueCollection nvc = new NameValueCollection();
      nvc.Add("query", Query);
      nvc.Add("polling", "1");
      string json = SendRequest("query", nvc);
      if (ProcessResponse(json, ref pollingResponse))
      {
        string url = pollingResponse.resultUrl;

        bool done = false;
        while (!done)
        {
          Thread.Sleep(5000);
          try
          {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            StreamReader respStream = new StreamReader(response.GetResponseStream());
            json = respStream.ReadToEnd();
            respStream.Close();

            result = ProcessResponse(json, ref Response);
            if (!result)
            {
              if (Response.message == "Pending.")
              {
                Response = new BwaApiWhoisQuery();
              }
              else done = true;
            }
            else done = true;
          }
          catch
          {
            done = true;
          }
        }
      }
      else Response.message = pollingResponse.message;

      return result;
    }
  }

  public class BwaApiResponse
  {
    public int success;
    public string message;
  }

  public class BwaApiPollResponse : BwaApiResponse
  {
    public string resultUrl;
  }

  public class BwaApiAuthTest : BwaApiResponse
  {

  }

  public class BwaApiInfo : BwaApiResponse
  {
    public string email;
    public int requestsLeft;
  }

  public class BwaApiWhoisQuery : BwaApiResponse
  {
    public BwaApiWhoisOutput output;
    public string[] rawOutput;
  }

  public class BwaApiWhoisOutput
  {
    public string domain;
    public string domain_id;
    public string[] status;
    public bool registered;
    public bool available;
    public string created_on;
    public string updated_on;
    public string expires_on;
    public BwaApiWhoisRegistrar registrar;
    public BwaApiWhoisContact registrant_contact;
    public BwaApiWhoisContact admin_contact;
    public BwaApiWhoisContact technical_contact;
    public BwaApiWhoisNameServer[] nameservers;
  }

  public class BwaApiWhoisRegistrar
  {
    public string id;
    public string name;
    public string organization;
    public string url;
  }

  public class BwaApiWhoisContact
  {
    public string id;
    public string name;
    public string organization;
    public string address;
    public string city;
    public string zip;
    public string state;
    public string country;
    public string country_code;
    public string phone;
    public string fax;
    public string email;
    public string url;
    public string created_on;
    public string updated_on;
    public string url;
    public string ref_url;
    public string handle;
  }

  public class BwaApiWhoisNameServer
  {
    public string name;
    public string ipv4;
    public string ipv6;
  }
}

5.2 PHP Implementation – Basic API Calls

A basic PHP 5 implementation that demonstrates how to call API in different modes. Please see section 2.2 for more information about the calling modes. Note that the synchronous mode should not be used for large volume querying.

Download sample

sample.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

include __DIR__ . "/BwaApi.php";

// Settings
$apiKey = "";
$apiSecret = "";


// API initialization
$api = new BwaApi($apiKey, $apiSecret);

// Accept asynchronous callback.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $json = file_get_contents('php://input');
        if ($json) {
                echo "ODT: OK";
                $response = json_decode($json, true);

                if ($response === null) { // error
                        file_put_contents(__DIR__ . '/log.txt', var_export(array("error", $json, $response), true));
                } else { // success
                        file_put_contents(__DIR__ . '/log.txt', var_export(array("ok", $json, $response), true));
                        $whoisAsyncResult = new BwaApiWhoisQuery($response);
                        $output = getDomainResults($whoisAsyncResult->output->domain, $whoisAsyncResult);

                        file_put_contents(__DIR__ . '/result.txt', $output);
                }
        }

        exit;
}

// Test authentication
$authTest = $api->authTest();

if ($authTest->success) {
        echo "Authentication test succeeded.\n";
} else {
        echo "Authentication test failed: " . $authTest->message;
        exit;
}

// Account information
$accountInfo = $api->accountInfo();

if ($accountInfo->success) {
        echo "Account info:";
        echo "  Email: {$accountInfo->email}";
        echo "  Requests left: {$accountInfo->requestsLeft}\n";
} else {
        echo "Getting account information failed: " . $accountInfo->message;
        exit;
}


// Synchronous Whois Query on domain
$domain = "woot.com";
$whoisQuerySync = $api->whoisQuerySynchronous($domain);

if ($whoisQuerySync->success) {
        echo "\nResult of synchronous whois query:";
        echo getDomainResults($domain, $whoisQuerySync);
} else {
        echo "Synchronous whois query failed: " . $whoisQuerySync->message;
        exit;
}

// Asynchronous Whois Query on domain
$domain = "woot.com";
$thisUrl = "http" . (!empty($_SERVER['HTTPS'])?"s":"") . "://" . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
$whoisQueryAsync = $api->whoisQueryAsynchronous($domain, $thisUrl);

if ($whoisQueryAsync->success) {
        echo "\nResult of asynchronous whois query: Success";
} else {
        echo "Asynchronous whois query failed: " . $whoisQueryAsync->message;
        exit;
}


// Polling
$whoisQueryPoll = $api->whoisQueryPolling($domain);

if ($whoisQueryPoll) {
        echo "\n\n\nResult of polling whois query:";
        echo getDomainResults($domain, $whoisQueryPoll);
} else {
        echo "Polling whois query failed: " . $whoisQueryPoll->message;
        exit;
}

function getDomainResults($domain, BwaApiWhoisQuery $whoisQuery)
{
        $output = "Whois query ({$domain}):";

        $output .= "\n   Output:";
        $output .= "\n     Domain: " . $whoisQuery->output->domain;
        $output .= "\n     Domain ID: " . $whoisQuery->output->domain_id;
        $output .= "\n     Status: " . implode(", ", $whoisQuery->output->status);
        $output .= "\n     Registered: " . $whoisQuery->output->registered;
        $output .= "\n     Available: " . $whoisQuery->output->available;
        $output .= "\n     Created on: " . $whoisQuery->output->created_on;
        $output .= "\n     Updated on: " . $whoisQuery->output->updated_on;
        $output .= "\n     Expires on: " . $whoisQuery->output->expires_on;

        if ($whoisQuery->output->registrar != null) {
                $output .= "\n   Registrar:";
                $output .= "\n     ID: " . $whoisQuery->output->registrar->id;
                $output .= "\n     Name: " . $whoisQuery->output->registrar->name;
                $output .= "\n     Organization: " . $whoisQuery->output->registrar->organization;
                $output .= "\n     URL: " . $whoisQuery->output->registrar->url;
        }

        if ($whoisQuery->output->registrant_contact != null) {
                $output .= "\n   Registrant contact:";
                $output .= "\n     ID: " . $whoisQuery->output->registrant_contact->id;
                $output .= "\n     Name: " . $whoisQuery->output->registrant_contact->name;
                $output .= "\n     Organization: " . $whoisQuery->output->registrant_contact->organization;
                $output .= "\n     Address: " . $whoisQuery->output->registrant_contact->address;
                $output .= "\n     City: " . $whoisQuery->output->registrant_contact->city;
                $output .= "\n     ZIP code: " . $whoisQuery->output->registrant_contact->zip;
                $output .= "\n     State: " . $whoisQuery->output->registrant_contact->state;
                $output .= "\n     Country: " . $whoisQuery->output->registrant_contact->country;
                $output .= "\n     Country code: " . $whoisQuery->output->registrant_contact->country_code;
                $output .= "\n     Phone: " . $whoisQuery->output->registrant_contact->phone;
                $output .= "\n     Fax: " . $whoisQuery->output->registrant_contact->fax;
                $output .= "\n     Email: " . $whoisQuery->output->registrant_contact->email;
                $output .= "\n     Created on: " . $whoisQuery->output->registrant_contact->created_on;
                $output .= "\n     Updated on: " . $whoisQuery->output->registrant_contact->updated_on;
                $output .= "\n     URL: " . $whoisQuery->output->registrant_contact->url;
                $output .= "\n     Ref URL: " . $whoisQuery->output->registrant_contact->ref_url;
                $output .= "\n     Handle: " . $whoisQuery->output->registrant_contact->handle;
        }

        if ($whoisQuery->output->admin_contact != null) {
                $output .= "\n   Administrative contact:";
                $output .= "\n     ID: " . $whoisQuery->output->admin_contact->id;
                $output .= "\n     Name: " . $whoisQuery->output->admin_contact->name;
                $output .= "\n     Organization: " . $whoisQuery->output->admin_contact->organization;
                $output .= "\n     Address: " . $whoisQuery->output->admin_contact->address;
                $output .= "\n     City: " . $whoisQuery->output->admin_contact->city;
                $output .= "\n     ZIP code: " . $whoisQuery->output->admin_contact->zip;
                $output .= "\n     State: " . $whoisQuery->output->admin_contact->state;
                $output .= "\n     Country code: " . $whoisQuery->output->admin_contact->country_code;
                $output .= "\n     Phone: " . $whoisQuery->output->admin_contact->phone;
                $output .= "\n     Fax: " . $whoisQuery->output->admin_contact->fax;
                $output .= "\n     Email: " . $whoisQuery->output->admin_contact->email;
                $output .= "\n     Created on: " . $whoisQuery->output->admin_contact->created_on;
                $output .= "\n     Updated on: " . $whoisQuery->output->admin_contact->updated_on;
                $output .= "\n     URL: " . $whoisQuery->output->admin_contact->url;
                $output .= "\n     Ref URL: " . $whoisQuery->output->admin_contact->ref_url;
                $output .= "\n     Handle: " . $whoisQuery->output->admin_contact->handle;
        }

        if ($whoisQuery->output->technical_contact != null) {
                $output .= "\n   Technical contact:";
                $output .= "\n     ID: " . $whoisQuery->output->technical_contact->id;
                $output .= "\n     Name: " . $whoisQuery->output->technical_contact->name;
                $output .= "\n     Organization: " . $whoisQuery->output->technical_contact->organization;
                $output .= "\n     Address: " . $whoisQuery->output->technical_contact->address;
                $output .= "\n     City: " . $whoisQuery->output->technical_contact->city;
                $output .= "\n     ZIP code: " . $whoisQuery->output->technical_contact->zip;
                $output .= "\n     State: " . $whoisQuery->output->technical_contact->state;
                $output .= "\n     Country code: " . $whoisQuery->output->technical_contact->country_code;
                $output .= "\n     Phone: " . $whoisQuery->output->technical_contact->phone;
                $output .= "\n     Fax: " . $whoisQuery->output->technical_contact->fax;
                $output .= "\n     Email: " . $whoisQuery->output->technical_contact->email;
                $output .= "\n     Created on: " . $whoisQuery->output->technical_contact->created_on;
                $output .= "\n     Updated on: " . $whoisQuery->output->technical_contact->updated_on;
                $output .= "\n     URL: " . $whoisQuery->output->technical_contact->url;
                $output .= "\n     Ref URL: " . $whoisQuery->output->technical_contact->ref_url;
                $output .= "\n     Handle: " . $whoisQuery->output->technical_contact->handle;
        }

        if ($whoisQuery->output->nameservers) {
                $output .= "\n   Name servers:";
                foreach ($whoisQuery->output->nameservers as $ns) {
                        /** @var BwaApiWhoisNameServer $ns */
                        $output .= "\n    Server: " . $ns->name;
                        $output .= "\n      IPv4: " . $ns->ipv4;
                        $output .= "\n      IPv6: " . $ns->ipv6;
                }
        }

        $output .= "\n\n  Raw outputs:\n";

        foreach ($whoisQuery->rawOutput as $ra) {
                $output .= "-------------------------------------------------------------\n{$ra}\n";
        }

        return $output;
}


BwaApi.php
<?php

class BwaApi
{

        const API_URL = "http://api.bulk-whois-api.com/api/";

        private $key;
        private $secret;

        public function __construct($key, $secret)
        {
                $this->key = $key;
                $this->secret = $secret;
        }

        /**
         * @param $action
         * @param array $params
         * @return array
         * @throws Exception
         */
        private function sendRequest($action, array $params = array())
        {
                $result = null;

                $postFields = http_build_query($params);
                $dateTime = new \DateTime('now', new \DateTimeZone('UTC'));
                $time = $dateTime->format('Y-m-d H:i:s');

                $message = $this->key . $time . $postFields;
                $signature = hash_hmac('sha512', $message, $this->secret);

                // generate extra headers
                $headers = array(
                        'Sign: ' . $signature,
                        'Time: ' . $time,
                        'Key: ' . $this->key
                );

                $ch = curl_init();
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_URL, self::API_URL . $action);
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
                curl_setopt($ch, CURLOPT_POST, 1);
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
                $res = curl_exec($ch);

                if ($res === false) {
                        throw new \Exception('Could not get a reply: ' . curl_error($ch));
                } else {
                        $result = json_decode($res, true);

                        if ($result === null) {
                                throw new \Exception('Invalid response received.');
                        }
                }

                return $result;
        }

        public function authTest()
        {
                $response = $this->sendRequest("authTest");
                $result = new BwaApiAuthTest($response);

                return $result;
        }

        public function accountInfo()
        {
                $response = $this->sendRequest("info");
                $result = new BwaApiInfo($response);

                return $result;
        }

        public function whoisQuerySynchronous($query)
        {
                $response = $this->sendRequest("query", array('query' => $query));
                $result = new BwaApiWhoisQuery($response);

                return $result;
        }

        public function whoisQueryAsynchronous($query, $callbackUrl)
        {
                $response = $this->sendRequest("query", array('query' => $query, "asyncCallback" => $callbackUrl));
                $result = new BwaApiResponse($response);

                return $result;
        }

        /**
         * @param string $query
         * @return bool|BwaApiWhoisQuery  false on error
         * @throws Exception
         */
        public function whoisQueryPolling($query)
        {
                $result = false;

                $response = $this->sendRequest("query", array('query' => $query, 'polling' => "1"));
                $pollingResponse = new BwaApiPollResponse($response); // may throw exception
                $url = $pollingResponse->resultUrl;

                $done = false;
                while (!$done) {

                        sleep(5);
                        $json = file_get_contents($url);
                        $response = json_decode($json, true);

                        if ($response === null) {
                                throw new \Exception("Invalid JSON.");
                        } else {
                                $result = new BwaApiWhoisQuery($response);

                                if (!$result->success && $result->message === "Pending.") {
                                        # Not finished yet.
                                } else {
                                        $done = true;
                                }
                        }
                }

                return $result;
        }

}

class BwaApiResponse
{
        /** @var int */
        public $success;
        /** @var string|null */
        public $message;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }

}

class BwaApiPollResponse extends BwaApiResponse
{
        /** @var string */
        public $resultUrl;
}

class BwaApiAuthTest extends BwaApiResponse
{

}

class BwaApiInfo extends BwaApiResponse
{
        /** @var string */
        public $email;
        /** @var int */
        public $requestsLeft;
}

class BwaApiWhoisQuery extends BwaApiResponse
{
        /** @var BwaApiWhoisOutput */
        public $output;
        /** @var string[] */
        public $rawOutput;

        public function __construct(array $data)
        {
                parent::__construct($data);

                $output = array_key_exists('output', $data) ? $data['output'] : array();
                $this->output = new BwaApiWhoisOutput($output);
        }

}

class BwaApiWhoisOutput
{
        /** @var string */
        public $domain;
        /** @var string */
        public $domain_id;
        /** @var string[] */
        public $status;
        /** @var bool */
        public $registered;
        /** @var bool */
        public $available;
        /** @var string */
        public $created_on;
        /** @var string */
        public $updated_on;
        /** @var string */
        public $expires_on;
        /** @var BwaApiWhoisRegistrar|null */
        public $registrar;
        /** @var BwaApiWhoisContact|null */
        public $registrant_contact;
        /** @var BwaApiWhoisContact|null */
        public $admin_contact;
        /** @var BwaApiWhoisContact|null */
        public $technical_contact;
        /** @var BwaApiWhoisNameServer[] */
        public $nameservers = array();

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }

                if (!empty($data['registrar'])) {
                        $this->registrar = new BwaApiWhoisRegistrar($data['registrar']);
                }

                foreach (array('registrant_contact', 'admin_contact', 'technical_contact') as $contactType) {
                        if (!empty($data[$contactType])) {
                                $this->{$contactType} = new BwaApiWhoisContact($data[$contactType]);
                        }
                }

                if (!empty($data['nameservers'])) {
                        $this->nameservers = array();
                        foreach ($data['nameservers'] as $nameserver) {
                                $this->nameservers[] = new BwaApiWhoisNameServer($nameserver);
                        }
                }
        }
}

class BwaApiWhoisRegistrar
{
        /** @var string */
        public $id;
        /** @var string */
        public $name;
        /** @var string */
        public $organization;
        /** @var string */
        public $url;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }
}

class BwaApiWhoisContact
{
        /** @var string */
        public $id;
        /** @var string */
        public $name;
        /** @var string */
        public $organization;
        /** @var string */
        public $address;
        /** @var string */
        public $city;
        /** @var string */
        public $zip;
        /** @var string */
        public $state;
        /** @var string */
        public $country;
        /** @var string */
        public $country_code;
        /** @var string */
        public $phone;
        /** @var string */
        public $fax;
        /** @var string */
        public $email;
        /** @var string */
        public $created_on;
        /** @var string */
        public $updated_on;
        /** @var string */
        public $url;
        /** @var string */
        public $ref_url;
        /** @var string */
        public $handle;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }
}

class BwaApiWhoisNameServer
{
        /** @var string|null */
        public $name;
        /** @var string|null */
        public $ipv4;
        /** @var string|null */
        public $ipv6;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }
}


5.3 PHP Implementation – Bulk Asynchronous Requests

This example demonstrates how to perform a large volume querying using Bulk Whois API. See the description in the sample source code for more information about this sample and instructions on how to use it.

Download sample

sample-bulk.php
<?php
//
// This script is an example of how to use Bulk Whois API in PHP 5.
// It allows you to perform any amount of WHOIS queries written into an input file.
//
// This script is provided "AS IS" without warranties of any kind, either express or implied.
// Everyone is allowed to use it, copy it, or create derived products from it, completely
// free of charge, provided that Bulk Whois API TOS (see the link below) is respected
// and not violated.
//
//
// Copyright (c) 2015 - Bulk Whois API
//
// Relevant links:
//  * https://bulk-whois-api.com/ - Bulk Whois API homepage
//  * https://bulk-whois-api.com/documentation/ - Bulk Whois API documentation
//  * https://bulk-whois-api.com/privacy-policy-and-tos/ - Bulk Whois API TOS
//
//
// How does it work?
//  * This script loads input queries stored in $whoisInputFile, separated by newlines.
//  * Each time it runs it removes the first line from the input file and sends it
//    as a query to BWA API server.
//  * Asynchronous callback mode is used with BWA API so that this script is called by BWA
//    API server when the job is done.
//  * BWA uses POST request with JSON data to report the results to the callback.
//  * The results from the server are written to $whoisResultsFile. Each result is written
//    on a separate line in the format "QUERY::RESULT" (without the quotes).
//  * The script logs its progress to $logFile, so in case of any problems, check the log
//    file.
//
// How to use it?
//  * You have to have BWA account with non-zero requests and obtain API key and secret. 
//    See BWA API documentation on how to obtain API key and secret.
//  * Fill in your BWA API key and secret to $apiKey and $apiSecret global variables.
//  * Create a list of WHOIS queries and store them to whois-input.txt.
//    Separate queries in the input file with newlines. Each query should be either a domain 
//    or IP address.
//  * Copy whois-input.txt, this script and BwaApi.php file to the same folder on a PHP 
//    hosting server.
//  * Execute the script using your browser - simply access the script's URL.
//  * Wait until the input file is empty or until log file does not grow.
//  * If whois-input.txt is empty, the work is done. Otherwise, check bwa-log.txt to see
//    the problem.
//


include __DIR__ . "/BwaApi.php";

 
# Settings
 
// Your API key & API secret

$apiKey = 'Your API key here'; // BWA-XXX
$apiSecret = 'Your API secret here';



 
// Is logging enabled
$loggingEnabled = true;
 
// Name of input file, in which queries waiting for processing are separated by newlines.
$whoisInputFile = __DIR__ . '/whois-input.txt';
 
// Name of output file with unparsed results.
$whoisResultsFile = __DIR__ . '/whois-results.txt';
 
// Name of log file.
$logFile = __DIR__ . '/bwa-log.txt';
 
// URL of this script.
$thisUrl = "http" . (!empty($_SERVER['HTTPS'])?"s":"") . "://" . $_SERVER['SERVER_NAME']
         . $_SERVER['REQUEST_URI'];
 
 
ini_set('display_errors', 1);
error_reporting(E_ALL);
 




// API initialization
$api = new BwaApi($apiKey, $apiSecret);


// Setup unique token
$token = uniqid();
if (isset($_GET["token"])) $token = $_GET["token"];


 
/**
 * Appends $message to file $fileName.
 *
 * @param string $fileName Name of the file to append to.
 * @param string $data Data to append.
 */
function fileAppend($fileName, $data)
{
  $fp = fopen($fileName, "a");
 
  flock($fp, LOCK_EX);
  fwrite($fp, $data);
  flock($fp, LOCK_UN);
 
  fclose($fp);
}
 
 
/**
 * Appends $message to log file with time stamp.
 *
 * @param string $message
 */
function mlog($message)
{
  global $logFile, $loggingEnabled, $token;
 
  if ($loggingEnabled)
  {
    $dateTime = new \DateTime('now', new \DateTimeZone('UTC'));
    $time = $dateTime->format('Y-m-d H:i:s');
    $line = '[' . $time . ']<' . $token . '> ' . $message . "\n";
 
    fileAppend($logFile, $line);
  }
}
 
 
/**
 * Appends $message to output file.
 *
 * @param string $message
 */
function writeOutput($message)
{
  global $whoisResultsFile;
 
  $line = $message . "\n";
  fileAppend($whoisResultsFile, $line);
}
 

/**
 * This function is called during the initial request that starts the job.
 * It verifies that we have access rights to all files that we need.
 *
 * @return bool Returns true if succeeded, false otherwise.
 */
function initialization()
{
  $result = false;
  global $whoisInputFile, $whoisResultsFile, $logFile;

  // Clean log file.
  $res = file_put_contents($logFile, "");
  if ($res !== false)
  {
    mlog("initialization()");
 
    // Check input is readable. 
    $inputContents = file_get_contents($whoisInputFile);
    if ($inputContents !== false)
    {
      // Check input is writable.
      $res = file_put_contents($whoisInputFile, $inputContents);
      if ($res !== false)
      {
        // Clean output.
        $res = file_put_contents($whoisResultsFile, "");
        if ($res !== false)
        {
          $result = true;
        }
        else mlog("initialization(): ERROR: Unable to write to output file "
                . "'$whoisResultsFile'.");
      }
      else mlog("initialization(): ERROR: Unable to write to input file '$whoisInputFile'.");
    }
    else mlog("initialization(): ERROR: Unable to read input file '$whoisInputFile'.");
  }
  else mlog("initialization(): ERROR: Unable to write to log file '$logFile'.");
 
  mlog("initialization(-):" . (int)$result);
  return $result;
}
 
 
/**
 * Checks if there are any incoming data in form of JSON POST.
 * If not, does nothing and returns immediately. If yes, the data are processed.
 *
 * @return bool Returns true if succeeded, false otherwise.
 */
function processIncomingData()
{
  $result = false;
  if (!isset($_GET["query"])) 
    $result = initialization();

  mlog("processIncomingData()");
  try
  {
    if (isset($_GET["query"]))
    {
      $query = $_GET["query"];
      mlog("processIncomingData(): Query is '$query'.\n");
   
      // Load data from input.
      $json = file_get_contents('php://input');
      if ($json)
      {
        $jsonLen = strlen($json);
        mlog("processIncomingData(): Incoming data ($jsonLen bytes) detected "
           . "(first 256 bytes):\n" . substr($json, 0, 256) . "\n");
      
        $response = json_decode($json, true);
        if ($response !== null)
        {
          $whoisAsyncResult = new BwaApiWhoisQuery($response);
          if ($whoisAsyncResult->success == 1)
          {
            mlog("processIncomingData(): Call succeeded.");
          }
          else 
          {
            mlog("processIncomingData(): ERROR: Call failed with error message: '"
                  . $response['message'] . "'");
          }
          writeOutput($query . "::" . $json);
          $result = true;
        }
        else mlog("processIncomingData(): ERROR: Call failed. Server response is not a valid"
                . " JSON message.");
      }
      else mlog("processIncomingData(): ERROR: Call failed. No JSON POST data received.");
    }
    else mlog("processIncomingData(): No incoming data. Initialization called.");
  } 
  catch (\Exception $e)
  {
    mlog("processIncomingData(): ERROR: Exception occurred: '" . $e->getMessage() . "'.");
  }
 
  mlog("processIncomingData(-):" . (int)$result);
  return $result;
}
 
 
/**
 * Removes and returns first line from the input file.
 *
 * @return string Returns first line of the input file if succeeded, false otherwise.
 */
function removeLineFromInputFile()
{
  mlog("removeLineFromInputFile()");
 
  global $whoisInputFile;
  $result = false;
 
  $lines = file($whoisInputFile, FILE_IGNORE_NEW_LINES);
  if ($lines !== false)
  {
    if (count($lines) > 0)
    {
      $firstLine = $lines[0];
      $newLines = array_slice($lines, 1);
      $newContent = implode("\n", $newLines);
      $res = file_put_contents($whoisInputFile, $newContent);
      if ($res !== false)
      {
        $result = trim($firstLine);
        if (empty($result)) $result = false;
      } 
      else mlog("removeLineFromInputFile(): ERROR: Unable to write to file"
              . " '$whoisInputFile'.");
    } 
    else mlog("removeLineFromInputFile(): Empty input file '$whoisInputFile'.");
  } 
  else mlog("removeLineFromInputFile(): ERROR: Unable to read from file"
          . " '$whoisInputFile'.");
   
 
  mlog("removeLineFromInputFile(-):" . ($result !== false ? $result : "false"));
  return $result;
}
 
 
// Return what is expected by API.
echo "BWA: OK\n";


// If processing is OK, continue.
if (processIncomingData())
{
  $done = false;
  while (!$done) 
  {
    // Load query from input file.
    $query = removeLineFromInputFile();
    if ($query !== false)
    {
      try
      {
        $arg = "query=" . urlencode($query) . "&token=" . urlencode($token);
        $url = strtok($thisUrl, '?') . "?$arg";
        mlog("Callback URL set to '$url'.");
   
        $whoisQueryAsync = $api->whoisQueryAsynchronous($query, $url);
        if ($whoisQueryAsync->success) 
        {
          mlog("API call succeeded, waiting for callback ...");      
          $done = true;
        }
        else 
        {
          mlog("ERROR: API call failed with error: " . $whoisQueryAsync->message);
          writeOutput($query . "::" . json_encode($whoisQueryAsync));
        }
      }
      catch (\Exception $e)
      {
        mlog("ERROR: Exception occurred: '" . $e->getMessage() . "'.");
        $done = true;
      }
    }
    else 
    {
      mlog("No more queries to process, all done!");
      $done = true;
    }
  }
}
else mlog("ERROR: Processing incoming data failed, stopping execution.");

BwaApi.php
<?php

class BwaApi
{

        const API_URL = "http://api.bulk-whois-api.com/api/";

        private $key;
        private $secret;

        public function __construct($key, $secret)
        {
                $this->key = $key;
                $this->secret = $secret;
        }

        /**
         * @param $action
         * @param array $params
         * @return array
         * @throws Exception
         */
        private function sendRequest($action, array $params = array())
        {
                $result = null;

                $postFields = http_build_query($params);
                $dateTime = new \DateTime('now', new \DateTimeZone('UTC'));
                $time = $dateTime->format('Y-m-d H:i:s');

                $message = $this->key . $time . $postFields;
                $signature = hash_hmac('sha512', $message, $this->secret);

                // generate extra headers
                $headers = array(
                        'Sign: ' . $signature,
                        'Time: ' . $time,
                        'Key: ' . $this->key
                );

                $ch = curl_init();
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_URL, self::API_URL . $action);
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
                curl_setopt($ch, CURLOPT_POST, 1);
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
                $res = curl_exec($ch);

                if ($res === false) {
                        throw new \Exception('Could not get a reply: ' . curl_error($ch));
                } else {
                        $result = json_decode($res, true);

                        if ($result === null) {
                                throw new \Exception('Invalid response received.');
                        }
                }

                return $result;
        }

        public function authTest()
        {
                $response = $this->sendRequest("authTest");
                $result = new BwaApiAuthTest($response);

                return $result;
        }

        public function accountInfo()
        {
                $response = $this->sendRequest("info");
                $result = new BwaApiInfo($response);

                return $result;
        }

        public function whoisQuerySynchronous($query)
        {
                $response = $this->sendRequest("query", array('query' => $query));
                $result = new BwaApiWhoisQuery($response);

                return $result;
        }

        public function whoisQueryAsynchronous($query, $callbackUrl)
        {
                $response = $this->sendRequest("query", array('query' => $query, "asyncCallback" => $callbackUrl));
                $result = new BwaApiResponse($response);

                return $result;
        }

        /**
         * @param string $query
         * @return bool|BwaApiWhoisQuery  false on error
         * @throws Exception
         */
        public function whoisQueryPolling($query)
        {
                $result = false;

                $response = $this->sendRequest("query", array('query' => $query, 'polling' => "1"));
                $pollingResponse = new BwaApiPollResponse($response); // may throw exception
                $url = $pollingResponse->resultUrl;

                $done = false;
                while (!$done) {

                        sleep(5);
                        $json = file_get_contents($url);
                        $response = json_decode($json, true);

                        if ($response === null) {
                                throw new \Exception("Invalid JSON.");
                        } else {
                                $result = new BwaApiWhoisQuery($response);

                                if (!$result->success && $result->message === "Pending.") {
                                        # Not finished yet.
                                } else {
                                        $done = true;
                                }
                        }
                }

                return $result;
        }

}

class BwaApiResponse
{
        /** @var int */
        public $success;
        /** @var string|null */
        public $message;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }

}

class BwaApiPollResponse extends BwaApiResponse
{
        /** @var string */
        public $resultUrl;
}

class BwaApiAuthTest extends BwaApiResponse
{

}

class BwaApiInfo extends BwaApiResponse
{
        /** @var string */
        public $email;
        /** @var int */
        public $requestsLeft;
}

class BwaApiWhoisQuery extends BwaApiResponse
{
        /** @var BwaApiWhoisOutput */
        public $output;
        /** @var string[] */
        public $rawOutput;

        public function __construct(array $data)
        {
                parent::__construct($data);

                $output = array_key_exists('output', $data) ? $data['output'] : array();
                $this->output = new BwaApiWhoisOutput($output);
        }

}

class BwaApiWhoisOutput
{
        /** @var string */
        public $domain;
        /** @var string */
        public $domain_id;
        /** @var string[] */
        public $status;
        /** @var bool */
        public $registered;
        /** @var bool */
        public $available;
        /** @var string */
        public $created_on;
        /** @var string */
        public $updated_on;
        /** @var string */
        public $expires_on;
        /** @var BwaApiWhoisRegistrar|null */
        public $registrar;
        /** @var BwaApiWhoisContact|null */
        public $registrant_contact;
        /** @var BwaApiWhoisContact|null */
        public $admin_contact;
        /** @var BwaApiWhoisContact|null */
        public $technical_contact;
        /** @var BwaApiWhoisNameServer[] */
        public $nameservers = array();

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }

                if (!empty($data['registrar'])) {
                        $this->registrar = new BwaApiWhoisRegistrar($data['registrar']);
                }

                foreach (array('registrant_contact', 'admin_contact', 'technical_contact') as $contactType) {
                        if (!empty($data[$contactType])) {
                                $this->{$contactType} = new BwaApiWhoisContact($data[$contactType]);
                        }
                }

                if (!empty($data['nameservers'])) {
                        $this->nameservers = array();
                        foreach ($data['nameservers'] as $nameserver) {
                                $this->nameservers[] = new BwaApiWhoisNameServer($nameserver);
                        }
                }
        }
}

class BwaApiWhoisRegistrar
{
        /** @var string */
        public $id;
        /** @var string */
        public $name;
        /** @var string */
        public $organization;
        /** @var string */
        public $url;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }
}

class BwaApiWhoisContact
{
        /** @var string */
        public $id;
        /** @var string */
        public $name;
        /** @var string */
        public $organization;
        /** @var string */
        public $address;
        /** @var string */
        public $city;
        /** @var string */
        public $zip;
        /** @var string */
        public $state;
        /** @var string */
        public $country;
        /** @var string */
        public $country_code;
        /** @var string */
        public $phone;
        /** @var string */
        public $fax;
        /** @var string */
        public $email;
        /** @var string */
        public $created_on;
        /** @var string */
        public $updated_on;
        /** @var string */
        public $url;
        /** @var string */
        public $ref_url;
        /** @var string */
        public $handle;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }
}

class BwaApiWhoisNameServer
{
        /** @var string|null */
        public $name;
        /** @var string|null */
        public $ipv4;
        /** @var string|null */
        public $ipv6;

        public function __construct(array $data)
        {
                foreach ($data as $key => $value) {

                        if (!property_exists($this, $key)) {
                                throw new \Exception("Invalid property {$key}.");
                        }

                        $this->{$key} = $value;
                }
        }
}


5.4 NodeJS Implementation – Basic Query API Call

A basic NodeJS implementation of a singe synchronous call of the query API. Note that the synchronous mode should not be used for large volume querying. The author of this sample is Francesco Merletti.

Download sample

bwa.js
// This source code was kindly provided by Francesco Merletti.

// To install dependencies run (note that a specific version of jssha is needed):
// npm install request moment jssha@1.6.0 --save

// Require necessary libs
var request = require('request'),
  moment  = require('moment'),
  jsSHA   = require('jssha'),
  config = {
    bwaKey:     'YOUR BWA KEY HERE',
    bwaSecret:  'YOUR BWA SECRET HERE',
    bwaBaseUrl: 'http://api.bulk-whois-api.com/api/query'
  };

function getWhoisData(domain, callback) {
  'use strict';
 
  var date = moment.utc().format('YYYY-MM-DD HH:mm:ss'),
    requestBody = 'query=' + domain,
    shaObj = new jsSHA(config.bwaKey + date + requestBody, 'TEXT'),
    options = {
      url: config.bwaBaseUrl,
      method: 'POST',
      body: requestBody,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Key': config.bwaKey,
        'Time': date,
        'Sign': shaObj.getHMAC(config.bwaSecret, "TEXT", "SHA-512", "HEX").toLowerCase()
      }
    };

  request(options, function (err, response, body) {
    if (err) {
      console.log('There was an error with the whois request.');
      callback(err, null);
      return;
    }
   
    body = JSON.parse(body);
   
    if (response.statusCode !== 200 || body.success !== 1) {
      console.log('There was an error in the whois response.');
      callback('Error', null);
      return;
    }
   
    // Get the data you need here or return the whole body.output
    callback(null, body.output);
  });
}
 
getWhoisData('google.com', function (err, data) {
  if (err) {
    console.log(err);
    return;
  }
  console.log(data);
})