Search Open menu

Messages API

The Messages API is a REST API allowing continual paging of AIS messages (NMEA or decoded) received using time based criteria for when data is received.

You can use available filters to continually capture data from a point in time onwards within a timeframe within the past seven days, and you can also filter this data by a specific list of vessels by MMSI number or for a specific AOI.

Returned data is in the form of separate AIS messages, each of which are different types and have their own set of fields.

 

Postman App Icon

Get started now

Download our open source Postman collection, drop in your access token and you will be making Vessels API calls in seconds.

Get started now

Don’t have a token yet? Request a trial

Decoded fields

The following fields are currently decoded in Messages API:

Position Fields

msg_type
AIS message type
mmsi
Marine Mobile Service Identity number for the vessel
timestamp
ISO 8601 formatted UTC timestamp of the AIS message
accuracy
Vessel GPS geolocation accuracy, based on meters. 1: high accuracy (≤ 10 meters), 0: low accuracy (> 10 meters) or default
course
Course over ground in degrees
heading
Direction vessel is facing in degrees
latitude
Vessel latitude in degrees (North = positive, South = negative)
longitude
Vessel longitude in degrees (East = positive, West = negative)
maneuver
Indicates whether or not a vessel may be engaged in a “special” maneuver
rot
Vessel rate of turn in degrees per minute
speed
Vessel speed over ground expressed in knots
status
Vessel navigation status

Static fields

ais_version
Vessel AIS version
call_sign
Vessel call sign
destination
Reported destination of the vessel
draught
Vessel draught expressed in 1/10 meters
eta
Estimated time of arrival for the vessel
imo
Vessel unique International Maritime Organization number
length
Vessel length
name
Vessel name
ship_and_cargo_type
Vessel ship and cargo type code
width
Vessel width
dimensions
The 4 dimensions a,b,c,d of the ship relative to its GPS as detailed below.

  • dimension a: the distance in meters from the GPS to the bow
  • dimension b: the distance in meters from the GPS to the stern
  • dimension c: the distance in meters from the GPS to the port side
  • dimension d: the distance in meters from the GPS to the starboard side

Since AIS messages contain several fields that must be translated into human readable descriptions for usability, we handle this for some fields within Messages AIS for your convenience. Read more about it on the AIS fundamentals articles on ship type mappings and vessel flag codes.

Available AIS Message Types

Messages API returns data as is received via AIS messages. This means the Position and Static Messages are separate,  and can be differentiated via the msg_type field.

Each of these types of messages has its own set of fields; for example, Static AIS messages contain static data related to the vessel, hence will have the imo field populated but not latitude and longitude.

Similarly, Position AIS messages will have latitude and longitude information but not imo.

You can read more about different message types served by Spire APIs in out AIS Fundamentals article on AIS message types.

Making API calls

Authentication

Messages API uses Bearer tokens to authenticate requests. Attempting to make requests to the API without a valid API Key will result in the return of an HTTP 401 Not Authorized response code containing a WWW-Authenticate HTTP header with an error message.

In addition, to ensure transport layer security, all access or communication with the APIs must be made over HTTPS.

Note: if you have a long token (greater than 32 characters), all requests will be made through the https://ais.spire.com/ endpoint. The documentation below uses the short token (32 characters or less) endpoint https://api.sense.spire.com/ for all examples. Please substitute the appropriate endpoint for all requests made and all examples in the following documentation.

Not a Spire customer yet?

You’ll need a token to start using the API. Get in touch to become a customer or request a trial token.

Get a token

 
Quick access test (Bash)
curl -H "Authorization: Bearer {your_token}" -H "Accept: application/json" 'https://api.sense.spire.com/messages?fields=decoded&limit=1'

If you received a response containing one AIS message, you are good to go.

Receiving decoded fields

By default, the Messages API returns a basic message format consisting of only the timestamp, NMEA message, and the message ID. In order to receive all of the decoded fields and additional metadata, simply add fields=decoded as a query parameter:

GET https://api.sense.spire.com/messages?fields=decoded

Data types

There are a few basic data types that describe the resource properties returned within Messages API. They are also used to specify the formatting of inputs to our APIs as query parameters.

string
String value
date
Dates conforming to ISO 8601 format. Time is represented in the UTC timezone.
The generic ISO 8601 timestamp representation is: YYYY-MM-DDTHH:MM:SS+00:00
Some timestamps are received with milliseconds or nanoseconds , when such data is received it is presented in the timestamp format.
Example with milliseconds "timestamp": "2020-07-30 07:40:41.750+00:00"
Example with microseconds "timestamp": "2021-07-18 23:35:29.068408+00:00"
integer
Integer value
number
Numeric value with variable precision; includes floats, decimals.
geometry
Input and response geometry as GeoJSON objects. Geometries can also be used to spatially filter your queries. Responses return latitude/longitude points while inputs accept polygons.
bool
Boolean value, having one of two possible values: true or false
array
JSON array
json
JSON object

Sorting

You can specify your sort order by including sort={PROPERTY_NAME} in the query string. Use a minus sign (-) before the property name to denote descending sort order: sort=-{PROPERTY_NAME}.

By default, the Messages API is sorted by created_at date/time in ascending order.

 
Ascending order sorting
GET https://api.sense.spire.com/messages?sort=created_at
Descending order sorting
GET https://api.sense.spire.com/messages?sort=-created_at

Filtering

We provide a variety of filters to make it easier to manage the amount of data to just what is needed.

Below is a list of common types; the actual filter parameters are listed under Query Parameters section.

Exact match
Returns records where there is a specific match for the value provided.
Example: GET https://api.sense.spire.com/messages?collection_type=satellite
List
Returns results that match multiple values. Any field that support list also supports exact match.
Example: GET https://api.sense.spire.com/messages?mmsi=239245000,273216800,249810000
Range
Range filters work on some fields that are dates and strings.
Example:  GET https://api.sense.spire.com/messages?received_after=2019-02-01T20:56:15&received_before=2019-02-04T22:34:20
Geospatial
Returns data that has a geospatial intersection with the provided input geometry. Input geometries should be valid GeoJSON polygons.
Example: GET https://api.sense.spire.com/messages?position="type":"Polygon","coordinates":[[[-122.41269350051881,37.76058796575955],[-122.41269350051881,37.764124860544094][-122.40750074386597,37.764124860544094],[-122.40750074386597,37.76058796575955],[-122.41269350051881,37.76058796575955]]]}

Query parameters

The Messages API is essentially a firehose of AIS messages. A basic query may return messages of a variety of types from anywhere across the globe. For some, this isn’t entirely helpful if only particular vessels, areas of interest, or specific types of message collection are of interest.

We offer a variety of filters that help you to narrow down the amount of data received from the stream to what you need:

cleansed bool
Returns cleansed or uncleansed data. Valid values: true (default) or false
Supported filters: Exact Match
land_filter bool
Part of the AIS data cleansing implemented by Spire as default (cleansed=true) is to filter out AIS positions messages that are on land. While all data cleansing in Messages API is turned on by default, if needed the land filter can be enabled or disabled independently from other cleansing. To exclude land filtering of positions, set land_filter to false. To explicitly include land filtering set land_filter to true.
Supported filters: Exact Match
fields string
Specifies AIS fields to return from AIS messages. For example, to only return msg_type and mmsi, set fields=msg_type,mmsi .
Supported filters: Exact Match, List
collection_type string
How the AIS message was collected. Valid values: satellite, terrestrial, or dynamic
Supported filters: Exact Match
msg_description string
Description of the message content. Valid values:position, static, aton, other
Supported filters: Exact Match
msg_type integer
AIS message type. Common values: 1, 2, 3, 5, 18, 19, 24, 27.
*If you have an AOI restriction on your token, your msg_type should not include static messages (5,24) in the query parameter as this might give you incomplete data
Supported filters: Exact Match, List
mmsi integer
Vessel MMSI. Valid values: 000000000999999999
Supported filters: Exact Match
position geometry
Vessel position coordinates represented in GeoJSON
Supported filters: Geospatial
received_after date
Returns valid AIS messages from a seven-day window with a timestamp greater than or equal to the time specified
Supported filters: Range
received_before date
Returns valid AIS messages from a seven-day window with a timestamp less than the time specified
Supported filters: Range

Geographical filtering

If you’re only interested in a particular region of the world and want to filter out everything else, we recommend using geographical filters. These filters are inclusive: any messages that fall on the border will be included.

This can be done via position API filter parameter. Here’s an example:

https://ais.spire.com/messages?fields=decoded&position={"type":"Polygon","coordinates":[[[135.25, -30], [135.25, 30.5], [170, 30.5], [170, -30], [135.25, -30]]]}

Make sure the definition of the polygon follows the “right-hand rule”, otherwise your query may not work properly. We recommend testing your coordinates at geojson.io or GeoJSONLint to ensure it’s formatted properly. Also, make sure that the first point of the polygon is repeated as the last point of the polygon to close it.

The example below uses a valid GeoJson polygon but does not repeat the first point to close the polygon:

https://api.sense.spire.com/vessels?limit=100&position={"type":"Polygon","coordinates":[[[121.6,35.4],[121.4,32.8],[123.7,31.9],[125.07,34.2],[124.4,36.8]]]}
Which leads to an error response:
{ "status": 422, "title": "Invalid query parameter(s)", "description": "Invalid GeoJSON Polygon position coordinates" }
And here is the corrected polygon from the previous example with first point repeated as the last point:
{"type":"Polygon","coordinates":[[[121.6,35.4],[121.4,32.8],[123.7,31.9],[125.07,34.2],[121.6,35.4]]]}
…and the valid API call using the corrected polygon, which would now return data:
https://api.sense.spire.com/vessels?limit=100&position={"type":"Polygon","coordinates":[[[121.6,35.4],[121.4,32.8],[123.7,31.9],[125.07,34.2],[121.6,35.4]]]}
 

If you are positively interested in a particular region of the earth, we’re happy to place a geo-filter on your account.

Once applied, the Messages API will only feed you AIS messages within the defined region.

Note: If you’re using the TCP feed, the Customer Experience team will have to apply a geo-filter for you.

Notes on mmsi filtering

For customers which subscribe to a fixed, limited list of ships (either specified by IMO number or MMSI number), this overrides any mmsi filter parameters passed in calls to the Messages API.

For example,  for a user subscription that is limited to the following MMSI list:

Example of a list of MMSIs used for filtering calls to Messages API

If a call to Messages API was made, filtering using only the highlighted MMSI:

https://ais.spire.com/messages?mmsi=356417000&msg_type=5&fields=decoded&limit=2000&received_after=2020-04-18T00:00:00

… the API response would actually contain messages from 192 different ship MMSI numbers, corresponding to the full list, which overrides the mmsi parameter. In this particular situation, the result filtering would have to be done client-side.

Pagination

We have implemented two different types of pagination:

  • Cursors: for more easily working with constantly updating data feeds
  • Limits & Offsets: for working with more static data sets.

Cursors

The Messages API returns a timeline of constantly updating message data. This near-real-time nature and volume of data continuously being added to the feed means that typical “pages” do not work very well.

By default, the Messages API returns results for the past three hours. But if you make the same query 30 minutes later, you will not return the same results as new messages have been added while others have dropped out because they are no longer in the three-hour window.

What most customers want to do is get all the data from the feed since the last time they queried the API. To make this easier we have introduced the since cursor so that you can quickly request new data from where you were before.

Example of working with cursors

Make a Messages API request to get data for the past three hours:

GET https://api.sense.spire.com/messages?fields=decoded

This will return the first “page” of 10,000 results for the past three hours. A since cursor is provided in the body of the response that points to the end of the results that have been returned.

We then use this since cursor in the next request to the API. This will return the next “page” of results moving closer in time to now.

GET https://api.sense.spire.com/messages?fields=decoded&since=MTAxNTExOTQ1MjAwNzI5NDE=

Continue to cycle through the pages with each new cursor until you get a response with an empty array. You are now caught up with the feed. From there you can continue requesting on a regular cadence (ex: every 5 minutes) to keep up with new data and move the cursor forward.

Limits & Offsets

When using ranged-based historical queries in the Messages API, we use a fairly standard limit & offset pagination through a provided ID for the next and previous pages of results.

Given no specific arguments, the Messages API returns timestamp , msg_id , nmea  and id fields for 20,000 AIS messages received within the past six hours. This is constitutes the maximum limit of messages per page.

 

Move to the next page of results by appending the after query parameter:

GET https://api.sense.spire.com/messages?fields=decoded&before=MjAxNy0wOC0yOCAwMDowMDowMCswMDowMA==

Move to the previous page of results by appending the previous query parameter:

GET https://api.sense.spire.com/messages?fields=decoded&after=MjAxNy0wOC0yOCAwMDowMDowMCswMDowMA==

Limits

Messages API requests may lead to thousands of available results; therefore, when a request is made, all of the results usually aren’t received in a single response.

Response limits can be customized to help limit the amount of data returned using the limit filter parameter.

The Messages API has a default limit of 10000 with a max of 15000.

 
GET https://api.sense.spire.com/messages?limit=10

Rate limiting

We recommend keeping the frequency of your API calls below 30 per minute. If you attempt to query one of the Spire Sense APIs more often than that, you may encounter the following error:

"Spire API rate limit exceeded. Please limit your requests to 30 per minute to avoid future issues."

If you encounter this error, it should clear within about 30 seconds.

Handling API responses

Available fields

nmea string
Full NMEA 0183 v4 message
Message types: All
msg_type integer
AIS message type. Common values: 1, 2, 3, 5, 18, 19, 24, 27
Message types: All
timestamp string
ISO 8601 formatted timestamp of message collection in UTC at the time of broadcast
Message types: All
created_at string
ISO 8601 formatted system ingestion time in UTC of message into Messages API
Message types: All
mmsi integer
Vessel Maritime Mobile Service Identity number. Possible values: 000000000999999999
Message types: All
collection_type string
How the message was captured. Possible values: satellite, terrestrial, or dynamic
Message types: All
source string
From May 2023 this field is deprecated for new users and will contain the fixed value satellite to indicate satellite received AIS messages.
Prior to May 2023, the source field could specify the Spire satellite ID for satellite received AIS messages.
Message types: All
msg_id string
Unique identifier for each message, created by combining the timestamp and MMSI
Message types: All
flag string
Vessel country flag (derived from MMSI)
Message types: All
flag_short_code string
Vessel country flag using 2-letter country codes (derived from MMSI)
Message types: All
longitude number
Vessel longitude in degrees (East = positive, West = negative)
Message types: 1, 2, 3, 4, 18, 19, 27
latitude number
Vessel latitude in degrees (North = positive, South = negative)
Message types: 1, 2, 3, 4, 18, 19, 27
position geometry
Vessel position coordinates represented in GeoJSON
Message types: 1, 2, 3, 4, 18, 19, 27
speed number
Vessel speed over ground represented in knots. Possible values: 0102.2 knots, 102.3 (not available)
Message types: 1, 2, 3, 18, 19, 27
course number
Vessel course over ground in degrees. Possible values:0359.9 degrees, 360.0 (not available)
Message types: 1, 2, 3, 18, 19, 27
heading number
Vessel true heading in degrees. Possible values: 0 - 359 degrees, 511 (not available)
Message types: 1, 2, 3, 18, 19
status integer
Vessel navigation status. Some common values: 0 (under way using engine), 1 (at anchor), 3 (restricted maneuverability), 7 (engaged in fishing), 15
Message types: 1, 2, 3, 18, 19
accuracy integer
Vessel GPS geolocation accuracy in meters. Possible values: 1 (high, <=10 meters); 0 (low, >10 meters, default)
Message types: 1, 2, 3, 4, 18, 19, 27
rot number
Vessel rate of turn. Possible values: -127127; -128 (not available)
Message types: 1, 2, 3
maneuver integer
Vessel maneuver code. Valid values: 0 (not available; default), 1 (not engaged in special maneuver), 2 (engaged in special maneuver)
Message types: 1, 2, 3
ais_version integer
Vessel AIS version. Possible values: 0 (compliant with Recommendation ITU-R M.1371-1), 1 (compliant with Recommendation ITU-R M.1371-3), 2 (compliant with Recommendation ITU-R M.1371-5 or later), 3 (compliant with future editions)
Message types: 5
name string
Vessel name
Message types: 5, 19, 24A
length number
Vessel length extracted from ship dimensions to_bow and to_stern in meters. Possible values: 0500 metres, 511 (not available)
Message types: 5, 19, 24B
width number
Vessel width extracted from ship dimensions to_port and to_starboard in meters. Possible values: 0500 meters, 511 (not available)
Message types: 5, 19, 24B
ship_and_cargo_type integer
Vessel ship and cargo type code. Some common values: 30 (fishing vessel), 52 (tug boat), 70 (cargo/fishing ship)
Message types: 5, 19, 24B
ship_type string
Vessel type description
Message types: 5, 19, 24B
call_sign string
Vessel call sign
Message types: 5, 24B
imo integer
Vessel unique International Maritime Organization number. Possible values: 0 (not available; default), 00010000000009999999, 00100000001073741823 (office flag state number)
Message types: 5
destination string
Vessel destination as entered by the vessel captain
Message types: 5
eta string
Vessel estimated time of arrival as entered by the captain, represented in ISO 8601 format. Possible values:Month: 112, 0 (not available; default); Day: 131, 0 (not available; default); Hour: 023, 24 (not available; default); Minute: 059, 60 (not available; default)
Message types: 5
draught number
Vessel draught represented in 1/10 meters. Possible values: 0.1255, 0 (not available; default)
Message types: 5

Difference between created_at and timestamp

It’s important to differentiate created_at and timestamp, how they may impact your AIS messages processing, and how they define latency.

One might expect them to be the same, however, this often not the case, as seen in a response below:

"data": [
    {
      "msg_type": 1,
      "msg_id": "1490374942_413769954",
      "course": 0,
      "collection_type": "terrestrial",
      "nmea": "!AIVDM,1,1,0,A,16:VPpPk00`U@i<BD3S@0?vd40=6,0*72",
      "rot": 120,
      "speed": 0,
      "latitude": 32.0049283333,
      "type": 1,
      "accuracy": 1,
      "status": 0,
      "maneuver": 0,
      "timestamp": "2017-03-24T17:02:22+00:00",
      "mmsi": 413769954,
      "flag": "China",
      "created_at": "2017-03-24T17:03:10.101117",
      "msg_description": "position",
      "longitude": 119.9881166667,
      "flag_short_code": "CN",
      "position": {
        "type": "Point",
        "coordinates": [
          119.9881166667,
          32.0049283333
        ]
      },
      "heading": 511
    }

The timestamp field

The timestamp value is tagged to an AIS message transmitted from a vessel, and is is the reception time of the AIS message by the receiving system, be it a Terrestrial AIS receiver, a Dynamic AIS™ receiver, or a Satellite AIS receiver. This is expected to be almost identical to the transmission time of the message, and will vary from transmission time only if the system clocks of receiving systems are drifting from actual time synchronization.

  • For messages collected by a Spire satellite, timestamp is based on the satellite time reference. (We check satellite time to a ground reference multiple times per day and synchronize whenever necessary.)
  • For messages collected by a Terrestrial AIS source, timestamp is effectively when the terrestrial source received the message.

Unfortunately, timestamp fields reported with AIS messages are not always guaranteed to be accurate. Spire Maritime cleans up a significant portion of these “dirty” messages by filtering out any AIS messages with timestamp older than 30 days.

The created_at field

The timestamp is the reception time of the AIS message by the receiving system; whereas created_at is the time that the message was processed by the ingestion script at Spire Maritime. Hence, created_at will always be after timestamp as you can see in the example provided above.

These two fields can be used to define the latency of AIS messages: it is the difference in time between created_at and timestamp. Read more about this in our AIS Fundamentals article on AIS message latency.

 

Note: By default the Messages API only returns a timestamp value. You must issue a fields=decoded  parameter within your API call in order to see created_at:

https://ais.spire.com/messages?fields=decoded&limit=10

The collection_type and source fields

The collection_type and source values attached to some AIS messages are additional metadata provided by Spire Maritime.

These values are not reported by ships or within the AIS standard.

Possible values for collection_type

"collection_type": "satellite" : The AIS message was collected by a Spire satellite.
"collection_type": "terrestrial" : The AIS message was collected by a terrestrial source.
"collection_type": "dynamic" : The AIS message was collected by Spire’s Dynamic AIS™ source.

Please note that Dynamic AIS™ messages are only available if you have subscribed to this data source. Get in touch with our team to get started with Dynamic AIS™, or learn more about it.

Possible values for source

If an AIS message has a collection_type of “satellite”, the source field will also be available.

From April 2023 this field is deprecated for new users and will contain the fixed value satellite to indicate satellite received AIS messages.

Prior to May 2023 this field listed which satellite collected the message using Spire-internal satellite identifiers. Most satellite IDs follow a Flight Number (FM) naming scheme (e.g. "source": "FM49") with some exceptions.

Note that AIS messages with a collection_type of “terrestrial”  or “dynamic” have a null value for source.

Dimensions

The dimensions object contains the ship dimensions information from an AIS static voyage message.

A number
Dimension A is the distance in meters from the ships GPS to the bow.
B number
Dimension B is the distance in meters from the ships GPS to the stern.
C number
Dimension C is the distance in meters from the ships GPS to the port side.
D number
Dimension D is the distance in meters from the ships GPS to the starboard side.

Vessel flag countries

We append flag and flag_short_code as additional meta data to each message in Messages API in order to identify a vessel’s country flag:

"flag": "USA"
"flag_short_code": "US"

These fields are based on the MID portion of the MMSI; please refer to the Vessel Flag Codes article of the AIS Fundamentals for a complete explanation and reference tables on MID and flag codes.

How ship types are assigned

Static messages (types 5 & 24) broadcast a ship and cargo type, which we provide in the ship_and_cargo_type field.

Additionally, we map this value as close to the AIS standard as possible in the ship_type field.

Standard Ship Type – Code(s) (2 digits only)
Reserved for future use
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
Wing In Ground
20, 21, 22, 23, 24, 25, 26, 27, 28, 29
Search and Rescue
51
Fishing Vessel
30
Tug
31, 32, 52
Special Craft
33, 34, 35, 50, 53, 54, 55, 56, 57, 58, 59
Sailing Vessel
36
Pleasure Craft
37
Reserved
38, 39
High-Speed Craft
40, 41, 42, 43, 44, 45, 46, 47, 48, 49
Passenger Ship
60, 61, 62, 63, 64, 65, 66, 67, 68, 69
Cargo
70, 71, 72, 73, 74, 75, 76, 77, 78, 79
Tanker
80, 81, 82, 83, 84, 85, 86, 87, 88, 89
Other
90, 91, 92, 93, 94, 95, 96, 97, 98, 99
Non-standard AIS Ship Type – Code(s)
Other (Reserved for regional use)
100-199
Other (Reserved for future use)
200-255
Other (No designation)
256-999
 
Need more specific vessel classification?

Vessels API provides a more granular and precise description of a vessel typology based not only on AIS messages, but also external data sources and vessel behavior.

Errors

When there is an error with your request, the response header will contain a status code to help you determine what the issue is.

Additionally, the response body will contain a more detailed message.

Our APIs may respond with the following errors:

400 – Bad Request
A request made with a malformed HTTP Authorization Header or query parameters. Unaccepted query parameters will simply be ignored.
401 – Unauthorized
A request made with an invalid, unrecognized or missing access token.
403 – Forbidden
The metadata associated to a JWT is no longer valid and access to the API is denied.
404 – Not Found
A request made to an unknown or supported resource.
406 – Not acceptable
A request made with invalid HTTP headers.
414 – URI Too Long
The request was well-formed but is too large.
422 – Unprocessable
The request was well-formed but was unable to be followed due to semantic errors.
429 – Too many requests
Exceeding the rate limit will result in a 429 error response until a rate limit refresh threshold has been met.
502 – Bad gateway
If the API encounters any technical difficulties while processing a request, it will respond with a description detailing the status of the API.
503 – Service unavailable
If the API encounters any technical difficulties while processing a request, it will respond with a description detailing the status of the API.

Handling last page of results

A since or after value is returned in the paging section of Message API results, and which ever is returned should be used to paginate through the multiple sets of messages that will be returned by the original request:

GET https://ais.spire.com/messages?fields=decoded

Response:

"paging": {
    "limit": 20000,
    "since": "MjAxOC0wNS0xNyAyMzozNzowNC4yODI2NDUrMDA6MDA=",
    "actual": "165"
}

However, after the last page is reached, the since or after cursor value will remain the same value and actual results will be 0 .

"paging": {
    "limit": 20000,
    "since": "MjAxOC0wNS0xNyAyMzozNzowNC4yODI2NDUrMDA6MDA=",
    "actual": "0"
}

If you’re querying for recent AIS messages (the default behavior), this response indicates you’ve reached the end of the feed – which means, at that point, there are no new AIS messages to ingest.

Since the Messages API primarily serves as a constantly updating feed, once you’ve reached the last page, your API client should continue periodically fetching the since cursor. A change in the value for since indicates new messages are available.

Below, we have some output from our sample Python Live Messages API Client as it keeps up with live Messages API updates for reference:

Start Querying SPIRE Data...
https://ais.spire.com/messages?fields=decoded
20000 messages
{u'actual': u'20000+', u'since': u'MjAxOC0wNS0xNyAyMzo1Njo0Ni40MzczNjgrMDA6MDA=', u'limit': 20000}
https://ais.spire.com/messages?since=MjAxOC0wNS0xNyAyMzo1Njo0Ni40MzczNjgrMDA6MDA=&fields=decoded
6201 messages
{u'actual': u'6201', u'since': u'MjAxOC0wNS0xOCAwMDozNjozMy43ODQ3MDMrMDA6MDA=', u'limit': 20000}
https://ais.spire.com/messages?since=MjAxOC0wNS0xOCAwMDozNjozMy43ODQ3MDMrMDA6MDA=&fields=decoded
0 messages
{u'actual': u'0', u'since': u'MjAxOC0wNS0xOCAwMDozNjozMy43ODQ3MDMrMDA6MDA=', u'limit': 20000}
Waiting for 1 minute.
https://ais.spire.com/messages?since=MjAxOC0wNS0xOCAwMDozNjozMy43ODQ3MDMrMDA6MDA=&fields=decoded
162 messages
{u'actual': u'162', u'since': u'MjAxOC0wNS0xOCAwMDozNzozMy42Njk3MzUrMDA6MDA=', u'limit': 20000}
https://ais.spire.com/messages?since=MjAxOC0wNS0xOCAwMDozNzozMy42Njk3MzUrMDA6MDA=&fields=decoded
0 messages
{u'actual': u'0', u'since': u'MjAxOC0wNS0xOCAwMDozNzozMy42Njk3MzUrMDA6MDA=', u'limit': 20000}
Waiting for 1 minute.

Historical querying best practices

You can request any AIS messages with timestamps within the past seven days from the Messages API. This can be done via the received_after and received_before API parameters.

For instance, the following command requests AIS messages with timestamps from 2018-05-25 from 00:00:00 to 00:02:00:

GET https://ais.spire.com/messages?fields=decoded&received_after=2018-05-25T00:00:00&received_before=2018-05-25T00:02:00

Similar to queries for recent AIS messages, API responses above a certain size get paginated with cursors to refer to the next page of results.

Going forward in time, the after and before parameters point to your following page of results:

{
    "paging": {
        "limit": 20000,
        "after": "MjAxOC0wNS0yNSAwMDowMDoxNiswMDowMA==",
        "actual": "20000+",
        "before": "MjAxOC0wNS0yNSAwMDowMjowMCswMDowMA=="
    },
    "data": [
        {
            "msg_type": 1,
            "msg_id": "1527206400_273824000",
            "course": 304.6,
            "collection_type": "satellite",
            "nmea": "!AIVDM,1,1,,B,1458q0700RC=UC0RuNh;qar00000,0*39",
            "rot": 0,
            "id": "a0eb2135-142c-4009-96e6-3d302d403f09",
            "speed": 3.4,
            "source": "FM61",
            "latitude": 61.0981333333,
            "type": 1,
            "accuracy": 0,
            "status": 7,
            "maneuver": 0,
            "timestamp": "2018-05-25T00:00:00+00:00",
            "mmsi": 273824000,
            "flag": "Russia",
            "created_at": "2018-05-25T07:30:50.144283+00:00",
            "msg_description": "position",
            "longitude": -178.7859733333,
            "flag_short_code": "RU",
            "position": {
                "type": "Point",
                "coordinates": [
                    -178.7859733333,
                    61.0981333333
                ]
            },
            "heading": 317
        },
        {
            "msg_type": 1,
            "msg_id": "1527206400_235335000",
            "course": 66,
            "collection_type": "satellite",
            "nmea": "!AIVDM,1,1,,B,13PKeF0036bafB0I4G82U27n0000,0*03",
            "rot": 0,
            "id": "4b1b6c63-8b9f-4c1d-910b-0fda9e8573c0",
            "speed": 19.8,
            "source": "FM61",
            "latitude": 43.80976,
            "type": 1,
            "accuracy": 1,
            "status": 0,
            "maneuver": 0,
            "timestamp": "2018-05-25T00:00:00+00:00",
            "mmsi": 235335000,
            "flag": "United Kingdom",
            "created_at": "2018-05-25T07:30:50.144283+00:00",
            "msg_description": "position",
            "longitude": 148.9246933333,
            "flag_short_code": "GB",
            "position": {
                "type": "Point",
                "coordinates": [
                    148.9246933333,
                    43.80976
                ]
            },
            "heading": 67
        },

To fetch additional pages of results, include the subsequent after and before cursors in your following API calls:

GET https://ais.spire.com/messages?fields=decoded&after=MjAxOC0wNS0yNSAwMDowMDoxNiswMDowMA==&before=MjAxOC0wNS0yNSAwMDowMjowMCswMDowMA==

And so forth. Below is some output from our sample Python Historical Messages API Client as it goes through all of the results:

Start Querying SPIRE Data...
https://ais.spire.com/messages?fields=decoded&received_after=2018-05-22T00:00:00&received_before=2018-05-22T00:01:00
20000 messages
{u'actual': u'20000+', u'after': u'MjAxOC0wNS0yMiAwMDowMDoxOCswMDowMA==', u'limit': 20000, u'before': u'MjAxOC0wNS0yMiAwMDowMTowMCswMDowMA=='}
https://ais.spire.com/messages?fields=decoded&after=MjAxOC0wNS0yMiAwMDowMDoxOCswMDowMA==&before=MjAxOC0wNS0yMiAwMDowMTowMCswMDowMA==
20000 messages
{u'actual': u'20000+', u'after': u'MjAxOC0wNS0yMiAwMDowMTowMCswMDowMA==', u'limit': 20000, u'before': u'MjAxOC0wNS0yMiAwMDowMTowMCswMDowMA=='}
https://ais.spire.com/messages?fields=decoded&after=MjAxOC0wNS0yMiAwMDowMTowMCswMDowMA==&before=MjAxOC0wNS0yMiAwMDowMTowMCswMDowMA==
381 messages
{u'actual': u'381', u'limit': 20000}
The data transfer is over. Thank you.

If you attempt to request data older than 7 days from the Messages API, you’ll encounter the following error:

"Invalid filter received_before timestamp should be after received_after"
 

Looking for historical data older than 7 days?

Our AIS archives contains data that goes back as far as 2011. If you’re interested in archived AIS data, it can be prepared for delivery within file(s) in a CSV or JSON format.

Make a historical data request

Querying examples

Get most recent decoded messages

Request

curl - i - H "Authorization: Bearer {your_token}" - X GET https://api.sense.spire.com/messages?fields=decoded

Response

{
  "paging": {
    "limit": "20000",
    "since": "MjAxNy0wMy0yNCAxNzowNzoxNi4yMzUyNTc=",
    "actual": "20000+"
  },
  "data": [
    {
      "msg_type": 5,
      "msg_id": "1490375235_356446000",
      "collection_type": "terrestrial",
      "nmea": "!AIVDM,2,1,1,A,55Csg<82=RqdPu<n22118Tp<E=>0u04j2222221@:`G5=tt=0?2T85Bh`888,0*7A\r\n!AIVDM,2,2,1,A,88888888880,2*25",
      "call_sign": "HOSM",
      "ais_version": 2,
      "destination": "JP UKB",
      "imo": 9276315,
      "width": 18,
      "ship_and_cargo_type": 80,
      "type": 5,
      "draught": 6,
      "timestamp": "2017-03-24T17:07:15.828897+00:00",
      "mmsi": 356446000,
      "flag": "Panama",
      "ship_type": "Tanker",
      "name": "PRINCESS OPAL",
      "created_at": "2017-03-24T17:07:16.235257",
      "msg_description": "static",
      "length": 108,
      "eta": "2017-03-24T13:00:00",
      "flag_short_code": "PA"
    }
  ]
}

Get most recent messages by MMSI list

Request

curl - i - H "Authorization: Bearer {your_token}" - X GET https://api.sense.spire.com/messages?fields=decoded&mmsi=356206000,219657000,244992000

Response

{
  "paging": {
    "limit": "20000",
    "since": "MjAxNy0wMy0yNCAxNzoxMDozMy43NDQxOTY=",
    "actual": "79"
  },
  "data": [
    {
      "msg_type": 1,
      "msg_id": "1490375406_356206000",
      "course": 166.5,
      "collection_type": "terrestrial",
      "nmea": "!AIVDM,1,1,0,A,15Ce5d002:Q@1mFFGRBFPED<0>`<,0*0E",
      "rot": 0,
      "speed": 13.8,
      "latitude": 39.0904683333,
      "type": 1,
      "accuracy": 1,
      "status": 0,
      "maneuver": 0,
      "timestamp": "2017-03-24T17:10:06+00:00",
      "mmsi": 356206000,
      "flag": "Panama",
      "created_at": "2017-03-24T17:10:33.744196",
      "msg_description": "position",
      "longitude": 17.482525,
      "flag_short_code": "PA",
      "position": {
        "type": "Point",
        "coordinates": [17.482525,39.0904683333]
      },
      "heading": 170
    }
  ]
}

Get most recent type 1 messages for AOI

Request

curl - i - H "Authorization: Bearer {your_token}" - X GET 'https://api.sense.spire.com/messages?msg_type=1&fields=decoded&position={"type":"Polygon","coordinates":[[[-48.33984375,48.10743118848039],[-71.015625,28.92163128242129],[-68.203125,24.686952411999155],[-52.20703125,15.623036831528264],[-24.43359375,17.308687886770034],[-13.7109375,47.754097979680026],[-48.33984375,48.10743118848039]]]}'

Response

{
  "paging": {
    "limit": "20000",
    "since": "MjAxNy0wMy0yNCAxNzoxMTowMy40NTg5NjM=",
    "actual": "20000+"
  },
  "data": [
    {
      "position": {
        "type": "Point",
        "coordinates": [-29.6648133333,38.3556583333]
      },
      "mmsi": 245995000,
      "nmea": "!AIVDM,1,1,0,B,1CbVEv300<Mp=8hEt`4nJbN60@1N,0*6B",
      "msg_type": 1,
      "timestamp": "2017-03-24T17:11:03+00:00"
    }
  ]
}

Get all messages inside date/time range

Request

curl - i - H "Authorization: Bearer {your_token}" - X GET 'https://api.sense.spire.com/messages?fields=decoded&received_after=2017-08-28T00:00:00&received_before=2017-08-28T23:59:59'

Response

{
  "paging": {
    "limit": "20000",
    "after": "MjAxNy0wOC0yOCAwMDowMDoyMiswMDowMA==",
    "actual": "20000+",
    "before": "MjAxNy0wOC0yOCAyMzo1OTo1OSswMDowMA=="
  },
  "data": [
    {
      "msg_type": 1,
      "msg_id": "1503878400_224335000",
      "course": 79.8,
      "collection_type": "satellite",
      "nmea": "!AIVDM,1,1,,B,13EtDV0P0I0J1F1iu`d37gv00000,0*1C",
      "rot": 731,
      "speed": 2.5,
      "source": "FM49",
      "latitude": - 24.5313333333,
      "type": 1,
      "accuracy": 0,
      "status": 0,
      "maneuver": 0,
      "timestamp": "2017-08-28T00:00:00+00:00",
      "mmsi": 224335000,
      "flag": "Spain",
      "created_at": "2017-08-28T12:09:10.601135+00:00",
      "msg_description": "position",
      "longitude": 5.6843733333,
      "flag_short_code": "ES",
      "position": {
        "type": "Point",
        "coordinates": [5.6843733333,-24.5313333333]
      },
      "heading": 511
    }
  ]
}

Python sample code

Here is a sample of a python script to request and store information from the Messages API:

import csv, json, requests

endpoint = 'https://ais.spire.com/messages'
token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21lciI6eyJpZCI6IjUzMiIsIm5hbWUiOiJNYXggQWJvdWNoYXIgU3BpcmUiLCJ1dWlkIjoiNTMyIn0sImlzcyI6InNwaXJlLmNvbSIsImlhdCI6MTU1OTc3MDM3NH0.Mm6axFI_0LPAJweQ0RqVK6DPFHJoE3bG1F38iHlVnSk'
headers = {'Authorization': "Bearer " + token, 'Accept': 'application/json'}

since_token = 'CiQ4NjdhZTI5OS03NWUzLTU2ZWUtYmZlNS1jZDYxOWYxNmQ1NTk='

fields_decoded = '&fields=decoded'
other_params = '&limit=1000'


request_url = endpoint + '?since=' + since_token + fields_decoded + other_params

response = requests.get(request_url, headers = headers).json()

all_fields = ['id',
 'nmea',
 'timestamp',
 'msg_id',
 'msg_type',
 'created_at',
 'mmsi',
 'collection_type',
 'flag',
 'flag_short_code',
 'longitude',
 'latitude',
 'position',
 'speed',
 'course',
 'heading',
 'status',
 'accuracy',
 'rot',
 'maneuver',
 'ais_version',
 'name',
 'length',
 'width',
 'ship_and_cargo_type',
 'call_sign',
 'imo',
 'destination',
 'eta',
 'draught',
 'dimensions',
 'ship_type']

def write_json(data, file_name):
    #takes data and file name and creates a json file
    with open(file_name, 'w') as json_file:
        json.dump(data, json_file)

def write_csv(data, file_name, columns):
    #takes data and writes a csv with given headers
    
    output_csv = open(file_name, 'w', newline='')
    csv_writer = csv.DictWriter(output_csv, columns)
    
    csv_writer.writeheader()
    for line in data:
        csv_writer.writerow(line)

    output_csv.close()

write_json(response, 'json_output.json')

write_csv(response['data'], 'csv_output.csv', all_fields)