func (c *Client) BulkLookupWithContext(ctx context.Context, ips []string) ([]*IP, error) {
	resp, err := c.RawBulkLookupWithContext(ctx, ips)
	if err != nil {
		return nil, err
	}
	defer func() {
		_, _ = io.Copy(ioutil.Discard, resp.Body)
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Wrap(err, "failed to read response body")
	}
	var bip []bulkIP
	if err := json.Unmarshal(body, &bip); err != nil {
		return nil, errors.Wrap(err, "failed to parse JSON")
	}
	res := make([]*IP, len(bip))
	var retErr error
	for i, ip := range bip {
		if len(ip.Message) > 0 && retErr == nil {
			retErr = Error{
				m: ip.Message,
				c: resp.StatusCode,
				i: i,
			}
			continue
		}
		res[i] = bulkToIP(ip)
	}
	if retErr != nil {
		return res, retErr
	}
	return res, nil  
}
BulkLookupWithContext is a BulkLookup with a provided context.Context.
package ipdata
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"time"
	"github.com/pkg/errors"
)
const Version = "0.7.1"
const fqpn = "github.com/theckman/go-ipdata"
const (
	apiEndpoint  = "https://api.ipdata.co/"
	apiAuthParam = "api-key"
)
var userAgent = fmt.Sprintf(
	"go-ipdata/%s (%s) Go-http-client/%s (%s %s)",
	Version, fqpn, runtime.Version(), runtime.GOOS, runtime.GOARCH,
)
var errAPIKey = errors.New("apiKey cannot be an empty string")
type Client struct {
	c *http.Client  
	e string        
	k string        
}
func NewClient(apiKey string) (Client, error) {
	if len(apiKey) == 0 {
		return Client{}, errAPIKey
	}
	return Client{
		c: newHTTPClient(),
		e: apiEndpoint,
		k: apiKey,
	}, nil
}
type apiErr struct {
	Message string `json:"message"`
}
func (c Client) RawLookup(ip string) (*http.Response, error) {
	return c.RawLookupWithContext(context.Background(), ip)
}
func (c Client) RawLookupWithContext(ctx context.Context, ip string) (*http.Response, error) {
	req, err := newGetRequestWithContext(ctx, c.e+ip, c.k)
	if err != nil {
		return nil, errors.Wrapf(err, "error building request to look up %s", ip)
	}
	resp, err := c.c.Do(req)
	if err != nil {
		return nil, errors.Wrapf(err, "http request to %q failed", req.URL.Scheme+"://"+req.URL.Host+req.URL.Path)
	}
	switch resp.StatusCode {
	case http.StatusOK:  
		return resp, nil
	default:
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to read body from response with status code %q: %s", resp.Status, err)
		}
		var a apiErr
		if err := json.Unmarshal(body, &a); err != nil {
			return nil, errors.Errorf("request for %q failed (unexpected response): %s: %v", ip, resp.Status, err)
		}
		return nil, newError(a.Message, resp.StatusCode)
	}
}
func decodeIP(r io.Reader) (IP, error) {
	body, err := ioutil.ReadAll(r)
	if err != nil {
		return IP{}, err
	}
	ip := IP{}
	if err := json.Unmarshal(body, &ip); err != nil {
		return IP{}, fmt.Errorf("failed to parse JSON: %s", err)
	}
	return ip, nil
}
func (c Client) Lookup(ip string) (IP, error) {
	return c.LookupWithContext(context.Background(), ip)
}
func (c Client) LookupWithContext(ctx context.Context, ip string) (IP, error) {
	resp, err := c.RawLookupWithContext(ctx, ip)
	if err != nil {
		return IP{}, err
	}
	defer func() {
		_, _ = io.Copy(ioutil.Discard, resp.Body)
		_ = resp.Body.Close()
	}()
	pip, err := decodeIP(resp.Body)
	if err != nil {
		return IP{}, err
	}
	return pip, nil
}
func newGetRequestWithContext(ctx context.Context, urlStr, apiKey string) (*http.Request, error) {
	if len(urlStr) == 0 {
		return nil, errors.New("url cannot be an empty string")
	}
	if len(apiKey) == 0 {
		return nil, errAPIKey
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlStr, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Accept", "application/json")
	q := url.Values{apiAuthParam: []string{apiKey}}
	req.URL.RawQuery = q.Encode()
	return req, nil
}
func (c *Client) RawBulkLookup(ips []string) (*http.Response, error) {
	return c.RawBulkLookupWithContext(context.Background(), ips)
}
	func (c *Client) RawBulkLookupWithContext(ctx context.Context, ips []string) (*http.Response, error) {
	req, err := newBulkPostRequestWithContext(ctx, c.e+"bulk", c.k, ips)
	if err != nil {
		return nil, errors.Wrap(err, "error building bulk lookup request")
	}
	resp, err := c.c.Do(req)
	if err != nil {
		return nil, errors.Wrapf(err, "http request to %q failed", req.URL.Scheme+"://"+req.URL.Host+req.URL.Path)
	}
	switch resp.StatusCode {
	case http.StatusOK:  
		return resp, nil
	default:
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to read body from response with status code %q: %s", resp.Status, err)
		}
		var a apiErr
		if err := json.Unmarshal(body, &a); err != nil {
			return nil, errors.Errorf("request failed (unexpected response): %s: %v", resp.Status, err)
		}
		return nil, newError(a.Message, resp.StatusCode)
	}
}
func newBulkPostRequestWithContext(ctx context.Context, urlStr, apiKey string, ips []string) (*http.Request, error) {
	if len(urlStr) == 0 {
		return nil, errors.New("url cannot be an empty string")
	}
	if len(apiKey) == 0 {
		return nil, errAPIKey
	}
	if len(ips) == 0 {
		return nil, errors.New("must provide at least one IP")
	}
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	buf := &bytes.Buffer{}
	if err := json.NewEncoder(buf).Encode(ips); err != nil {
		return nil, errors.Wrap(err, "failed to encode JSON")
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, urlStr, buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	q := url.Values{apiAuthParam: []string{apiKey}}
	req.URL.RawQuery = q.Encode()
	return req, nil
}
func (c *Client) BulkLookup(ips []string) ([]*IP, error) {
	return c.BulkLookupWithContext(context.Background(), ips)
}