Voxel's Hosting API

Voxel's Hosting API (hAPI) exposes much of Voxel's hosting infrastructure via a standardized programmatic interface. Using hAPI, you can manage and monitor your Voxel services, using your programming language of choice. Here are just a few of the things you can do with hAPI:

  • View details of your existing Voxel "devices", including VoxSERVERS, VoxCLOUD VMs, VoxCAST CDN sites, and other devices like firewalls, switches, etc.
  • Adjust the configuration of your VoxCAST CDN to change parameters like cache expiration time; origin settings; log formats; etc.
  • Fetch VoxCAST logs or purge objects from VoxCAST's cache.
  • Launch a new VoxCLOUD VM in Singapore.
  • Instantly provision a new physical VoxSERVER, or reimage a VoxSERVER you already own.
  • Add a monitor to one of your servers that contacts Voxel's support staff if the server stops responding to HTTP requests.
  • Fetch bandwidth usage data for the last month for your VoxCAST sites.
  • Manage hAPI permissions for different staff members at your company; allow some people access to add or remove devices, but give "read-only" access to others.

In line with our core philosophy, hAPI is built upon an open, standards based, fully documented platform. It is available to all customers at no additional charge.

hAPI is an evolving interface to Voxel's infrastructure; we will continue to expose more functionality via hAPI. If you have any questions about what you can or can't do with hAPI, or how to do it, get in touch with our expert support staff any time.

Technology overview

hAPI is a RESTful API based on HTTP requests and XML or JSON responses. Each hAPI request is authenticated. If you're familiar with the APIs of Flickr, Amazon's S3, del.icio.us, or a host of other web services, you'll feel right at home with Voxel's hAPI.

This page gives a technical overview of hAPI, meant for programmers who are integrating hAPI into their systems.

hAPI toolkits

Voxel's customers build their applications on a variety of platforms. Several of them have written toolkits for interacting with hAPI, and have been kind enough to share. If you have hAPI code you'd like to share (or that you need help with) let us know! We strongly encourage you to use one of these toolkits for talking to hAPI; we'll keep them up to date.

If you're using one of these toolkits, you may want to skip right to some example code, but we'd recommend you take a quick scan through the rest of this document to get a good understanding of how hAPI works.

Endpoints

hAPI is accessed by making HTTP requests to an endpoint URL, in which GET or POST variables contain information like which hAPI method you're accessing; parameters to the method; and authentication details that prove you have access to call the method.

Every hAPI endpoint is accessible via standard HTTP (port 80) and SSL-enabled HTTPS (port 443). The default hAPI endpoint URL is:

http://api.voxel.net/

The default SSL endpoint is:

https://api.voxel.net/

Interface versions

hAPI's interface evolves with Voxel's infrastructure. To provide some amount of stability, hAPI uses interface versions. hAPI's interface (methods, parameters, etc.) is fixed to a version number. Different interface versions of hAPI are available at different endpoint URLs. For example, the stable HTTP endpoint for this version of hAPI, "1.0", is:

http://api.voxel.net/version/1.0/

and the stable SSL-enabled endpoint is:

https://api.voxel.net/version/1.0/

The "default" hAPI endpoint will adjust to point to different interface versions over time. Version-specific endpoints are stable, but will generally be deprecated after some transition period when a new interface version becomes available.

The hAPI method hapi.version can be used to obtain a description of the interface version you're using.

Methods

All the hAPI methods are in the "voxel" namespace and are called something like voxel.group.method_name. Every request to hAPI must include a method parameter. For example, to call the test.echo method, request:

http://api.voxel.net/version/1.0/?method=voxel.test.echo
    &name=value&[auth_params]

Response formats

Responses are either XML (the default) or JSON. You can control the response format by passing the format parameter (the value should be either "xml" or "json_v2"). For example:

http://api.voxel.net/version/1.0/?method=voxel.test.echo
    &name=value&[auth_params]&format=json_v2

returns a JSON response. The two response formats are detailed below.

XML responses

Methods respond in a simple XML format by default:

<?xml version="1.0"?>
<rsp stat="ok">
	[xml-payload-here]
</rsp>

There may be an additional "warn" attribute in the response in certain cases, such as when you are using a deprecated hAPI endpoint URL. For example:

<?xml version="1.0"?>
<rsp stat="ok" warn="Some warning message">
	[xml-payload-here]
</rsp>

Additionally, warning messages are placed in a special X-hAPI-Warning HTTP response header.

When there is an error, the response is instead like:

<?xml version="1.0"?>
<rsp stat="fail">
	<err code="[error-code]" msg="[error-message]"/>
	<method>voxel.method.name</method>
	<parameters>
		<param name="name">value</param>
		...
	</parameters>
</rsp>

JSON responses

JSON responses are directly analogous to their XML counterparts.

The below table shows how we've chosen to represent XML data in the JSON format:

XML                                       JSON                                                      JavaScript Examples

<rsp><e/></rsp>                           {"e":[{}]}                                                e[0]
<rsp><e>text</e></rsp>                    {"e":[{"#text":"text"}]}                                  e[0]["#text"]
<rsp><e name="value"/></rsp>              {"e":[{"@attributes":{"name":"value"}}]}                  e[0]["@attributes"].name
<rsp><e name="value">text</e></rsp>       {"e":[{"@attributes":{"name":"value"},"#text":"text"}]}   e[0]["#text"]
<rsp><e><a>text</a><a>text</a></e></rsp>  {"e":[{"a":[{"#text":"text"},{"#text":"text"}]}]}         e[0].a[1]["#text"]
<rsp><e><a/><a/><a>text</a></e></rsp>     {"e":[{"a":[{},{},{"#text":"text"}]}]}                    e[0].a[2]["#text"]
<rsp><e><a>text</a><b>text</b></e></rsp>  {"e":[{"a":[{"#text":"text"}],"b":[{"#text":"text"}]}]}   e[0].a[0]["#text"],
                                                                                                    e[0].b[0]["#text"]

A few important points:

  • To avoid changing the format of our response when XML nodes have attributes, we consistently mark node values with the "#text" label, and attributes with the "@attributes" label. If we didn't do this, XML such as "<element>value</element>" would be converted to "element":"value". If an attribute is added to such an element, as in <element id="1">value</element>, a completely different representation would be required. We prefer to provide our users with a consistent interface.
  • XML does not distinguish between scalars and arrays. For example, in the XML "<list><element>value</element></list>," there is no programmatic way to know if this is a list with one element, or structural representation of a single value. To provide a consistent interface, we treat all XML nodes as JSON arrays.
  • There is no top-level "rsp" element; the response starts with what would be the attributes of "rsp" in an XML response.

A successful JSON response looks like:

{
    "@attributes": {
        "stat":"ok"
    },
    "api_sig":[{
        "#text":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }],
    "format":[{
        "#text":"json_v2"
    }],
    "key": [{
        "#text":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }],
    "method":[{
        "#text":"voxel.test.echo"
    }],
    "timestamp":[{
        "#text":"2009-11-05T03:37:40"
    }]
}

When there is an error, the response is instead like:

{
    "@attributes": {
        "stat":"fail"
    },"err":[{
        "@attributes": {
            "code":"[error-code]",
            "msg":"[error-message]"
        }
    }],
    "method":[{
        "#text":"voxel.method.name"
    }],
    "parameters":[{
        "param":[
            {
                "@attributes": {
                    "name":"api_sig"
                },
                "#text":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            },{
                "@attributes": {
                    "name":"format"
                },
                "#text":"json_v2"
            },{
                "@attributes": {
                    "name":"key"
                },
                "#text":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            },{
                "@attributes": {
                "name":"timestamp"
                },
                "#text":"2009-11-05T03:38:35"
            }
        ]
    }]
}

hAPI keys and secrets

Access to hAPI methods is authenticated using an insecure "hAPI key" indicating the user or program accessing hAPI; and a secure "hAPI secret" known only to the user/program and hAPI. Every hAPI key/secret pair is associated with a particular username in Voxel's customer portal. There is exactly one hAPI key/secret per Voxel username. A customer may add additional usernames under their account in the customer portal, each of which has an associated key/secret.

The main purpose of separating hAPI keys/secrets from normal customer portal usernames/passwords is to add security. hAPI keys/secrets are machine generated and difficult to guess; are suitable for inclusion in code (unlike personal passwords); can be reset at any time via the customer portal if compromised; and if desired, can be used to provide hAPI-only access to Voxel's infrastructure.

The key/secret associated with a Voxel username can be obtained via a special hAPI method that is not subject to the standard hAPI authentication mechanism, but is only accessible via an SSL-enabled hAPI endpoint (e.g., https://api.voxel.net/):

voxel.hapi.authkeys.read

The customer must use Basic HTTP authentication with their their Voxel username/password when calling this method, e.g., they should request:

https://username:password@api.voxel.net/version/1.0/
    ?method=voxel.hapi.authkeys.read

without any key, timestamp, or api_sig parameters. If the username/password are correct, the method will return the hAPI key/secret pair associated with the username.

The method:

voxel.hapi.authkeys.reset

can be used to replace an existing key/secret pair with a new one, which will be returned in the response. It must also be accessed via a secure hAPI endpoint.

Authenticating hAPI requests

Every request to hAPI (except for the hapi.authkeys.read method) must include three authentication parameters:

key
The key from the hAPI key/secret pair.
timestamp
The current timestamp in ISO 8601 format. This can be obtained in PHP with date(DATE_ISO8601). The timestamp must be within 15 minutes of the hAPI server's NTP-synchronized time.
api_sig
The MD5 hash of a request-specific string. The string is computed by first concatenating all of the GET/POST variables sent with the request, like "key1value1key2value2...", in alphabetical order by variable name. This includes key and timestamp. Concatenate this string at the end of the hAPI secret. Finally, compute the MD5 hash of the string. The following PHP code fragment computes api_sig given the GET and POST variables and the secret:
function compute_signature($get, $post, $secret)
{
  $vars = array_merge($get, $post);
  ksort($vars);
  $string = $secret;
  foreach($vars as $k => $v)
    if($k != 'api_sig')
      $string .= $k . $v;
  return md5($string);
}

So, a complete hAPI request will look something like:

http://api.voxel.net/version/1.0/?method=voxel.test.echo
    &foo=bar
    &key=9cb36fba2def790098c27abaf419a46f
    &timestamp=2008-10-09T13:10:43-0400
    &api_sig=910e0850e49608d7c7a2ab05cf68b15f

If you're interested in implementing your own hAPI client, we strongly recommend you take a look at the code in the existing hAPI toolkits to gain a better understanding of how hAPI's authentication works.

Authentication example: graphical app

A graphical UI may use hAPI on the backend, and still accept a human-compatible username and password for login. The app should present the user with a login prompt, and the user should enter their Voxel customer portal username/password. The app should then call:

https://username:password@api.voxel.net/version/1.0/
    ?method=voxel.hapi.authkeys.read

which will return the key/secret pair for the user. The app can cache the key/secret locally (e.g., in a configuration file) for future use. As long as the hAPI key/secret for the user are not reset, the cached versions will continue to work. However, for many applications it may instead be desireable to discard the key/secret at the end of the session and require the user to login again at next use. No app should ever store the Voxel customer portal username or password once it has been used to obtain the hAPI key/secret pair.

Important note: your app should never need to make more than one call to hapi.authkeys.read per session. If your app's sessions are short but frequent, you should cache hAPI keys/secrets locally for some time. See the hAPI command line client for an example of caching the key/secret temporarily.

Once the app has obtained the key/secret, it may call hAPI methods normally:

http://api.voxel.net/version/1.0/?method=voxel.test.echo
    &foo=bar
    &key=9cb36fba2def790098c27abaf419a46f
    &timestamp=2008-10-09T13:10:43-0400
    &api_sig=910e0850e49608d7c7a2ab05cf68b15f

Authentication example: backend script

A backend script, such as a cron job that interacts with hAPI, should never store a username/password or call hapi.authkeys.read. Instead, the developer should obtain a hAPI key/secret pair for the script with a one-time call to hapi.authkeys.read, and store the key/secret in the script. It is recommended that an account subuser be created for each application that will interact with hAPI automatically; these subusers should not be reused for human access. To ensure this, the subuser's password can be set to a suitably random string, and the subuser's permissions can be set to disallow access to hapi.authkeys.read.

Permissions

Every hAPI method is subject to access restrictions based on two properties of the method:

Categories
Methods in hAPI are grouped into categories based on the particular subsystems of Voxel's infrastructure with which they interact. Every method belongs to one or more categories.
Actions
Every method performs one or more actions on data associated with the categories to which it belongs. Actions in hAPI are one of read, update, create, or delete.

hAPI's permissions are based on a category-action matrix. Users are granted or denied access to perform actions in each category. To access a method, the caller must have permission to perform all of the method's actions on every category to which the method belongs.

A user's permissions matrix may be obtained with the hapi.permissions.list method, and managed using hapi.permissions.update. The categories to which a method belongs, and the actions it performs on those categories, may be obtained with the hapi.version call.

As an example, the voxcast.ondemand.content.purge_file method belongs to the ONDEMAND and ONDEMAND_CONTENT categories, and performs a "delete" action. To deny a hAPI auth key access to this method, you could use:

http://api.voxel.net/version/1.0/?method=voxel.hapi.permissions.update
    &[auth_params]
    &authkey=9cb36fba2def790098c27abaf419a46f
    &category=ONDEMAND_CONTENT
    &action=delete
    &permissions=deny

Permissions best practice: we strongly recommend denying permissions aggressively, particularly to hAPI keys you're using in automated scripts. Only allow them to perform the actions necessary for your script, and deny the rest. This ensures that if the key/secret are compromised, potential damage to your infrastructure is minimized.

Rate Limits

Voxel maintains a limit to the rate at which certain expensive hAPI methods may be called.

Some methods employ a limit to the Average call rate, which simply limits the number of requests that can be made in a given time period. A few methods may be rate-limited using some other rate calculation, such as a Moving Average. Methods may have multiple limits, for example limiting the number of requests per day as well as the number of requests per minute.

Method rate limits may be per customer, per IP address, or may include other method-specific statistics. For example, in voxel.ondemand.content.purge_file, the rate is measured based on the number of files purged, not on the number of requests made to hAPI.

Method rate limiting details, where applicable, can be found in the method specific documentation under "Rate Limits."

In cases where a hAPI user has exceeded the rate-limit, the user will be given a detailed error response message:

<?xml version="1.0" encoding="UTF-16"?>
<rsp stat="fail">
    <err code="10" msg="You have exceeded the maximum rate of calls allowable for this method">
        <limits_exceeded>
            <limit>
                <limit_type>Averaging Rate Limiter (Requests per period)</limit_type>
                <period_length>3600</period_length>
                <max_per_period>300</max_per_period>
                <rate>301</rate>
            </limit>
        </limits_exceeded>
    </err>
    <method>voxel.not.really.a.method</method>
    <parameters>
        <param name="api_sig">702c3abd046f34d2e48637503c37afe2</param>
        <param name="key">a5e4e3469fe25f77707b1c6767ccf475</param>
        <param name="timestamp">2010-07-06T05:10:01</param>
    </parameters>
</rsp>

Error codes

Any hAPI request that fails responds with an error code and a brief explanation. The method documentation indicates error codes specific to each method. Several error codes may be generated by any hAPI method. They are:

1: Invalid login or password
Authentication for the request failed because either key or api_sig was incorrect.
2: Unspecified API method
The method parameter doesn't exist or is not a valid hAPI method.
3: Request time too different from server time
The timestamp parameter is too different from the hAPI server's current time. Make sure to synchronize your clock, e.g., using NTP.
4: Backend error, please try again shortly
hAPI had trouble communicating with one of Voxel's other systems (e.g., a CDN node or a device management server). If this problem persists, please contact us.
5: Missing method-specific parameters
Some parameters required by the particular method being called were omitted.
6: Parameters are invalid
Some parameters passed to the method are in an incorrect format or are otherwise unusable.
7: A general error occurred
A generic error occurred in processing the hAPI call; an explanation should be provided.
8: Unknown API version
The requested API version endpoint is for an invalid or fully deprecated hAPI version.
9: Permission denied: user lacks access rights for this method
The calling hAPI authentication key has been denied access to perform certain necessary actions within one of the categories associated with the method being called.

Labs access

Certain hAPI methods or parameters require "Voxel Labs" access. Voxel Labs functionality is experimental and is not subject to Voxel's standard SLAs, so customers must opt-in to have access to these methods. Contact agile-support@internap.com to inquire about access to Voxel Labs functionality.

Examples

You might find all the documentation above a bit much to wrap your head around, but using hAPI is actually pretty easy. Here are a few real-world examples, built using the existing hAPI toolkits, that show some typical hAPI usage.

Purging content from VoxCAST

Suppose you have a really, really popular website and you're hosting your static content (videos, images, whatever) on the VoxCAST CDN. There's some kind of big news and you decide to replace the header image on your front page to reflect it. Only problem: the version in VoxCAST's cache isn't set to expire for a week, so all your visitors still see the old image. You can use hAPI to purge the image from the cache and force VoxCAST to get the new one. This example is written using the PHP toolkit.

<?php

// load the hAPI PHP client library from Voxel/Advomatic
require_once('hapi_client.php');

// instantiate hAPI client class using my hAPI auth key/secret
$hapi = new hapi_client('key', 'secret');

// a list of paths to purge from the VoxCAST cache for our CDN site
$paths = array('/images/image1.png',
               '/images/image2.png');

// ask hAPI to purge the paths from all VoxCAST nodes
$rsp = $hapi->call_api('voxel.voxcast.ondemand.content.purge_file',
                       array('device_id' => 1234,
                             'paths' => implode("\n", $paths)));

if(!$rsp)
  print 'error: ' . $hapi->get_error_message();
else
  print 'success!';

?>

Launching a VoxCLOUD VM

You're a video site and a customer has just uploaded a few hundred five minute videos. Your SLA says you've got to get them all online within ten minutes, but all your transcoding servers are maxed out. Your app can use hAPI to launch new VoxCLOUD VMs, which you'll be able to use within minutes. This example is written using the Ruby toolkit.

#!/usr/bin/env ruby

# load the hAPI ruby client library from Voxel
require 'hapi'

# specify options for a new VoxCLOUD VM
options = {
  :hostname => 'my.hostname.com',
  :facility => 'LGA6',
  :image_id => 16,  # chosen from voxel.images.list
  :processing_cores => 2,
  :disk_size => 20,
  :ssh_username => admin
}

# start provisioning of the VM
rsp = hapi.call_method({:method => 'voxel.voxcloud.create'}.merge(options))
device_id = rsp['device']['id']
status = rsp['device']['status']

# check the status of the VM every 3 seconds until it's provisioned
while status != 'SUCCEEDED' and status != 'FAILED' do
  sleep 3
  rsp = hapi.call_method({:method => 'voxel.voxcloud.status',
                          :device_id => device_id,
                          :verbosity => 'compact'});
  status = rsp['devices']['device']['status']
end

puts "Done: {#status}"

Creating an HTTP monitor

You've recently provisioned a new VoxSERVER and it's running a mission critical web server. In case something goes wrong and the server crashes or the hardware fails, you want Voxel's support staff to be notified whenever the server stops responding to HTTP requests. And for some strange reason, you want to do this on the command line! This example is written using the Perl toolkit.

#!/usr/bin/perl

# require the username and password as command line options; since
# this is an "interactive" application we'll let the user enter
# human-readable credentials, then go fetch the associated hAPI
# key/secret.  also get the device id and IP address to monitor.
if($#ARGV+1 != 4) {
  print "Usage: make_http_monitor <user> <pass> <device_id> <ip_address>\n";
  exit 1;
}
my ($user, $pass, $device_id, $ip_address) = @ARGV;

# load the hAPI perl client library from Voxel/DailyKos
use VoxelHAPI;
my $hapi = VoxelHAPI->new;

# fetch the hAPI key/secret associated with this user
if(!$hapi->fetch_key($user, $pass)) {
  print "Invalid username or password\n";
  exit 1;
}

# now, create an HTTP monitor on the requested device/interface
my $args = {
  'device_id'                => $device_id,
  'monitor_type'             => 'http',
  'ip_address'               => $ip_address,
  'staff_notification_level' => 'emergency'
};
$rsp = $hapi->call_api('voxel.devices.monitors.create', $args)

if(!$rsp) {
  print "Error: $hapi->{errcode} ($hapi->{errmsg})\n";
  exit 1;
} else {
  print "Created monitor with id: " . $rsp->{'monitor'}{'id'} . "\n";
}

hAPI Terms of Use

Creative Commons License
Voxel Hosting API Specification by Voxel dot Net, Inc. is licensed under a Creative Commons Attribution 3.0 United States License.

In keeping with Voxel's open philosophy, hAPI's specification is published under the Creative Commons Attribution 3.0 license. This means other companies in Voxel's space are free to build compatible APIs, including extensions to better suit their own customers.

Voxel's customers who make use of hAPI are subject to Voxel's standard policies and SLAs, along with any separate agreements specifically made between Voxel and the customer.