Skip to main content
All CollectionsDeveloper & API
Getting Started with the OData API
Getting Started with the OData API

For developers who want to learn how to write code and queries using the OData Restful API.

Andrew Ardron avatar
Written by Andrew Ardron
Updated over 3 months ago

This guide is a getting started document, starting with an introduction to OData and goes on to explain how to get a token and gives examples of the many ways to query OData. If you already know that stuff, and are just looking for details of the endpoints and the OData data models, then please refer to the API documentation.

What is OData?

The Technical description:

A REST-based protocol for querying and updating data built on standard technologies such as HTTP, Atom/XML and JSON.

OData is standardised by OASIS and is ISO/IEC approved.

The Useful Description:

OData is a way to query databases over the web in a uniform way, requesting the data in a variety of formats (mainly JSON and XML).

Because the OData specification is standardised and ISO/IEC approved, we can theoretically use any OData feed, and it'll respond in the same way to the defined OData functions.

Authenticating

Some OData services are open (available to anyone without authentication). You'll need a Personal Access Token (PAT) to access our OData services. Visit this article for information on generating a PAT.

Rate Limit

API access is limited to 1200 requests every 10 minutes using a Sliding Window algorithm. Exceeding this will temporarily return a 429 response code before allowing more requests through. We don't have a request queue which means if a request gets a 429 response code, you must retry the request as the API will not store it. As a result of using this algorithm, the rate limit is implemented fairly whilst maintaining the required level of infrastructure protection. The rate limit only applies to services which use a CRM generated PAT token but the limit is monitored for your overall Workspace and not per PAT. As a result, if you have multiple PAT tokens allowing connection then they all get counted towards the same limit.

Sliding Window Algorithm Explained

To help explain the Sliding Window algorithm logic please see refer to the image and description below:

Each block represents 1 minute in time whilst the number inside the block shows how many API requests are made per minute. The first window has used 15 requests with the second window using 20 requests. The Sliding Window algorithm keeps a log of the timestamps of each incoming request. The rate limit is then determined based on the number of requests in the current sliding window — a continuously moving time frame. If the requests reach the limit, it will return a 429 response code until the window slides and the limit is no longer reached. In practice, this works as follows:

  1. A request is made to the Prospect API

  2. The system checks a list of recent timestamps of all other API requests made by your Workspace

  3. The system will remove any requests which are older than 10 minutes from the count list

  4. The remaining requests (within the last 10 minutes) are counted

  5. If the remaining count is >1200, the request will be denied with a 429 response code else the request will be successful

We don't have a queue implemented, therefore we highly recommend you build processes to retry requests if a 429 is returned.

The current ProspectSoft implementation of the rate limit is with the window covering 10 minutes worth of requests and the number of allowed API requests being 1200.

Please note: The rate limit will be live from the 31st October, so please take this into consideration when building processes with the API. Contact Support here, if you have any further questions.

Constructing a Query

Queries are constructed from 3 parts:

  1. The base URL

  2. The entity you're accessing

  3. The query parameters

A complete query will look something like this:

https://crm-odata-v1.prospect365.com/Contacts
    ?$select=Title,Forename,Surname,Email,StatusFlag
    &$filter=contains(email,'@prospectsoft.com') and StatusFlag eq 'A'
    &$expand=Leads(
        $select=Description,Created,StatusFlag;
        $filter=StatusFlag eq 'A' and Status/DeadFlag eq 0;
        $expand=Status(
            $select=Description
        )
    )

If you want to try that query later on, when you're connected and authenticated, you'll need to copy and paste it without the white space. Here's the same code as above, but this time without the formatting:

https://crm-odata-v1.prospect365.com/Contacts?$select=Title,Forename,Surname,Email,StatusFlag&$filter=contains(email,'@prospectsoft.com') and StatusFlag eq 'A'&$expand=Leads($select=Description,Created,StatusFlag;$filter=StatusFlag eq 'A' and Status/DeadFlag eq 0;$expand=Status($select=Description))

Base URL

The base URL explains where you want to get your data from. Some examples are:

The services.odata example URLs are unsecured, so you can use these to test queries without needing to worry about authentication.

The OData service is secured with bearer authentication, which means you need to add a bearer authorisation header to your request, using a valid access token. If you don't have a token, you can create one by following the steps in this article.

Important Note on Security

These access tokens allow reading and writing to the database as you, with your user ID. It allows the same access to the data as you have when you log in. You should treat this token as if it's your password. Don't hand out your token or share it with anyone. If a colleague, another developer or someone else needs a token, they should generate one themselves - with their own identity and permissions.

If you ever need to disable a token, you can revoke it from here.

Entity

The entity is the type of CRM record you want to access. These are defined in our database and generally mirror our tables.

At the time of writing this, we don't have a full list of entities available on our service, but for the most part, they are the plural of the table name.

This entity name is appended after the base URL like so:

Please note: Running these queries without filtering will retrieve all data for those entities. If you try to retrieve 50,000 Contacts, it'll take a long time to complete.

Wherever possible, you should aim to only retrieve the data you need. Smaller chunks of data, over a larger number of queries, is almost always better.

To retrieve a specific record by ID, you can append it to the entity name in parentheses:

Parameters

Parameters are made up of the various OData functions for selecting and filtering entities.

The main functions are:

  • $Select

  • $Expand

  • $Filter

  • $OrderBy

  • $Top

  • $Skip

These parameters are chained together after the entity using '&':

Please note: For most services, the entity has a '/' before the parameters. On our service we opted to omit this.

OData Functions

OData functions are used in the parameters section to manipulate the data before it's returned to the caller. These functions can be used alone, or chained together to make more complex queries.

$Select

This allows you to reduce the data returned by only picking the fields you want to see. This is synonymous to SQL's select, where the OData default is all (select * from...).

The select looks like this: $Select=[Field 1],[Field 2]...where Field 1, Field 2 and so on are replaced with field names.

Each field you want to select is separated by a comma:

$Expand

Expand allows you to move from the base entity out to other levels of the hierarchy.

The syntax for expand is: $Expand=[Navigation Property]

Navigation properties are the links between entities, and therefore tend to match foreign keys.

These links can be used in both directions, E.g. Contact to Company is Contact. Company. Company to Contact is Company. Contacts.

Compared to SQL, these are most similar to the Join syntax.

Expand is the most complex function, as it allows you to chain other functions inside of the expand e.g. selecting Contact forename and Company name:

https://crm-odata-v1.prospect365.com/Contacts?$select=Surname&$expand=Division($select=Name)

Once inside an expand, you can chain together functions by using ';'. For example, to select every field on Contact, the Company name and everything from Company:

https://crm-odata-v1.prospect365.com/Contacts?$expand=Division($select=Name;$expand=Company)

$Filter

Filters allow you to reduce the data set by specifying a field and a requirement. There are 2 formats for filter:

  • $Filter=[Field Name] [Operator] [Value]

  • $Filter=[Operator]([Field],[Value])

The correct format to use depends on the operator you are using.

Operators for first format:

  • eq - Exactly equal to

  • ne - Inverse of eq

  • lt - Less than but not including

  • gt - Greater than but not including

  • le - Less than or exactly equal to

  • ge - Greater than or exactly equal to

Operators for second format:

  • contains - Value is contained somewhere in the field

  • not contains - Inverse of contains

  • startswith - Field starts with value

  • not startswith - Inverse of startswith

  • endswith - Field ends with value

  • not endswith - Inverse of endswith

There are other more specific expressions which you can find at the Microsoft Documentation on filters.

Finally, filters can be chained together internally with 'and' or 'or' to create more complex expressions:

  • $Filter=[Field] [Operator] [Value] or/and [Operator]([Field], [Value])

Either style of filter can be chained together this way.

Some examples:

$OrderBy

The orderBy function allows you to specify a sort for your data. The syntax for this is:

  • $OrderBy=[Field Name] [Direction]

Direction is either ascending or descending. Some examples:

$Top and $Skip

Top and Skip tend to go hand in hand. Top can be used to limit the number of records returned, while skip allows you to start from further down your list of results.

The syntax for these is:

  • $Top=[number]

  • $Skip=[number]

Some examples:

Further Information

For details of the endpoints and the OData data models, please refer to the API documentation.

Did this answer your question?