vessel-api-python 2.0.1__tar.gz → 2.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,57 @@
3
3
  All notable changes to this package are documented here.
4
4
  This project follows [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [2.1.1] - 2026-10-27
7
+
8
+ Documentation and spec only. No method, parameter or model changed.
9
+
10
+ ### Changed
11
+
12
+ - The API now returns `identity_match` in `PositionCheck` only when it holds a
13
+ record for the vessel and an identifier can be compared. In other cases the
14
+ field is `None`. Before this change, a vessel with no record always got `True`.
15
+ - The API now checks every returned position for a suspected glitch, measured
16
+ from the vessel's previous clean report. More positions can come back with
17
+ `suspected_glitch` set to `True`.
18
+ - The bundled OpenAPI spec is synced with the API. Field descriptions no longer
19
+ begin with an internal field name ("DistanceNM Great-circle distance..." now
20
+ reads "Great-circle distance...").
21
+
22
+ ### Fixed
23
+
24
+ - Docstrings now say when `identity_match` and `vessel_eta` are `None`.
25
+ `vessel_eta` is `None` when no destination or ETA is available, including when
26
+ the lookup does not finish in time. The 2.1.0 notes said it was `None` only
27
+ when the API held no record.
28
+ - `exceeds_typical_speed` and `speed_observed_max` describe the vessel's usual
29
+ top speed (the 99th percentile of its recent speeds) and no longer call it a
30
+ peak.
31
+ - Docstrings call the position in the response the "returned" position, where
32
+ they said "served".
33
+
34
+ ## [2.1.0] - 2026-10-27
35
+
36
+ ### Added
37
+
38
+ - `VesselPositionResponse` has three new fields, which the API returns on every
39
+ position response since 25 October 2026: `vessel` (a `Vessel`, as from
40
+ `vessels.get()`), `vessel_eta` (a `VesselETA`, as from `vessels.eta()`) and
41
+ `position_check` (the new `PositionCheck` model). `vessel` and `vessel_eta`
42
+ are `None` when the API holds no record for them. Earlier versions drop these
43
+ keys when parsing.
44
+ - `PositionCheck` compares the position with the vessel's previous report from
45
+ the last 80 hours (`previous_position`, the new `PreviousPosition` model). It
46
+ gives the time and great-circle distance since then, and the speed and course
47
+ that would cover that distance. It also says whether that speed is more than
48
+ 1.2 times the vessel's usual top speed, and whether the MMSI and IMO match.
49
+ - `vessels.position()` takes `filter_sat_lookback_minutes` and
50
+ `filter_sat_max_age_minutes`, which set the satellite fallback's time windows.
51
+ Both the sync and async clients have them.
52
+
53
+ ### Changed
54
+
55
+ - The bundled OpenAPI spec is synced with the API.
56
+
6
57
  ## [2.0.1] - 2026-10-08
7
58
 
8
59
  Documentation only. No method, parameter or model changed.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vessel-api-python
3
- Version: 2.0.1
3
+ Version: 2.1.1
4
4
  Summary: Python client for the Vessel Tracking API: maritime vessel tracking, port events, emissions, and nautical infrastructure.
5
5
  Project-URL: Documentation, https://vesselapi.com/docs
6
6
  Project-URL: Repository, https://github.com/vessel-api/vesselapi-python
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vessel-api-python"
7
- version = "2.0.1"
7
+ version = "2.1.1"
8
8
  description = "Python client for the Vessel Tracking API: maritime vessel tracking, port events, emissions, and nautical infrastructure."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -45,6 +45,8 @@ from ._models import (
45
45
  PortReference,
46
46
  PortResponse,
47
47
  PortsWithinLocationResponse,
48
+ PositionCheck,
49
+ PreviousPosition,
48
50
  RadioBeacon,
49
51
  RadioBeaconsWithinLocationResponse,
50
52
  ResolutionMeta,
@@ -96,6 +98,8 @@ __all__ = [
96
98
  "VesselResponse",
97
99
  "VesselPosition",
98
100
  "VesselPositionResponse",
101
+ "PositionCheck",
102
+ "PreviousPosition",
99
103
  "VesselPositionsResponse",
100
104
  "MarineCasualty",
101
105
  "MarineCasualtiesResponse",
@@ -1,6 +1,6 @@
1
1
  """Constants for the Vessel API Python SDK."""
2
2
 
3
- VERSION = "2.0.1"
3
+ VERSION = "2.1.1"
4
4
  DEFAULT_BASE_URL = "https://api.vesselapi.com/v1"
5
5
  DEFAULT_USER_AGENT = f"vesselapi-python/{VERSION}"
6
6
  DEFAULT_MAX_RETRIES = 3
@@ -127,7 +127,11 @@ class VesselResponse(_Base):
127
127
 
128
128
 
129
129
  class VesselPosition(_Base):
130
- """A real-time vessel position record from AIS data."""
130
+ """A real-time vessel position record from AIS data.
131
+
132
+ ``suspected_glitch`` is true when the move from the previous clean report
133
+ implies a speed over 500 knots.
134
+ """
131
135
 
132
136
  cog: float | None = None
133
137
  heading: int | None = None
@@ -144,10 +148,47 @@ class VesselPosition(_Base):
144
148
  vessel_name: str | None = None
145
149
 
146
150
 
151
+ class PreviousPosition(_Base):
152
+ """The vessel's newest position report before the returned one."""
153
+
154
+ latitude: float | None = None
155
+ longitude: float | None = None
156
+ timestamp: str | None = None
157
+
158
+
159
+ class PositionCheck(_Base):
160
+ """How the returned position compares with the vessel's previous position report.
161
+
162
+ ``lookback_hours`` is always present. ``identity_match`` is present only when a
163
+ vessel record exists and an identifier can be compared. It is true when the
164
+ returned position's identifiers match those in the record. The other fields
165
+ need a previous report within ``lookback_hours``. ``exceeds_typical_speed`` is
166
+ true when the implied speed exceeds the vessel's usual top speed (99th
167
+ percentile of its recent speeds).
168
+ """
169
+
170
+ previous_position: PreviousPosition | None = None
171
+ minutes_since_previous: int | None = None
172
+ distance_nm: float | None = None
173
+ implied_speed_kn: float | None = None
174
+ implied_course: int | None = None
175
+ exceeds_typical_speed: bool | None = None
176
+ identity_match: bool | None = None
177
+ lookback_hours: int | None = None
178
+
179
+
147
180
  class VesselPositionResponse(_Base):
148
- """Response for a single vessel position lookup."""
181
+ """Response for a single vessel position lookup.
182
+
183
+ ``vessel`` is ``None`` when no vessel record exists. ``vessel_eta`` is ``None``
184
+ when no destination or ETA is available, including when the lookup does not
185
+ finish in time.
186
+ """
149
187
 
150
188
  vessel_position: VesselPosition | None = Field(default=None, alias="vesselPosition")
189
+ vessel: Vessel | None = None
190
+ vessel_eta: VesselETA | None = Field(default=None, alias="vesselEta")
191
+ position_check: PositionCheck | None = Field(default=None, alias="positionCheck")
151
192
  meta: ResolutionMeta | None = Field(default=None, alias="_meta")
152
193
 
153
194
 
@@ -94,15 +94,32 @@ class VesselsService:
94
94
  return VesselResponse.model_validate(r.json())
95
95
 
96
96
  def position(
97
- self, vessel_id: str, *, filter_id_type: str = "imo", filter_sat: bool | None = None
97
+ self,
98
+ vessel_id: str,
99
+ *,
100
+ filter_id_type: str = "imo",
101
+ filter_sat: bool | None = None,
102
+ filter_sat_lookback_minutes: int | None = None,
103
+ filter_sat_max_age_minutes: int | None = None,
98
104
  ) -> VesselPositionResponse:
99
105
  """Retrieve the latest position for a vessel.
100
106
 
101
107
  ``filter_sat`` enables satellite AIS fallback; the server defaults it to false.
108
+ With ``filter_sat=True``, ``filter_sat_lookback_minutes`` (60 to 4800, default 180)
109
+ sets how old a satellite fix may be and still be returned.
110
+ ``filter_sat_max_age_minutes`` (15 to 4800, default 60, paid plans only) sets how
111
+ old a stored position may be before a satellite lookup is made.
102
112
  """
103
113
  r = self._client.get(
104
114
  f"/vessel/{vessel_id}/position",
105
- params=_strip_none({"filter.idType": filter_id_type, "filter.sat": filter_sat}),
115
+ params=_strip_none(
116
+ {
117
+ "filter.idType": filter_id_type,
118
+ "filter.sat": filter_sat,
119
+ "filter.satLookbackMinutes": filter_sat_lookback_minutes,
120
+ "filter.satMaxAgeMinutes": filter_sat_max_age_minutes,
121
+ }
122
+ ),
106
123
  )
107
124
  error_from_response(r.status_code, r.content)
108
125
  return VesselPositionResponse.model_validate(r.json())
@@ -1561,15 +1578,32 @@ class AsyncVesselsService:
1561
1578
  return VesselResponse.model_validate(r.json())
1562
1579
 
1563
1580
  async def position(
1564
- self, vessel_id: str, *, filter_id_type: str = "imo", filter_sat: bool | None = None
1581
+ self,
1582
+ vessel_id: str,
1583
+ *,
1584
+ filter_id_type: str = "imo",
1585
+ filter_sat: bool | None = None,
1586
+ filter_sat_lookback_minutes: int | None = None,
1587
+ filter_sat_max_age_minutes: int | None = None,
1565
1588
  ) -> VesselPositionResponse:
1566
1589
  """Retrieve the latest position for a vessel.
1567
1590
 
1568
1591
  ``filter_sat`` enables satellite AIS fallback; the server defaults it to false.
1592
+ With ``filter_sat=True``, ``filter_sat_lookback_minutes`` (60 to 4800, default 180)
1593
+ sets how old a satellite fix may be and still be returned.
1594
+ ``filter_sat_max_age_minutes`` (15 to 4800, default 60, paid plans only) sets how
1595
+ old a stored position may be before a satellite lookup is made.
1569
1596
  """
1570
1597
  r = await self._client.get(
1571
1598
  f"/vessel/{vessel_id}/position",
1572
- params=_strip_none({"filter.idType": filter_id_type, "filter.sat": filter_sat}),
1599
+ params=_strip_none(
1600
+ {
1601
+ "filter.idType": filter_id_type,
1602
+ "filter.sat": filter_sat,
1603
+ "filter.satLookbackMinutes": filter_sat_lookback_minutes,
1604
+ "filter.satMaxAgeMinutes": filter_sat_max_age_minutes,
1605
+ }
1606
+ ),
1573
1607
  )
1574
1608
  error_from_response(r.status_code, r.content)
1575
1609
  return VesselPositionResponse.model_validate(r.json())