API Technical Details

We recommend that you use our Sailthru API libraries. These are debugged and in widespread use. But there may be situations where you need or want to write your own library, or otherwise directly access the API. This document explains the details.

Basic Rules

  • All API requests are either GET, POST, or DELETE calls to paths on the host api.sailthru.com. All requests must be sent using HTTPS.
  • The base URL for each request is https://api.sailthru.com/<name>, where <name> is the name of the call. For example, send requests are either a GET or POST to https://api.sailthru.com/send.
  • Requests are subject to rate limits as specified in the Rate Limiting section on this page.
  • All requests and responses must always be UTF-8 encoded. (API requests and responses in ISO-8859-1, not UTF-8, will result in an “Unable To Stream” error.)
  • The Sailthru API supports all versions of TLS, but does not support SSL, due to known security vulnerabilities with SSL.
  • In REST style, a GET and a POST have different behaviors and often take different parameters. The documentation for each action type will explain these differences. Consistently, a GET is idempotent and will only return information to you – it will never make any changes or cause any action to be taken; a POST is not idempotent.
  • Parameters are passed either as part of the GET query string or as the POST body. This means your POSTs must be of the MIME content-type application/x-www-form-urlencoded. All parameters must be properly URL-encoded.
  • One shortcoming of URL parameters is that they do not preserve type data. For all API calls, you are allowed to pass a single parameter called jsoncontaining an object representation of all of your parameters. This is the recommended approach supported by most of the client libraries.

Authentication

We use a simple shared-secret hash authentication mechanism. (For added security, you are strongly recommended to restrict the list of IPs that are allowed to access your API on your Settings page).

You have to start by knowing your API key and shared secret. You can get that information at the API Settings page.

Every request, whether GET or POST, must pass your API key as one of the parameters, as a parameter named api_key.

Every request must also generate a signature hash called sigaccording to the following rules:

  1. take the string values of every parameter, including api_key
  2. sort the values alphabetically, case-sensitively (i.e. ordered by Unicode code point)
  3. concatenate the sorted values, and prepend this string with your shared secret
  4. generate an MD5 hash of this string and use this as sig
  5. now generate your URL-encoded query string from your parameters plus sig

Example: Let’s suppose your API key is abcdef1234567890abcdef1234567890and your shared secret is 00001111222233334444555566667777. Let’s further suppose you’re doing an API call with the following parameters:

  • email=test@example.com
  • format=xml
  • vars[myvar]=TestValue
  • optout=0

Follow the steps:

  1. The string values of every parameter, including api_keyare: test@example.comxmlTestValue0, and abcdef1234567890abcdef1234567890.
  2. Sorting these values case-sensitive alphabetically, they are in this order: 0TestValueabcdef1234567890abcdef1234567890test@example.comxml
  3. Concatenate these sorted values and prepend the string with the shared secret and we get a signature string of000011112222333344445555666677770TestValueabcdef1234567890abcdef1234567890test@example.comxml
  4. Run md5() on that string and we get a signature hash of b0c1ba5e661d155a940da08ed240cfb9
  5. So our final query string looks like this: email=test@example.com&format=xml&vars[myvar]=TestValue&optout=0&api_key=abcdef1234567890abcdef1234567890&sig=b0c1ba5e661d155a940da08ed240cfb9

To see signature strings and hashing in action, try out some test API requests at the API test page. This page should give you all the information you need to debug.

Common Authentication Errors

It’s a very common mistake when coding your own client to URL-encode at the wrong time. Do not URL-encode any of your parameters before generating the signature string, but do URL-encode before you send the HTTPS request. Depending on what HTTPS library you’re using, it may take care of URL-encoding for you, but if you have to generate your own query string, you need to do that as the last step. This problem often goes unnoticed until you attempt to pass a character that should be encoded, like =or &.

So, for example, if you are trying to pass the value PB & J, you should be sorting and generating the md5() (doing steps 1-4 above) on the literal value PB & J. The ampersand should not be encoded. Then, after you’ve generated the sig hash, encode it to PB+%26+Jwhen building the query string.

Binary Parameter Values and Authentication

It is permitted to pass binary parameters, e.g. file upload names for the job API call. These binary parameters are **not** included in the signature hash.

Response Format

For most requests, we support three response formats: simple XML, JSON, and serialized PHP.

If you are coding in PHP, we recommend serialized PHP. Otherwise, we generally recommend JSON. For legacy reasons, though, XML is the default.

You can specify which response format by passing a formatparameter and using one of the following values: xmljson, or php.

The response will be a structure with fields that will vary depending on the call you are making. The responses are individually documented for each call.

If we encounter an error, we will return a structure with two fields: error, which will contain a numeric error code, and errormsg, which will return a message. The errormsgis usually technical in nature and most likely not something you will want to expose to an end user.

If you’re using JSON or PHP, the response structure will be pretty natural: a JSON object or a PHP associative array. If you’re using XML, we convert the JSON object to XML according to consistent rules:

  • key/value pairs become <key>value</key>
  • numeric arrays become <item>element0</item><item>element1</item><item>element2</item>… etc
  • there will be an opening and closing element

So, for instance, a JSON response that read:

{ foo: 'bar',
  baz: 2,
  baps: ['a', 'b', 'c']
}

becomes XML

<triggermail>
  <foo>bar</foo>
  <baz>2</baz>
  <baps>
    <item>a</item>
    <item>b</item>
    <item>c</item>
  </baps>
</triggermail>

Rate Limiting

Sailthru API requests are subject to rate limits in order to provide a consistent, high-performance experience for all users, ensuring that no single source can overwhelm system resources.

Limits

For customers who completed their initial Sailthru implementation before May 1, 2016, the following limits will be effective September 6, 2016. For all other customers, the following rate limits are effective beginning May 1, 2016.

Limits are per account, per endpoint, per method. If you are using integrations that leverage the Sailthru API with the same client ID, those requests will count against your limit.

  • POST and DELETE requests
    • /send – 200 requests/second
    • /user – 300 requests/second
    • /email (deprecated) – 300 requests/second
    • All others – 40 requests/second
  • GET requests
    • All – 300 requests/second

Limits are measured and enforced for minute-long windows. The above numbers of requests per second are maximum average requests-per-second for each window. For example, the actual GET limit is 300 * 60 sec., or 18000 requests per minute.

Limits can be raised on a case-by-case basis in order to support valid business practices. However, we have found that nearly all of our customers are able to operate very comfortably within these limits without any constraint on the speed of their systems.

Error Codes

If a request exceeds the limit, the request is rejected with

  • an HTTP response code of 429 (Too Many Requests) and
  • an API response body error code 43.
    For example:

    {
     "error":43,
     "errormsg":"Too many POST requests this minute to /send API",
     "rate":{
       "send":{
         "POST":{
           "limit": 12000,
           "remaining":0,
           "reset":1462060860
         }
       }
     }
    }

HTTP Response Headers

The headers on the responses to your requests contain current rate-limit information for the applicable method. Headers match the following commonly used convention:

  • X-Rate-Limit-Limit: rate limit ceiling for the given request
  • X-Rate-Limit-Remaining: the number of requests left for the window
  • X-Rate-Limit-Reset: the remaining window before the rate limit resets (in UTC epoch seconds)

Use with Sailthru Libraries

In each library, you can use the function getLastRateLimitInfo() to return for any method its limit, number of requests remaining, and limit reset time, all matching the data received in the headers. See the sample code on the library pages for details, including a method for throttling requests to account for limits.

Support

If you are having trouble, feel free to contact us. And if you happen to write a client in a language for which we do not yet have a library, please consider donating it to the Sailthru community!

Top