Protocol - danopia/deviantart-difi GitHub Wiki

General

This article tries to cover DiFi usage, both in the DeviantArt website and in other external uses (such as Userscripts).

Usage

The DiFi is an extensive RPC/AJAX centric service that enables pages to communicate with the DeviantArt servers.

This includes the following:

  • Getting data (such as Deviation and User information)
  • Performing actions (such as adding a Deviation to Favourites)
  • Populating the pages (such as the Deviation page on browsing different pages)
  • Changing settings (User info, navigation, blocked users, etc)

This API was introduced in deviantART v5 and is used up until DeviantArt v9.

Rate Limitations

DeviantArt does not operate a fixed request limit for a client, instead, they have adaptive rate limiting, that allows short bursts of activity but preventing clients from making requests at a rate that will affect other users or the website's stability.

If you receive 403 errors that contain an HTML response rather than JSON, please ensure your client is sending a User Agent header and using HTTP compression for the request, as deviantART rejects any requests not meeting this requirement.

You may also receive rate errors if deviantART backend systems are overloaded and cannot accept any more requests. When this happens clients should slow down their requests for a period of time.

Requests

  • The url consists of the request parameters and a transmission parameter.
    • Each call (c[]) names a class object to perform the call on, a method to call on the object, and a list of params.
    • The transmission parameter (t) controls the format of the response.
  • HTTP request is either GET or POST verbs, based on the type of action requested.
  • A single DiFi request can carry multiple method calls, denoted as calls in both requests and responses.

Authentication

DiFi calls can either require authentication or don't, based on the action that is being performed.

The rule of thumb is that if you can access the call without being logged-in you probably will not need authorization cookies.

Most GET requests that only retrieve data and don't perform any actions will generally not require authentication. On the other hand, any type of requests that require authorization (e.g. POST requests) will require authentication.

Auth-dependent actions use the userinfo (ui) parameter.

Naming Conventions

DiFi appears to use mixed conventions that are rarely consistent even within a class and/or per method-basis.

Furthermore, all class and method names are case-sensitive.

  • Class names are always StudlyCaps or PascalCase.
  • Here's a list of known used method name conventions:
    • Class::DoSomething – i.e. UpperCamelCase
    • Class::doSomethingElse – i.e. lowerCamelCase
    • Class::do_another_thing – i.e. underscore_delimited_lowercase
    • Class::makesomechanges – i.e. lowercase entirely, without any delimiters

Format

Each request is broken down into the following sections:

  • Base URL – The base URL that the API accepts requests on
  • Parameters – the HTTP parameters that are passed to the API
    • c[] – an array containing the request calls
    • t – the response format
    • ui – the userinfo cookie token (optional)

Example

http://www.deviantart.com/global/difi/?c[]="Class","method",["arg1","arg2","etc"]&t=json

Base URL

DiFi calls are sent to either: /global/difi/ or /global/difi.php

It is also possible to access DiFi via http://www.deviantart.com/global/difi.php with the same results.

NOTE: The jsonp format only works on the https://backend.deviantart.com/global/difi/ url.

Method Calls c[]

A list of DiFi call requests are sent in the c[] parameter, thus more than one call may be performed per HTTP request.

The request parameters are:

  • Class
    The DiFi classes are listed on the sidebar of this Wiki.
  • Method
    See each Class for a list of available methods.
  • Arguments
    See a class method for its arguments.
    Argument Types:
    • boolean: false or true
    • string: Hello world
    • integer: 0, 1 or 2
    • object: i.e. named pairs {"name": "value"}
    • array: can contain any kind of arguments
      (Often integers are sent as a string due to this.)

Basic single request format

The c[] parameter contains the request parameters and is delimited with , like this:

?c[]="Class","method",["argument1","argument2","argument3","etc"]

This is the simple and easy format to perform a DiFi request.

Multiple requests to same method

To make a call with more than one set of arguments to the same class and method, you can append the arguments array like this:

?c[]="Class","method",["argument1","argument2"],["argument3","argument4"]

This means that you can put multiple calls that share the same class and method into a single URL request, using the first notation and repeating the [] part per needed.

Multiple requests to different methods

You can also put several calls that don't share the same class or method in a single request, by adding a second c[] parameter using any class/method/notation you like:

?c[]="Class","method",["argument1","argument2"]
&c[]="AnotherClass","other_method",["argument1","argument2"]
&c[]=...

To that, you can add as many c[]=… as you need. They must be separated by a & delimiter.

Legacy format

DiFi also supports an older format, that has a more simple format, but doesn't work with multiple calls.

?c[]=Class;method;argument1,argument2,argument3,etc

Example:

http://www.deviantart.com/global/difi.php?c[]=Class;method;argument1,argument2,argument3,etc

Transmission Parameter t

The second parameter in the url, t, is responsible for the response encoding.

  • json — gives you the data output in the standard JSON-formatted string
  • xml — gives you a XML document that can be parsed by XML readers
  • php — gives you a multi-dimensional PHP array that can be deserialized in PHP scripts using unserialize()
  • page — gives you an HTML page with an JSON object encoded in it REMOVED
  • default — gives you a human-readable table which is only suitable for testing REMOVED

The jsonp would output json wrapped in a call to DiFi._callbackSI() javascript.
(This historically used to be called jssi.)

Callbacks with jsonp

DiFi can also transmit data using the obscure JSON-P (i.e. JSON with padding) format, and it's mainly used in combination with the DiFi object's static method.

The content returned with this format is Javascript code that's meant to be executed.
(As a security measure, you can only access "public" data using this format).

To use the jsonp method, you need to further supply the following query parameters:

  • callback: e.g. DiFi._callbackSI
  • extraarg: e.g. lookup

Response:

DiFi._callbackSI( {"DiFi":{ . . . }, "<request_url_queries>" );

This format solely exists for historical purposes, and it is not recommended to use it in order to retrieve data in modern applications.

Userinfo Parameter ui

The third parameter, dubbed ui, is often required for POST calls.

Sometimes, in a call, this argument should be set in order for your call to work correctly. This is probably used to help prevent XSRF (Cross-Site Request Forgery) as this has been a problem on deviantART in the past.

The value of the ui parameter equals the value of userinfo cookie.

Optional Additional Parameters

DiFi calls can include two additional parameters that appear to be intended for debugging purposes.
You can safely omit these (in fact we recommend you to do so).

ace i.e. All callbacks are empty

This value is seen to be set to true when there are no callbacks set.
TODO: This might signal the DiFi backend not to set cookies? Purpose unknown.

iid i.e. LAVA clientside instanceid / last log id

This value is equal to the DaPx.last_logid, which defined in any HTML page.
Seems to be debugging-related.

mp i.e. Prefix for returned Roses metadata

This value seems to be an incremental number, that doesn't seem to control anything besides client-side caching.
With each new DiFi request, the mp value is incremented.

ap i.e. AnalogPayload

This value seems to be an additional static json-encoded parameter that is occasionally used.
The native DiFi object uses the setAnalogPayload() method to set a new payload.

This seems to be a legacy property, and it's unknown whether DeviantArt still uses this parameter. Example: {"content_source":"MC_deviation_stack"} which signals the MessageCenter class that the trashes_message method was called from a "deviation stack". Purpose is unknown.

A list of all whitelisted keys is mentioned to be available in the following property:

Lib_DiFi_Common_Http_Request::$analog_keys_whitelist

HTTP Methods

DiFi requests can be made with either GET or POST verbs.

GET requests

GET requests are usually used to gather public or personal information. Some actions will require authentication, which usually depends on sending login cookies (e.g. auth and userinfo) alongside your request. Userscripts are usually using the Browser's cookie container automatically, while website javascript or executables need to store the cookies when logged in, so GET-requests work correctly.

POST requests

POST requests are used to either perform actions or acquire private information. These requests require not only the POST HTTP method and the browser's cookies as identification, but are often also checked for their source of call (i.e. the "Referer" header). This means that most POST requests will fail if you don't start them from a DeviantArt page.

POST requests are often the DiFi object's method of choice to either receive or transmit data, even when a GET request could be used instead. This may be due to the length limitations of GET requests.

Response

The DiFi response can be encoded in a variety of formats, such as json, xml and php.

DeviantArt by default uses the JSON format in the DiFi object, and many Userscripts also follow the same principle.

Unless you need XML format in your project, or are more comfortable dealing with XML, it's probably a better idea to use JSON in your projects as well.

JSON

A barebone DiFi response of type json will look like this:

SUCCESS example

{
    "DiFi": {
        "status": "SUCCESS",
        "response": {
            "calls": [ ..., ..., ..., ... ]
        }
    }
}

FAIL example

{
    "DiFi": {
        "status": "FAIL",
        "response": {
            "error": "DiFi Request Failed",
            "details": "c[] argument not specified"
        }
    }
}

The response is encapsulated in the DiFi property, which is of type object. This can be used to parse the response via custom DiFi handlers, and/or identify if your DiFi call was recognized and executed by DeviantArt servers.

The DiFi object will contain exactly two properties, a status and a response property.

  • The status property will be either SUCCESS or FAIL.
  • The response property will contain a list of each call.
Name Value Explanation
status SUCCESS The c[] calls were correctly identified and executed.
status FAIL The request was failed due to reason specified in the error and details properties.

Most calls to DiFi should have a status value of SUCCESS, even if any of the subsequent calls themselves are failed. Therefore, if you get a status value of FAIL, something is wrong.

Format of the calls

If everything went successful, you will have a calls property which will be an array containing an object for each of your calls.

Here's an example of calls property:

[
    {
        "request": {
            "class": "MyClass",
            "method": "placebo_call",
            "args": [ ..., ..., ... ]
        },
        "response": {
            "status": "...",
            "content": ...
        }
    }
]

As you see, each call will contain exactly two properties:

  • request – which will contain your request details that you have sent. (This could be handy for debugging purposes.)
  • response – which will contain the result of DeviantArt servers processing your call. This property will contain:
    • status – the status of executing your call
    • content – the return content of your processed call. Could be either null, or an object.
Name Value Explanation
status SUCCESS The operation was executed successfully.
status NOEXEC_HALT Something prevented the operation from being executed.
status FAIL The request was executed, but it failed.

Notes

  • The SUCCESS and FAIL denote that the call was executed, irregardless of being successful or not. However, NOEXEC_HALT denotes that the call was not even attempted to be executed at all.
  • Any error reasons will be specified in the error and details properties of the content property.
  • The status for each call differs from the root status property, and it denotes whether the execution of this particular call was successful. (DiFi_result.response.calls[0].response.status)
  • On some calls, the content response will always return NULL. Check for the status field to see if the operation was successful.

NOEXEC_HALT Example

    "status": "NOEXEC_HALT",
    "content": {
        "error": "DiFi method is non-const in const execution state",
        "details": "User::usernameAvailable"
    }

Possible NOEXEC_HALT error messages

  • DiFi method is non-const in const execution state
    • When using incorrect HTTP verb for request
  • DiFi method not registered
    • When calling non-existent methods on valid classes (e.g. removed calls)
  • DiFi failed to load class
    • When calling a method that has failed to load due to some reason (e.g. invalid classname)
  • DiFi argument count mismatch
    • When calling a method with less or more arguments than required
  • DiFi argument validation error
    • When the passed arguments to a method are not in a valid form
  • DiFi Security Access Error (ERR_DIFI_ACCESS_DENIED)
    • When making a request without being logged-in
    • When making a request that you don't have the privilege to do so
    • Details: access was not granted by any rule.

Possible FAIL error messages

  • DiFi method did not return DiFi response structure
    • When a DiFi method did not return expected response

Scripting

DiFi object

The DiFi object is responsible to handle DiFi calls in the DeviantArt website, and can be used by either native calls or user-script calls to perform actions on the site.

More information is here.

Userscripts

More about sending Requests is available in JS Access.

⚠️ **GitHub.com Fallback** ⚠️