Zoom.it

API Formats - REST vs. non-REST

Some background on REST

REST is an architectural style for web services like ours. It’s the basis of the way the web works today, so it was designed with things like scalability and interoperability in mind. This style has a few main guidelines:

  1. URLs should represent resources (nouns) rather than actions (verbs).
  2. APIs should re-use HTTP, the language of the web, as much as possible. This includes HTTP status codes, request headers and response headers.
  3. Resources should be self-describing and cross-linking. Hyperlinks should power the discovery of other resources.

One of the main advantages to having a RESTful API is related to #2: by using HTTP as the interface, you have to write less of your own code; instead, you get to leverage widespread existing HTTP support.

For example, every HTTP stack understands that a 4xx status code signifies a client error whereas a 5xx status code signifies a server error. In .NET, these cases automatically result in an exception, without you having to program that logic manually. Similar intelligent behavior exists in many other platforms.

Unfortunately, today's mainstream client-side web platforms have some notable limitations in their support of HTTP, generally due to security reasons.

For example, XMLHttpRequest (the “x” in Ajax) doesn’t work across domains in older browsers. Some versions of Silverlight and Flash change all error codes to 404 and also lack the ability to specify request headers. And none of these platforms allow you to prevent automatic redirects.

The RESTfulness of our API

For the primary reason of interoperability, our API is RESTful by default:

  1. Our URLs represent the content directly, e.g. /v1/content/11mb rather than something like /v1/GetContent.ashx?id=11mb.
  2. We use standard HTTP status codes like 301, 400 and 503, and where applicable, we use standard HTTP headers like Accept, Location and Retry-After.
  3. Our content info is as self-describing as it gets, and it includes hyperlinks both into and out of the API.

If you're on a platform that has a full HTTP stack, you get the benefit of having this RESTful behavior. Server-side frameworks like ASP.NET, PHP and Ruby on Rails are examples of such platforms.

To support the constrained client-side platforms – JavaScript, Silverlight and Flash – we also have a non-RESTful variant to our API that overcomes those platforms' limitations.

In this variant, we officially send back only 200 OK status codes, and we use neither request nor response headers. Instead, we expand the response body to include what these things would have been.

We believe that offering both variants like this gives you the best of both worlds: you get the interoperability of REST when you have access to a full HTTP stack, without losing any of the API's functionality when you don't.

Details on the RESTful variant

Since our API is RESTful by default, you don’t need to do anything special to get the RESTful behavior.

If you request content info with a malformed source URL, or catch the service when it’s overloaded, expect a 400 or 503 response, respectively.

If your request for content info is successful, the response body will be a content object in the format of your choice. If your request leads to an error, the response body will be a plaintext error message, with a Content-Type: text/plain header.

To specify the format for content info, send a standard Accept request header in your request. Set this to application/xml for XML or application/json for JSON. If you don’t specify an Accept header, we default to JSON.

Details on the non-RESTful variant

To trigger the non-RESTful variant of the API, simply specify your response format in the query string. This means a format=xml or format=json parameter for XML or JSON, respectively, which will override the Accept header if there is one.

(For JSONP, an extra callback=<function> parameter is needed, where <function> is the name of the JavaScript function to call with the response object. In this case, the format=json parameter is not explicitly required.)

In this variant, the official response is always 200 OK, with no special response headers. Whatever the response body would have been in the RESTful API gets “wrapped” in a response object. This object describes the real status code and any headers that would have been sent.

This means that you always get back a response object in your specified format, which you can parse to determine if the response was successful or not. The easiest way to do this is check if the content field is present. If not, the error field will be.

Side-by-side example - Error Response

The following is an example RESTful request/response. The request contains a malformed URL, so the response has a 400 status code with a plaintext error message:

GET /v1/content/?url=example.com HTTP/1.1
Host: api.zoom.it
Accept: application/xml
HTTP/1.1 400 Bad Request
Content-Type: text/plain

Please give us the full URL, including the "http://" or "https://".

Here's the same malformed URL with a non-RESTful request/response. The request specifies XML format via the query string, and the response has a 200 OK status code, with the real 400 status code and error message both in the response XML:

GET /v1/content/?format=xml&url=example.com HTTP/1.1
Host: api.zoom.it
HTTP/1.1 200 OK
Content-Type: application/xml

<response>
    <status>400</status>
    <statusText>Bad Request</status>
    <error>Please give us the full URL, including the "http://" or "https://".</error>
</response>

Side-by-side example - Redirect Response

The following is an example RESTful request/response. The request asks for content info by URL, so the successful response is a redirect.

GET /v1/content/?url=http://example.com/ HTTP/1.1
Host: api.zoom.it
Accept: application/json
HTTP/1.1 301 Moved Permanently
Location: http://api.zoom.it/v1/content/11mb
Content-Type: application/json

{
    "id": "11mb",
    "url": "http://example.com/", 
    "ready": true, 
    "failed": false, 
    "progress": 1, 
    "shareUrl": "http://zoom.it/11mb", 
    "embedHtml": "<script src=\"http://zoom.it/11mb.js?width=auto&height=400px\"></script>", 
    "dzi": {
        "url": "http://cache.zoom.it/content/11mb.dzi", 
        "width": 1024, 
        "height": 640, 
        "tileSize": 254, 
        "tileOverlap": 1, 
        "tileFormat": "png"
    }
}

The same redirect as a non-RESTful request/response:

GET /v1/content/?format=json&url=http://example.com/ HTTP/1.1
Host: api.zoom.it
HTTP/1.1 200 OK
Content-Type: application/json

{
    "status": 301, 
    "statusText": "Moved Permanently", 
    "redirectLocation": "http://api.zoom.it/v1/content/11mb", 
    "content": {
        "id": "11mb", 
        "url": "http://example.com/", 
        "ready": true, 
        "failed": false, 
        "progress": 1, 
        "shareUrl": "http://zoom.it/11mb", 
        "embedHtml": "<script src=\"http://zoom.it/11mb.js?width=auto&height=400px\"></script>", 
        "dzi": {
            "url": "http://cache.zoom.it/content/11mb.dzi", 
            "width": 1024, 
            "height": 640, 
            "tileSize": 254, 
            "tileOverlap": 1, 
            "tileFormat": "png"
        }
    }
}


Terms of Use Privacy Statement © Microsoft Corp.