API Basics

While the easiest way to get stared using the API is to use one of the wrappers, we provide a complete set of documentation for you to build your own apps. Make sure you have your API Key before you get going!


Introduction

This section describes the overall architecture of the API.

REST

This API strongly attempts to conform to all the constraints and principles of a RESTful architecture. As such, the API is heavily focused on hypertext and transparency.

Due to this focus on hypertext, The only URL you should need to type (or paste) manually, is https://api.serps.com. All other URLs are available from another resource through links. Every resource contains links to related resources.

This documentation will provide frequent examples in several languages and/or tools (like cURL) but these examples can often be performed in a browser through various plugins and extensions

HTTP

This API also takes full advantage of the wide array of features provided by HTTP. Our philosophy is to not reinvent solutions to well understood problems, and reuse existing solutions wherever possible. This means that this API will use the full range of HTTP methods, status codes, and headers.


API Access

Access to the API is controlled in a few different ways. This section discusses those mechanisms and provides examples.

Token-based Authentication

All API access is restricted to those with an access token. API Administrators have the ability to create new tokens for specific uses, or simply publish one token for all uses. This token must be passed in an HTTP header X-Api-Token. Each application or tool you develop against the API should have its own token. The primary purpose of these tokens is to identify traffic sources, and in the future, this will be a source of information used to throttle.

Examples

Basic request

curl -vvv "https://api.serps.com/" -H"X-Api-Token: TOKEN" -u USERNAME:PASSWORD 
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "https://api.serps.com/");
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($process, CURLOPT_USERPWD, 'USERNAME:PASSWORD');
curl_setopt($request, CURLOPT_HTTPHEADER, array(
'X-Api-Token: TOKEN',
));
$response = curl_exec($request);
$data = json_decode($response);
var_dump($data);
                    
require "net/https"
                            require "json"

                            request = Net::HTTP::Get.new '/'
                            request.add_field 'X-Api-Token', 'TOKEN'
                            request.basic_auth 'USERNAME', 'PASSWORD'

                            connection = Net::HTTP.new 'api.serps.com'

                            response = connection.request(request)

                            puts JSON.parse(response.body)
$.ajax('https://api.serps.com/', {
    username: "USERNAME",
    password: "PASSWORD",
    headers: {
       'X-Api-Token': 'TOKEN'
    }
}).done(function(content) {
    alert(content);
})
import httplib
import json
import pprint

host = 'api.serps.com'
conn = httplib.HTTPSConnection(host)
auth = 'Basic ' + string.strip(base64.encodestring('USERNAME:PASSWORD'))
headers = {
            'Accept': '*/*',
            'Authorization': auth,
            'X-Api-Token': 'TOKEN',
          }
conn.request('GET', '', headers)
response = conn.getresponse()
data = json.loads(response.read())
pprint.pprint(data)

User-based Authentication

A service may also require authentication for individual users. This is done through the standard HTTP process of Basic Auth.

SSL

We highly recommend using HTTPS when communication with this API. This API and developer portal are served using an SSL extension called Server Name Indication. This extension solves the problem of serving multiple SSL applications from a single IP address. The vast majority of browsers support this extension but some programming languages or frameworks may not.

Applications developed against this API may need to add special handling to create a secure HTTPS connection. We strongly recommend you do not configure your application to ignore SSL certificate verifications if you run into problems with our certificates, as this can introduce a risk of a man-in-the-middle attack. Our client bindings all include a copy of our public key, and strictly check all certificates against that key, completely and securely mitigating that risk. If you choose not to use our supplied bindings (or there is not one yet available for your language/platform), we encourage you to download them anyways and look at how we have secured those connections.

Client Bindings

We offer downloadable client bindings that conform to all RESTful constraints and principles, and establish only the most secure connections possible to this API. While we don't yet support all the languages/frameworks we intend to, we are improving support as fast as possible, so check back frequently to see if we've gotten to your platform of choice!


Errors

Errors are communicated with standard HTTP status codes, but also with a response body for more detail.

HTTP Status Codes

All errors will use HTTP status codes to communicate the type of failure that has occurred. These are codes in the 4xx (client error) and 5xx (server error) ranges. 4xx means you've done something wrong, and 5xx means we've done something wrong. 5xx errors should be uncommon and temporary, but if they persist, we would appreciate it if you would inform us of the scenario.

4xx Codes

  • 400 Bad Request

    You made a request thatcannot be fulfilled due to bad syntax. This is common when schema constraints are being violated

  • 401 Unauthorized

    You have not authenticated with either a token or a username/password.

  • 403 Forbidden

    You are authenticated, but not authorized to access the requested resource

  • 404 Not Found

    You asked for something that is not there

  • 405 Method Not Allowed

    You attempted to perform a disallowed operation

  • 406 Not Acceptable

    You made a request that could not be fulfilled due to an inability of the server to produce an acceptable response. Check your Accept* headers.

  • 408 Request Timeout

    You need to do more, faster

  • 409 Conflict

    You have attempted to update a resource conditionally from a specific state, but the resource is no longer in that state.

  • 412 Precondition Failed

    You have made a conditional request that does not satisfy the requested conditions

  • 415 Unsupported Media Type

    You have attempted to create or update a resource by sending an unsupported format

  • 418 I'm a Teapot

    You have found an easter egg

5xx Codes

  • 500 Internal Server Error

    We had a problem that was fatal to the processing of your request

  • 502 Bad Gateway

    We ran out of processing power. We should probably scale.

  • 503 Service Unavailable

    We are down, either for maintenance or due to a crash.

  • 504 Gateway Timeout

    We are having performance issues. Please do not try again quickly.

Schema

Errors are usually returned with an error resource. This resource conforms to the following schema.

NameTypeDescription
codenumberThe code for the specific error that occurred
messagestringA message suitable for display to an end-user describing the error
suggestionstringA suggestion suitable for display to an end-user for how to correct the error
detailsstringThe technical details, not suitable for display to an end-user, about what went wrong

Error Codes

The following error codes are valid values for the 'code' field in an error.

  • 100 - An unknown error occurred

  • 101 - Access denied - Invalid or missing API Key

  • 102 - Access denied - Invalid or missing credentials

  • 103 - The provided search query is not acceptable

  • 104 - The provided sort query is not acceptable

  • 105 - The provided pagination query is not acceptable

  • 106 - The provided URL is not valid

  • 107 - We cannot process the where and sort criteria that you requested

  • 108 - The requested resource was not found

  • 109 - There was a problem with our back-end. We're working on it!

  • 110 - You requested a content type we couldn't provide


Types

All responses come back with a standard structure that contains some metadata about the returned data

Metadata

The standard metadata describes core information for all resources

NameTypeDescription
idstringThe unique identifier for the given resource
creatednumberThe timestamp for when the resource was created. Null if not available.
modifiednumberThe timestamp for when the resource was last modified. Null if not available.
linkslistA set of zero or more links
 linkobjectA link to another resource
  hrefstringThe url of the containing resource.
  relstringThe relation of the linked resource to the containing resource.
  typestringThe media type of the target resource. Null if variable or not relevent.
  titlestringA title appropriate to be displayed to an end-user to navigate to this resource. Present only for future features.

Lists also have metadata to communicate the number of items in the response and the total number of items in the collection

NameTypeDescription
countnumberThe number of items in the response
totalnumberThe total number of items in the collection
linkslistA set of zero or more links
 linkobjectA link to another resource
  hrefstringThe url of the containing resource.
  relstringThe relation of the linked resource to the containing resource.
  typestringThe media type of the target resource. Null if variable or not relevent.
  titlestringA title appropriate to be displayed to an end-user to navigate to this resource. Present only for future features.

Links

Links are core to great RESTful APIs, as they move the responsibility of URL construction from the client to the server. If a client is programmed to traverse these links dynamically, selecting an appropriate link from those available (based on media type and relation), then the server is free to change those URLs at any time without breaking older clients! This decoupling is what makes REST an incredibly powerful design philosophy.

A link in this API contains a location of another resource as well as some minimal metadata about that resource. Every link has exactly 4 fields:

NameTypeDescription
hrefstringThe url of the containing resource.
relstringThe relation of the linked resource to the containing resource.
typestringThe media type of the target resource. Null if variable or not relevent.
titlestringA title appropriate to be displayed to an end-user to navigate to this resource. Present only for future features.

Link relations are explained in detail here

References

Resources can contain references to other resources. A reference is a fully qualified resource completely embedded within another resource.

These resources contain metadata just like any other resource, so they'll have a @metadata field with an id and links. The link relation to the referenced resource's canonical URL will be via.

Numbers

Numbers can be either floating point values or integers.

String

Strings are standard JSON string values.

Booleans

Booleans are standard JSON boolean values, or the string 'true' or 'false' in XML.


Link Relations

We've done our best to use only standard link relations established and defined by IANA. Here we have outlined the relations we use.

General Relations

There are some common relations found throughout the API.

self - A link to the current resource. This relation is used for the top-level requested resource. Embedded resources (references) use...

via - A link that points to where the containing resource can be found. This relation is used for references included in other resources. The containing resource is included "via" the result of the url for this link.

index - A link that points to a collection.

Pagination Relations

Links are returned in the metadata of the list when pagination occurs. These links have the following relations:

first - A link to the first page of the collection

prev - A link to the previous page of the collection

next - A link to the next page of the collection

last - A link to the last page of the collection

Only relevent links are present. For example, a link with relation last is not present in the last page of a collection.

For this reason, the recommended way to request every page in a collection is to check for the presence of a next link, and if one is present, request its target. When there are no more pages, the link will not be present.


Queries

Several query string parameters are available to allow you to sort, paginate, and search lists. While all resources support pagination, searching and sorting may be unavailable or limited to certain fields for some collections.

Field Identifiers

When searching or sorting, you will need to reference an individual field within a resource belonging to the collection in question. This is accomplished by what we call a field identifier.

Field identifiers are basically a period-delimited list of the fields you would access if you were to traverse the resource hash from the root of a given resource to the desired field. When specifying fields identifiers for lists, the containing field within the list structure is ignored.

For example, the id of any given resource is available within the id field of the resource's @metadata hash: {"@metadata":{"id":"1"}}. Therefore, to reference a particular resource's id, you would use the field identifier @metadata.id.

Unfortunately, we currently do not support field identifiers that resolve to inlined references or items within arrays.

Sorting

Sorting is accomplished with the query string parameters sort.

The sort parameter is strctured in two parts. First, the field on which to sort the results. Second, the direction to sort the result (either asc or desc).

Example: sort=@metadata.created asc

Some client libraries may require you to urlencode the value of this parameter

Example: sort=%40metadata.created+asc

Only certain fields are sortable, depending on the resource in question.

A malformed sort parameter will cause a 400 Bad Request to be returned.

Examples

Sorting on created timestamp

curl -vvv "https://api.serps.com/types/:TYPE/items?sort=@metadata.created%20desc" -H"X-Api-Token: TOKEN" -u USERNAME:PASSWORD 
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "https://api.serps.com/types/:TYPE/items?sort=@metadata.created%20desc");
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($process, CURLOPT_USERPWD, 'USERNAME:PASSWORD');
curl_setopt($request, CURLOPT_HTTPHEADER, array(
    'X-Api-Token: TOKEN',
));
$response = curl_exec($request);
$data = json_decode($response);
var_dump($data);
require "net/https"
require "json"

request = Net::HTTP::Get.new '/types/:TYPE/items?sort=@metadata.created%20desc'
request.add_field 'X-Api-Token', 'TOKEN'
request.basic_auth 'USERNAME', 'PASSWORD'

connection = Net::HTTP.new 'api.serps.com'

response = connection.request(request)

puts JSON.parse(response.body)
$.ajax('https://api.serps.com/types/:TYPE/items?sort=@metadata.created%20desc', {
    username: "USERNAME",
    password: "PASSWORD",
    headers: {
       'X-Api-Token': 'TOKEN'
    }
}).done(function(content) {
    alert(content);
})
import httplib
import json
import pprint

host = 'api.serps.com'
conn = httplib.HTTPSConnection(host)
auth = 'Basic ' + string.strip(base64.encodestring('USERNAME:PASSWORD'))
headers = {
            'Accept': '*/*',
            'Authorization': auth,
            'X-Api-Token': 'TOKEN',
          }
conn.request('GET', 'types/:TYPE/items?sort=@metadata.created%20desc', headers)
response = conn.getresponse()
data = json.loads(response.read())
pprint.pprint(data)

Searching

Searching is accomplished through providing one or more where parameters

The where parameter is made up of three parts: The field you want to search, the operator you want to use to compare, and the value against which you want to compare.

Available operators are = or ==, !=, >, >=, <, <=.

Values except null, false, and true must be quoted in either single or double quotes. This includes numbers.

Examples: where=@metadata.id='5', where=@metadata.created>'1234567890'

Some client libraries may require you to urlencode the value of this parameter. Others may do so for you.

Example: where=%40metadata.id%3D%275%27

A malformed where parameter will cause a 400 Bad Request to be returned.

Examples

Searching on created timestamp

curl -vvv "https://api.serps.com/types/:TYPE/items?where=@metadata.created%3E%2212345678%22" -H"X-Api-Token: TOKEN" -u USERNAME:PASSWORD 
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "https://api.serps.com/types/:TYPE/items?where=@metadata.created%3E%2212345678%22");
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($process, CURLOPT_USERPWD, 'USERNAME:PASSWORD');
curl_setopt($request, CURLOPT_HTTPHEADER, array(
    'X-Api-Token: TOKEN',
));
$response = curl_exec($request);
$data = json_decode($response);
var_dump($data);
require "net/https"
require "json"

request = Net::HTTP::Get.new '/types/:TYPE/items?where=@metadata.created%3E%2212345678%22'
request.add_field 'X-Api-Token', 'TOKEN'
request.basic_auth 'USERNAME', 'PASSWORD'

connection = Net::HTTP.new 'api.serps.com'

response = connection.request(request)

puts JSON.parse(response.body)
$.ajax('https://api.serps.com/types/:TYPE/items?where=@metadata.created%3E%2212345678%22', {
    username: "USERNAME",
    password: "PASSWORD",
    headers: {
       'X-Api-Token': 'TOKEN'
    }
}).done(function(content) {
    alert(content);
})
import httplib
import json
import pprint

host = 'api.serps.com'
conn = httplib.HTTPSConnection(host)
auth = 'Basic ' + string.strip(base64.encodestring('USERNAME:PASSWORD'))
headers = {
            'Accept': '*/*',
            'Authorization': auth,
            'X-Api-Token': 'TOKEN',
          }
conn.request('GET', 'types/:TYPE/items?where=@metadata.created%3E%2212345678%22', headers)
response = conn.getresponse()
data = json.loads(response.read())
pprint.pprint(data)

Pagination

Pagination is done with the query string parameters start and limit.

start specifies the offset within the collection of the first resource. The default value is 0.

limit specifies the number of resources to return. The default (and maximum) is 100.

Both parameters can be used independantly.

Example: start=30&limit=10

Examples

Get 5 items, offset by 10

curl -vvv "https://api.serps.com/types/:TYPE/items?limit=5&start=10" -H"X-Api-Token: TOKEN" -u USERNAME:PASSWORD 
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "https://api.serps.com/types/:TYPE/items?limit=5&start=10");
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($process, CURLOPT_USERPWD, 'USERNAME:PASSWORD');
curl_setopt($request, CURLOPT_HTTPHEADER, array(
    'X-Api-Token: TOKEN',
));
$response = curl_exec($request);
$data = json_decode($response);
var_dump($data);
require "net/https"
require "json"

request = Net::HTTP::Get.new '/types/:TYPE/items?limit=5&start=10'
request.add_field 'X-Api-Token', 'TOKEN'
request.basic_auth 'USERNAME', 'PASSWORD'

connection = Net::HTTP.new 'api.serps.com'

response = connection.request(request)

puts JSON.parse(response.body)
$.ajax('https://api.serps.com/types/:TYPE/items?limit=5&start=10', {
    username: "USERNAME",
    password: "PASSWORD",
    headers: {
       'X-Api-Token': 'TOKEN'
    }
}).done(function(content) {
    alert(content);
})
import httplib
import json
import pprint

host = 'api.serps.com'
conn = httplib.HTTPSConnection(host)
auth = 'Basic ' + string.strip(base64.encodestring('USERNAME:PASSWORD'))
headers = {
            'Accept': '*/*',
            'Authorization': auth,
            'X-Api-Token': 'TOKEN',
          }
conn.request('GET', 'types/:TYPE/items?limit=5&start=10', headers)
response = conn.getresponse()
data = json.loads(response.read())
pprint.pprint(data)