Implementing the Tableau 9.0 REST API — Part 1 — HTTP Requests

Editor’s Note: If you just want to get started using the REST API, the tableau_rest_api library in Python is a fully functioning library that implements all of the REST API functionality and more. The guide to tableau_rest_api is available here.

Tableau Server 9.0 introduces a much expanded REST API for server management. The REST API has existed since 8.2 but in 9.0 it now covers the vast majority of administrative actions you might want to do. Along with all the improvements, there is also fantastic documentation on every possible method in the Tableau Server Administration Guide.

In this series, I’m going to walk you through some of the concepts and what an actual implementation of a couple of libraries for interacting via the REST API. By working through these sample implementation libraries and scripts, you will see the general method for working with the REST API and the specific order of steps needed to accomplish some common requests. I’ll be presenting examples from a library written in both PHP and Python, to illustrate use cases for embedded portals and administration.


REST-ing Easy

Unlike the JavaScript API or the Data Extract API, which explicitly define the actual code in the languages they exist for, the REST API is simply a documented set of URIs, HTTP methods, and XML requests that can be implemented in any language that can handle XML and HTTP requests. In the end, you’ll implement in the language you develop for and prefer. While this provides flexibility, it also means that you need to do some coding to actually implement the functionality.

HTTP isn’t just the start of a url

REST APIs use a wider range of the HTTP standard than most end users have ever explored. Traditionally, databases are said to have the CRUD capabilities: Create, Read, Update, Delete. Tableau’s REST API, like many REST APIs, uses four HTTP verbs to represent these abilities: POST, GET, PUT, DELETE.

GET is the most familiar: it is the verb used when you access a URL in a web browser. If you’ve ever built forms in HTML, you may also have encountered POST, which allows you to send data back to the server. In the Tableau REST API, PUT send data like a POST, but causes an update to an existing resource. DELETE’s use is pretty obvious.

If we look at the REST API documentation, the name of the actions translate pretty closely to the HTTP verb we are going to use. Query… or Get… tend to be HTTP GET. Add… is accomplished via POST, while Update… uses PUT. Remove… or Delete… use DELETE. So to make all this work, we’ll need to implement a reasonable way to make the correct HTTP request when we want to perform an action.

How do we actually do all these exotic HTTP requests? Every language and platform has libraries for accomplishing them. At the basic level, the cURL library ( ) allows for the full range of HTTP requests to be expressed. In the example PHP library, I’ve used PHP cURL since it is available in almost all standard packages, but encapsulated all of the necessary rules into a single method. In Python, a slight modification to the standard urllib2 will handle most our needs.

Here is the main method that handles the HTTP request via cURL:

    protected function _make_request($page_number = 1){
        $url = $this->base_url;
		$url .= "?pageNumber=$page_number";
        $this->last_url_request = $url;
        $req = curl_init($url);
		if($req === false){
			throw new Exception("Error with setting URL in cURL library");
       // Set to a GET if no data to POST
       if($this->http_verb == 'delete'){
           curl_setopt($req, CURLOPT_POST,0);
           curl_setopt($req, CURLOPT_CUSTOMREQUEST,"DELETE");
       // Get when no XML_request
       elseif($this->xml_request == null){
            curl_setopt($req, CURLOPT_HTTPGET,true);
       elseif ($this->http_verb == 'post'){
            curl_setopt($req, CURLOPT_POST,1);
            curl_setopt($req, CURLOPT_POSTFIELDS, utf8_encode($this->xml_request) );
       elseif ($this->http_verb == 'put'){
           curl_setopt($req, CURLOPT_POST,0);
           curl_setopt($req, CURLOPT_CUSTOMREQUEST,"PUT");
           curl_setopt($req, CURLOPT_POSTFIELDS, utf8_encode($this->xml_request) );

       // All calls other than signin or signout have to pass the header with the token
       if($this->token != false){
            curl_setopt($req, CURLOPT_HTTPHEADER, array("X-tableau-auth: {$this->token}") );
       if($this->response_type == 'png'){
           curl_setopt($req, CURLOPT_BINARYTRANSFER,1);
		curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($req, CURLOPT_CONNECTTIMEOUT,60); // Increase connect time limit
		curl_setopt($req, CURLOPT_TIMEOUT,60); // Increase response time limit

		$response = curl_exec($req);
		if($response === false){
			$error = curl_error($req);
			throw new Exception("cURL HTTP request failed: $error");
		$curlinfo = curl_getinfo($req);
		$this->last_response_headers = $curlinfo;
		$this->raw_response = $response;
		// Check for HTTP Response codes
		$http_response = $curlinfo['http_code'];
		if ($http_response >= (int) 400){
		   $this->last_error = $this->raw_response;
		   throw new Exception("HTTP Response code $http_response is an error code. Retrieve full error for more details");

You’ll notice I’m calling some internal methods to help with generating the request; we’ll dive further into the structure of the library in another part of the series, but I trust it is mostly self-explanatory. Two important things:

  1. Every request other than sign-in or sign-out must have a Tableau specific HTTP header that includes your sign-in token:  X-tableau-auth: {$this->token}
  2. All XML must be posted UTF-8 encoded

Once we know we are able to express ourselves using all of the the necessary HTTP verbs, we can start talking to the Tableau Server. The first thing it will want to know is who we are and if we should be there, which I’ll cover in Part 2.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s