API Technical Details

You may need or want to write your own library or build your own call from scratch. While we recommend using the existing API libraries, this page gives the details you need to build your own.

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/<endpointName>. For example, send requests are either a GET or POST to https://api.sailthru.com/send.
  • Requests are subject to the rate limits specified in the Rate Limiting section below.
  • All requests and responses must be UTF-8 encoded. (API requests in ISO-8859-1, for example, will result in an "Unable To Stream" error.)
  • The API supports TLS 1.2 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 json containing an object representation of all of your parameters. This is the recommended approach supported by most of the client libraries.
  • While you'll send request data JSON, you can receive data in JSON or XML, though some functions of the Job API will also allow you to send or receive a CSV or a file of JSON objects. For each call, the response will be a structure with fields that will vary depending on the endpoint and call you are making. The responses are individually documented for each on its API endpoint doc page.
  • If you receive a 406 response from a browser or the API, contact Support with the 406 error code and a copy of the request you made.

Authentication & Requests

To produce API requests, you will need your company's unique API key and secret, which you can obtain from the API & Postbacks Settings page in the platform. Click the lock icon to display these credentials. We use a shared-secret hash authentication mechanism, where for each request, you will create and pass a unique signature (sig) value. It is calculated using your API key, secret, and the unique parameter values you're sending for the current request. If you use an existing Client Library, this value is calculated and included automatically with each request. For added security, however, you are strongly recommended to restrict the list of IPs allowed to access your API on your Settings page.

Request URL Format

Replace <endpoint> with any endpoint name listed in the API Introduction, for example user, send, list, or content. GET
curl 'https://api.sailthru.com/<endpoint>?api_key=<key>&sig=<sig>&format=<json_or_xml>&json=<url_escaped_data>'
curl -X POST https://api.sailthru.com/<endpoint> -d 'api_key=<key>&sig=<sig>&format=<json_or_xml>&json=<data>'
curl -X DELETE 'https://api.sailthru.com/<endpoint>?api_key=<key>&sig=<sig>&format=<json_or_xml>&json=<url_escaped_data>'
Request URL Parameters
  • api_key - The unique key for your company's account, found on the API & Postbacks Settings page in the platform.
  • format - Format of the response: json or xml. (We recommend JSON.)
  • json - The JSON object containing your request parameters.
  • sig - The signature for the request, which is a keyed HMAC (Hash Message Authentication Code) specific to each call. It is the MD5 hash of a string of the following concatenated values in the following order: your secret, key, format, and JSON object.

Example Request

  1. Let's assume the following scenario:
    • You want to make a request to the user endpoint to create a profile for new user neil@example.com, then retrieve data stored for that user.
    • The format of data you want returned is json.
    • Your api_key is 123key.
    • Your secret is abcsecret.
  2. Build the request JSON. According to the user API documentation, your request JSON will be {"id":"neil@example.com"} whether you're creating that user with a POST request or requesting that user's data with a GET request. Before you include this JSON in your request, URL-escape it so that it contains only URL-safe values: %7B%22id%22:%22neil@example.com%22%7D.
  3. Calculate the sig: an MD5 hash of the following concatenated values as a single string: the secret, followed by all parameter values sorted alphabetically. So in our case, you'd build the following string:
    You could then create the MD5 hash using the following command:
    md5 -s 'abcsecret123keyjson{"id":"neil@example.com"}'
    The resulting MD5 hash would be:

    Note: The sig value should be generated with lower case letters.

  4. Make your request. To create the user, make this POST request:
    curl -X POST https://api.sailthru.com/user -d 'api_key=123key&sig=fa5c79189b708199f3cf69f1cf8f7928&format=json&json=%7B%22id%22:%22neil@example.com%22%7D'
    To retrieve the data stored for the user (i.e. the user profile), make this GET request:
    curl 'https://api.sailthru.com/user?api_key=123key&sig=fa5c79189b708199f3cf69f1cf8f7928&format=json&json=%7B%22id%22:%22neil@example.com%22%7D'
To see signature strings and hashing in action, try out some test API requests at the API test page in the platform. There, you will be able to select an endpoint and method, then input your JSON. Your API key is included automatically and the signature is automatically calculated, included in the call, and displayed. This page should give you all the information you need to debug. Note: This offers live access to retrieve and modify data in your account, so proceed with care.

Common Authentication Errors

It is a 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 using those parameters, but do URL-encode those parameter values before sending them in your 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 (as described 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+J when building the query string.

Binary Parameter Values and Authentication

It is permitted to pass binary parameters, for example, 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 two response formats: JSON and XML. Typically, we recommend JSON, unless you have specific requirements for XML. You will specify which response format by passing the format parameter and using one of the following values: json or xml. 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 errormsg is usually technical in nature and most likely not something you will want to expose to an end user. If you're using JSON, the response structure will be pretty natural: a JSON object. If you're using XML, we convert the JSON object to XML according to consistent rules:
  • key/value pairs become value
  • numeric arrays become element0element1element2... etc
  • there will be an opening and closing element

So, for instance, a JSON response that reads:

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

would be returned in the following format as XML:


Rate Limiting

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 are per account, per endpoint, per method. If you are using integrations that leverage the API with the same client ID, those requests will count against your limit.
  • POST 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
  • DELETE requests
    • All - 40 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:

 "errormsg":"Too many POST requests this minute to /send API",
             "limit": 12000,

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 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.


If you are having trouble, feel free to contact us by selecting the blue support button at the top or bottom of the page. If you happen to write a client in a language for which we do not yet have a library, please consider contributing it to the community!

Contact us