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.
Get started now
Download our open source Postman collection, drop in your access token and you will be making Vessels API calls in seconds.
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
- dimension
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.
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
orfalse
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.
GET https://api.sense.spire.com/messages?sort=created_at
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) orfalse
- 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, setland_filter
tofalse
. To explicitly include land filtering setland_filter
totrue
. - Supported filters: Exact Match
fields
string- Specifies AIS fields to return from AIS messages. For example, to only return
msg_type
andmmsi
, setfields=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, yourmsg_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:
000000000
–999999999
- 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]]]}
{ "status": 422, "title": "Invalid query parameter(s)", "description": "Invalid GeoJSON Polygon position coordinates" }
{"type":"Polygon","coordinates":[[[121.6,35.4],[121.4,32.8],[123.7,31.9],[125.07,34.2],[121.6,35.4]]]}
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:
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:
000000000
–999999999
- 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:
0
–102.2
knots,102.3
(not available) - Message types: 1, 2, 3, 18, 19, 27
course
number- Vessel course over ground in degrees. Possible values:
0
–359.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:
-127
–127
;-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
andto_stern
in meters. Possible values:0
–500
metres,511
(not available) - Message types: 5, 19, 24B
width
number- Vessel width extracted from ship dimensions
to_port
andto_starboard
in meters. Possible values:0
–500
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),0001000000
–0009999999
,0010000000
–1073741823
(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:
1
–12
,0
(not available; default); Day:1
–31
,0
(not available; default); Hour:0
–23
,24
(not available; default); Minute:0
–59
,60
(not available; default) - Message types: 5
draught
number- Vessel draught represented in 1/10 meters. Possible values:
0.1
–255
,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.
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)