vessel-api-python 1.3.1__tar.gz → 2.0.0__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.
Files changed (31) hide show
  1. vessel_api_python-2.0.0/CHANGELOG.md +123 -0
  2. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/PKG-INFO +80 -29
  3. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/README.md +77 -26
  4. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/pyproject.toml +22 -4
  5. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/__init__.py +9 -75
  6. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/_client.py +0 -4
  7. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/_constants.py +1 -1
  8. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/_errors.py +49 -1
  9. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/_models.py +50 -396
  10. vessel_api_python-2.0.0/src/vessel_api_python/_services.py +3019 -0
  11. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/_transport.py +5 -2
  12. vessel_api_python-1.3.1/.github/workflows/ci.yml +0 -46
  13. vessel_api_python-1.3.1/.github/workflows/publish.yml +0 -24
  14. vessel_api_python-1.3.1/Makefile +0 -21
  15. vessel_api_python-1.3.1/examples/async_basic.py +0 -67
  16. vessel_api_python-1.3.1/examples/basic.py +0 -86
  17. vessel_api_python-1.3.1/openapi/swagger.json +0 -5779
  18. vessel_api_python-1.3.1/src/vessel_api_python/_services.py +0 -1225
  19. vessel_api_python-1.3.1/tests/__init__.py +0 -0
  20. vessel_api_python-1.3.1/tests/conftest.py +0 -34
  21. vessel_api_python-1.3.1/tests/test_client.py +0 -102
  22. vessel_api_python-1.3.1/tests/test_errors.py +0 -136
  23. vessel_api_python-1.3.1/tests/test_iterator.py +0 -136
  24. vessel_api_python-1.3.1/tests/test_services.py +0 -177
  25. vessel_api_python-1.3.1/tests/test_smoke.py +0 -601
  26. vessel_api_python-1.3.1/tests/test_transport.py +0 -215
  27. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/.gitignore +0 -0
  28. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/LICENSE +0 -0
  29. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/SECURITY.md +0 -0
  30. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/_iterator.py +0 -0
  31. {vessel_api_python-1.3.1 → vessel_api_python-2.0.0}/src/vessel_api_python/py.typed +0 -0
@@ -0,0 +1,123 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented here.
4
+ This project follows [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [2.0.0] - 2026-08-16
7
+
8
+ Vessel ownership, classification and inspection data, and NAVTEX messages, were
9
+ retired from the VesselAPI service on **10 August 2026**. This release removes
10
+ the corresponding client surface and brings the SDK back in line with the API.
11
+
12
+ Every change below applies to both the sync and async clients.
13
+
14
+ ### Removed (breaking)
15
+
16
+ These endpoints no longer exist. Calling them returned HTTP 410 from 10 August
17
+ 2026 onward; the methods are now gone so the failure surfaces at import or call
18
+ time rather than as a runtime error.
19
+
20
+ | Removed | Replacement |
21
+ |---|---|
22
+ | `vessels.classification()` | none |
23
+ | `vessels.inspections()` | none |
24
+ | `vessels.inspection_detail()` | none |
25
+ | `vessels.ownership()` | none |
26
+ | `client.navtex` (whole service: `list()`, `list_all()`) | none |
27
+
28
+ Also removed:
29
+
30
+ - **Search filters** `filter_class_society` and `filter_owner` on
31
+ `search.vessels()`. The API began rejecting both with HTTP 400, so any call
32
+ passing them was already failing.
33
+ - **Vessel fields** `builder`, `class_society`, `owner_name`, `manager_name`.
34
+ - **Models** for the removed endpoints (26 classes), plus the four
35
+ backwards-compatibility aliases `Classification`, `Inspection`,
36
+ `InspectionDetail` and `Ownership`: 30 public names in total.
37
+
38
+ **Migrating from 1.x:** remove calls to the methods above. There is no
39
+ alternative source for that data through this API. Drop `filter_class_society`
40
+ and `filter_owner` from any search call. If you read `owner_name`,
41
+ `manager_name`, `builder` or `class_society` from a vessel, those attributes are
42
+ gone.
43
+
44
+ ### Added
45
+
46
+ - **Search**: `q` (free-text across name, IMO, MMSI, ENI and callsign) and
47
+ `filter_eni` on `search.vessels()`.
48
+ - **Repeatable filters.** `filter_flag` and `filter_vessel_type` on
49
+ `search.vessels()`, and `filter_country`, `filter_port_type`, `filter_size`,
50
+ `filter_harbor_size` and `filter_harbor_use` on `search.ports()`, now accept
51
+ `str | list[str]` (exported as `MultiFilter`). A list matches any of the
52
+ values. Passing a plain string behaves exactly as before.
53
+ - **`filter_sat`** on `vessels.position()`. ⚠️ Satellite lookups draw on a
54
+ prepaid balance and are charged per call. Omit it to serve stored positions.
55
+ - **`time_from` / `time_to`** on `port_events.by_port()`, `by_ports()`,
56
+ `by_vessels()` and their iterators.
57
+ - **Vessel fields**: `eni`, `teu`, `vessel_subtype`, `name_ais`,
58
+ `summer_draught`, `draught_calculated_avg`, `draught_observed_max`,
59
+ `speed_calculated_avg`, `speed_observed_max`. All are declared by the API and
60
+ were previously discarded by the models. Each is optional, and how often one
61
+ is populated varies by field and by vessel.
62
+ - **Search metadata**: `FindVesselsResponse.meta` (`matched_on`, `query`) via
63
+ the new `VesselSearchMeta` model, and `ResolutionMeta.suggested_id_type`, a
64
+ hint to retry with the other identifier type when a lookup misses.
65
+ - **Error handling**: `VesselPaymentRequiredError` (402, satellite credits
66
+ exhausted) and `VesselForbiddenError` (403, key suspended or feature not on
67
+ your plan), plus `is_payment_required` / `is_forbidden` properties. All errors
68
+ now expose the API's machine-readable `code` and `error_type`. Branch on
69
+ those rather than on `message`, which may be reworded.
70
+
71
+ ### Changed (breaking)
72
+
73
+ - **Parameters the API requires are now required in Python too.** They are
74
+ keyword-only with no default, so omitting one raises `TypeError` at the call
75
+ instead of returning HTTP 400 from the server. Previously they defaulted to
76
+ `None`, which type-checked cleanly and then failed at runtime.
77
+ - `location.*_bounding_box()`: `lat_min`, `lat_max`, `lon_min`, `lon_max`.
78
+ - `location.*_radius()`: `latitude`, `longitude`, `radius`.
79
+ - `search.dgps()`, `search.light_aids()`, `search.modus()`,
80
+ `search.radio_beacons()`: `filter_name`.
81
+ - `port_events.by_ports()`: `filter_port_name`.
82
+ `port_events.by_vessels()`: `filter_vessel_name`.
83
+ - `vessels.positions()`: `filter_ids`.
84
+ - The matching `all_*` iterators enforce the same set. 38 methods in total,
85
+ on both the sync and async clients.
86
+ - **`vessels.positions()` parameter order changed.** `filter_ids` now precedes
87
+ `filter_id_type`. Both are keyword-only, so callers are unaffected.
88
+ - `search.vessels()` and `search.ports()` are unaffected: the API marks their
89
+ `filter_name` optional, and it stays optional here.
90
+ - `filter_id_type` is unchanged. The API requires it, but the SDK sends it on
91
+ every call and it keeps its `"imo"` default.
92
+ - **Iterator signatures are now explicit.** 50 of the `all_*` / `*_all`
93
+ iterators accepted `**kwargs` and forwarded blindly. They now declare exactly
94
+ the parameters they support, so mistyped keywords raise `TypeError` instead of
95
+ being silently ignored, and type checkers and editors can see them. This one
96
+ is breaking only in a narrow case:
97
+ - Anyone passing valid keyword arguments is unaffected.
98
+ - Passing `pagination_next_token` to an iterator is now rejected at the call.
99
+ It was already broken: the iterator supplies that token itself, so it
100
+ raised `got multiple values for keyword argument` at first iteration.
101
+
102
+ **Migrating from 1.x:** any call that relied on the server rejecting a missing
103
+ bound or name now fails at the call site instead. Supply the values listed above.
104
+
105
+ ### Fixed
106
+
107
+ - **`vessels.all_positions()` could not be time-bounded.** It did not accept
108
+ `time_from` / `time_to` at all, although `positions()` supported them, so
109
+ auto-paginating a historical window was impossible without hand-rolling the
110
+ token loop.
111
+ - **`ports.inbound()` and `ports.inbound_all()` required an ETA window** they
112
+ should not have. `eta_from` and `eta_to` were mandatory since 1.2.0, although
113
+ the service treats both as optional and defaults them to now and 72 hours
114
+ ahead. Both are now optional.
115
+ - The User-Agent reported `1.1.0` regardless of the installed version.
116
+ - Source distributions previously included files beyond the package itself. The
117
+ sdist is now scoped to the library, README, licence and metadata.
118
+
119
+ ### Attribution
120
+
121
+ Emissions and casualty data: © European Union. Source: European Maritime Safety
122
+ Agency (EMSA), THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European
123
+ Marine Casualty Information Platform (EMCIP). See the README.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vessel-api-python
3
- Version: 1.3.1
4
- Summary: Python client for the Vessel Tracking API — maritime vessel tracking, port events, emissions, and navigation data.
3
+ Version: 2.0.0
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
7
7
  Project-URL: Issues, https://github.com/vessel-api/vesselapi-python/issues
@@ -10,7 +10,7 @@ Author-email: Vessel API <support@vesselapi.com>
10
10
  License-Expression: MIT
11
11
  License-File: LICENSE
12
12
  Keywords: ais,maritime,sdk,tracking,vessel,vesselapi
13
- Classifier: Development Status :: 7 - Inactive
13
+ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Programming Language :: Python :: 3
@@ -34,25 +34,12 @@ Description-Content-Type: text/markdown
34
34
 
35
35
  # vesselapi-python
36
36
 
37
- > [!WARNING]
38
- > **This version is no longer maintained.** Several endpoints wrapped by 1.x
39
- > were retired from the VesselAPI service on 10 August 2026 and now return
40
- > HTTP 410. Please upgrade:
41
- >
42
- > ```bash
43
- > pip install 'vessel-api-python>=2'
44
- > ```
45
- >
46
- > 2.x removes the retired surface, requires the parameters the API requires,
47
- > and adds unified search, repeatable filters and satellite position fallback.
48
- > See the CHANGELOG for migration notes.
49
-
50
37
  [![CI](https://github.com/vessel-api/vesselapi-python/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/vessel-api/vesselapi-python/actions/workflows/ci.yml)
51
38
  [![PyPI](https://img.shields.io/pypi/v/vessel-api-python.svg)](https://pypi.org/project/vessel-api-python/)
52
39
  [![Python](https://img.shields.io/pypi/pyversions/vessel-api-python.svg)](https://pypi.org/project/vessel-api-python/)
53
40
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
54
41
 
55
- Python client for the [Vessel Tracking API](https://vesselapi.com) — maritime vessel tracking, port events, emissions, and navigation data.
42
+ Python client for the [Vessel Tracking API](https://vesselapi.com): maritime vessel tracking, port events, emissions, and nautical infrastructure.
56
43
 
57
44
  **Resources**: [Documentation](https://vesselapi.com/docs) | [API Explorer](https://vesselapi.com/api-reference) | [Dashboard](https://dashboard.vesselapi.com) | [Contact Support](mailto:support@vesselapi.com)
58
45
 
@@ -104,15 +91,33 @@ asyncio.run(main())
104
91
 
105
92
  | Service | Methods | Description |
106
93
  |---------|---------|-------------|
107
- | `vessels` | `get`, `position`, `casualties`, `classification`, `emissions`, `eta`, `inspections`, `inspection_detail`, `ownership`, `positions` | Vessel details, positions, and records |
108
- | `ports` | `get` | Port lookup by UN/LOCODE |
94
+ | `vessels` | `get`, `position`, `casualties`, `emissions`, `eta`, `positions` | Vessel details, positions, and records |
95
+ | `ports` | `get`, `inbound`, `inbound_all` | Port lookup by UN/LOCODE and inbound vessels |
109
96
  | `port_events` | `list`, `by_port`, `by_ports`, `by_vessel`, `last_by_vessel`, `by_vessels` | Vessel arrival/departure events |
110
97
  | `emissions` | `list` | EU MRV emissions data |
111
98
  | `search` | `vessels`, `ports`, `dgps`, `light_aids`, `modus`, `radio_beacons` | Full-text search across entity types |
112
99
  | `location` | `vessels_bounding_box`, `vessels_radius`, `ports_bounding_box`, `ports_radius`, `dgps_bounding_box`, `dgps_radius`, `light_aids_bounding_box`, `light_aids_radius`, `modus_bounding_box`, `modus_radius`, `radio_beacons_bounding_box`, `radio_beacons_radius` | Geo queries by bounding box or radius |
113
- | `navtex` | `list` | NAVTEX maritime safety messages |
114
100
 
115
- **37 methods total.**
101
+ **33 methods total**, one per API endpoint, plus 28 auto-pagination iterators.
102
+
103
+ ## Required Parameters
104
+
105
+ Parameters the API requires are keyword-only with no default, so leaving one out
106
+ raises `TypeError` at the call instead of returning HTTP 400 from the server:
107
+
108
+ ```python
109
+ client.location.vessels_radius(latitude=51.9225, longitude=4.47917, radius=10000)
110
+ client.location.vessels_radius(latitude=51.9225) # TypeError: missing longitude, radius
111
+ ```
112
+
113
+ This covers the geo bounds on every `location` method (`lat_min`, `lat_max`,
114
+ `lon_min`, `lon_max` for bounding boxes; `latitude`, `longitude`, `radius` for
115
+ radius queries), `filter_name` on `search.dgps`, `search.light_aids`,
116
+ `search.modus` and `search.radio_beacons`, `filter_port_name` on
117
+ `port_events.by_ports`, `filter_vessel_name` on `port_events.by_vessels`, and
118
+ `filter_ids` on `vessels.positions`, along with the matching `all_*` iterators.
119
+ `filter_id_type` is required by the API but keeps its `"imo"` default, since the
120
+ SDK always sends it.
116
121
 
117
122
  ## Vessel Lookup & Location
118
123
 
@@ -121,16 +126,47 @@ asyncio.run(main())
121
126
  vessel = client.vessels.get("9811000")
122
127
  print(f"{vessel.vessel.name} ({vessel.vessel.vessel_type})")
123
128
 
124
- # Get the vessel's latest AIS position.
125
- pos = client.vessels.position("9811000")
129
+ # Get a vessel's latest stored AIS position. A vessel that exists may have no
130
+ # stored position, which raises VesselNotFoundError rather than returning empty.
131
+ pos = client.vessels.position("232003239", filter_id_type="mmsi")
126
132
  print(f"Position: {pos.vessel_position.latitude}, {pos.vessel_position.longitude}")
127
133
 
134
+ # filter_sat=True falls back to satellite AIS when nothing is stored.
135
+ # ⚠️ Satellite lookups draw on a prepaid balance and are charged per call.
136
+ # Omit it unless you need that fallback.
137
+
128
138
  # Find all vessels within 10 km of Rotterdam.
129
139
  nearby = client.location.vessels_radius(latitude=51.9225, longitude=4.47917, radius=10000)
130
140
  for v in nearby.vessels or []:
131
141
  print(f"{v.vessel_name} at {v.latitude}, {v.longitude}")
132
142
  ```
133
143
 
144
+ ## Search
145
+
146
+ `search.vessels` accepts `q`, a unified search across IMO, MMSI, ENI, callsign and
147
+ name. One value can return several vessels, and `_meta.matched_on` maps each result's
148
+ index to the fields it matched on.
149
+
150
+ ```python
151
+ res = client.search.vessels(q="4606770")
152
+ print(res.meta.matched_on) # {"0": ["eni"]}
153
+ ```
154
+
155
+ Filters the API declares as repeatable take either one value or a list. A list is
156
+ sent as a repeated query parameter, so the values are OR-ed:
157
+
158
+ ```python
159
+ # ?filter.flag=PA&filter.flag=LR&filter.vesselType=Container%20Ship
160
+ client.search.vessels(filter_flag=["PA", "LR"], filter_vessel_type="Container Ship")
161
+
162
+ client.search.ports(filter_country=["NL", "BE"], filter_size=["LARGE", "MEDIUM"])
163
+ ```
164
+
165
+ These are `filter_flag` and `filter_vessel_type` on `search.vessels`;
166
+ `filter_country`, `filter_port_type`, `filter_size`, `filter_harbor_size` and
167
+ `filter_harbor_use` on `search.ports`; and `filter_ids` on `vessels.positions`,
168
+ which also accepts the comma-separated single-string form.
169
+
134
170
  ## Error Handling
135
171
 
136
172
  All methods raise specific exception types on non-2xx responses:
@@ -144,7 +180,7 @@ except VesselAPIError as err:
144
180
  if err.is_not_found:
145
181
  print("Port not found")
146
182
  elif err.is_rate_limited:
147
- print("Rate limited — back off")
183
+ print("Rate limited, backing off")
148
184
  elif err.is_auth_error:
149
185
  print("Check API key")
150
186
  print(err.status_code, err.message)
@@ -163,8 +199,14 @@ for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
163
199
  async for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
164
200
  print(vessel.name)
165
201
 
166
- # Collect a bounded set at once
202
+ # Collect every match at once. pagination_limit is the page size, not a total:
203
+ # .collect() walks to the end, so this is one request per page until exhausted.
167
204
  vessels = client.search.all_vessels(filter_vessel_type="Tanker", pagination_limit=50).collect()
205
+
206
+ # To take a fixed number, stop the iterator yourself.
207
+ from itertools import islice
208
+
209
+ first_50 = list(islice(client.search.all_vessels(filter_vessel_type="Tanker"), 50))
168
210
  ```
169
211
 
170
212
  ## Configuration
@@ -183,15 +225,24 @@ Retries use exponential backoff with jitter on 429 and 5xx responses. The `Retry
183
225
 
184
226
  ## Documentation
185
227
 
186
- - [API Documentation](https://vesselapi.com/docs) — endpoint guides, request/response schemas, and usage examples
187
- - [API Explorer](https://vesselapi.com/api-reference) — interactive API reference
188
- - [Dashboard](https://dashboard.vesselapi.com) — manage API keys and monitor usage
228
+ - [API Documentation](https://vesselapi.com/docs): endpoint guides, request/response schemas, and usage examples
229
+ - [API Explorer](https://vesselapi.com/api-reference): interactive API reference
230
+ - [Dashboard](https://dashboard.vesselapi.com): manage API keys and monitor usage
189
231
 
190
232
  ## Contributing & Support
191
233
 
192
234
  Found a bug, have a feature request, or need help? You're welcome to [open an issue](https://github.com/vessel-api/vesselapi-python/issues). For API-level bugs and feature requests, please use the [main VesselAPI repository](https://github.com/vessel-api/VesselApi/issues).
193
235
 
194
- For security vulnerabilities, **do not** open a public issue — email security@vesselapi.com instead. See [SECURITY.md](SECURITY.md).
236
+ For security vulnerabilities, **do not** open a public issue. Email security@vesselapi.com instead. See [SECURITY.md](SECURITY.md).
237
+
238
+ ## Data Sources & Attribution
239
+
240
+ Emissions and casualty data: © European Union. Source: European Maritime Safety Agency
241
+ (EMSA): THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European Marine Casualty
242
+ Information Platform (EMCIP). Reused under the European Commission reuse notice
243
+ (Commission Decision 2011/833/EU), which authorises reuse for commercial and
244
+ non-commercial purposes with acknowledgement of the source. Data may be transformed and
245
+ combined; EMSA does not endorse this service.
195
246
 
196
247
  ## License
197
248
 
@@ -1,24 +1,11 @@
1
1
  # vesselapi-python
2
2
 
3
- > [!WARNING]
4
- > **This version is no longer maintained.** Several endpoints wrapped by 1.x
5
- > were retired from the VesselAPI service on 10 August 2026 and now return
6
- > HTTP 410. Please upgrade:
7
- >
8
- > ```bash
9
- > pip install 'vessel-api-python>=2'
10
- > ```
11
- >
12
- > 2.x removes the retired surface, requires the parameters the API requires,
13
- > and adds unified search, repeatable filters and satellite position fallback.
14
- > See the CHANGELOG for migration notes.
15
-
16
3
  [![CI](https://github.com/vessel-api/vesselapi-python/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/vessel-api/vesselapi-python/actions/workflows/ci.yml)
17
4
  [![PyPI](https://img.shields.io/pypi/v/vessel-api-python.svg)](https://pypi.org/project/vessel-api-python/)
18
5
  [![Python](https://img.shields.io/pypi/pyversions/vessel-api-python.svg)](https://pypi.org/project/vessel-api-python/)
19
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
20
7
 
21
- Python client for the [Vessel Tracking API](https://vesselapi.com) — maritime vessel tracking, port events, emissions, and navigation data.
8
+ Python client for the [Vessel Tracking API](https://vesselapi.com): maritime vessel tracking, port events, emissions, and nautical infrastructure.
22
9
 
23
10
  **Resources**: [Documentation](https://vesselapi.com/docs) | [API Explorer](https://vesselapi.com/api-reference) | [Dashboard](https://dashboard.vesselapi.com) | [Contact Support](mailto:support@vesselapi.com)
24
11
 
@@ -70,15 +57,33 @@ asyncio.run(main())
70
57
 
71
58
  | Service | Methods | Description |
72
59
  |---------|---------|-------------|
73
- | `vessels` | `get`, `position`, `casualties`, `classification`, `emissions`, `eta`, `inspections`, `inspection_detail`, `ownership`, `positions` | Vessel details, positions, and records |
74
- | `ports` | `get` | Port lookup by UN/LOCODE |
60
+ | `vessels` | `get`, `position`, `casualties`, `emissions`, `eta`, `positions` | Vessel details, positions, and records |
61
+ | `ports` | `get`, `inbound`, `inbound_all` | Port lookup by UN/LOCODE and inbound vessels |
75
62
  | `port_events` | `list`, `by_port`, `by_ports`, `by_vessel`, `last_by_vessel`, `by_vessels` | Vessel arrival/departure events |
76
63
  | `emissions` | `list` | EU MRV emissions data |
77
64
  | `search` | `vessels`, `ports`, `dgps`, `light_aids`, `modus`, `radio_beacons` | Full-text search across entity types |
78
65
  | `location` | `vessels_bounding_box`, `vessels_radius`, `ports_bounding_box`, `ports_radius`, `dgps_bounding_box`, `dgps_radius`, `light_aids_bounding_box`, `light_aids_radius`, `modus_bounding_box`, `modus_radius`, `radio_beacons_bounding_box`, `radio_beacons_radius` | Geo queries by bounding box or radius |
79
- | `navtex` | `list` | NAVTEX maritime safety messages |
80
66
 
81
- **37 methods total.**
67
+ **33 methods total**, one per API endpoint, plus 28 auto-pagination iterators.
68
+
69
+ ## Required Parameters
70
+
71
+ Parameters the API requires are keyword-only with no default, so leaving one out
72
+ raises `TypeError` at the call instead of returning HTTP 400 from the server:
73
+
74
+ ```python
75
+ client.location.vessels_radius(latitude=51.9225, longitude=4.47917, radius=10000)
76
+ client.location.vessels_radius(latitude=51.9225) # TypeError: missing longitude, radius
77
+ ```
78
+
79
+ This covers the geo bounds on every `location` method (`lat_min`, `lat_max`,
80
+ `lon_min`, `lon_max` for bounding boxes; `latitude`, `longitude`, `radius` for
81
+ radius queries), `filter_name` on `search.dgps`, `search.light_aids`,
82
+ `search.modus` and `search.radio_beacons`, `filter_port_name` on
83
+ `port_events.by_ports`, `filter_vessel_name` on `port_events.by_vessels`, and
84
+ `filter_ids` on `vessels.positions`, along with the matching `all_*` iterators.
85
+ `filter_id_type` is required by the API but keeps its `"imo"` default, since the
86
+ SDK always sends it.
82
87
 
83
88
  ## Vessel Lookup & Location
84
89
 
@@ -87,16 +92,47 @@ asyncio.run(main())
87
92
  vessel = client.vessels.get("9811000")
88
93
  print(f"{vessel.vessel.name} ({vessel.vessel.vessel_type})")
89
94
 
90
- # Get the vessel's latest AIS position.
91
- pos = client.vessels.position("9811000")
95
+ # Get a vessel's latest stored AIS position. A vessel that exists may have no
96
+ # stored position, which raises VesselNotFoundError rather than returning empty.
97
+ pos = client.vessels.position("232003239", filter_id_type="mmsi")
92
98
  print(f"Position: {pos.vessel_position.latitude}, {pos.vessel_position.longitude}")
93
99
 
100
+ # filter_sat=True falls back to satellite AIS when nothing is stored.
101
+ # ⚠️ Satellite lookups draw on a prepaid balance and are charged per call.
102
+ # Omit it unless you need that fallback.
103
+
94
104
  # Find all vessels within 10 km of Rotterdam.
95
105
  nearby = client.location.vessels_radius(latitude=51.9225, longitude=4.47917, radius=10000)
96
106
  for v in nearby.vessels or []:
97
107
  print(f"{v.vessel_name} at {v.latitude}, {v.longitude}")
98
108
  ```
99
109
 
110
+ ## Search
111
+
112
+ `search.vessels` accepts `q`, a unified search across IMO, MMSI, ENI, callsign and
113
+ name. One value can return several vessels, and `_meta.matched_on` maps each result's
114
+ index to the fields it matched on.
115
+
116
+ ```python
117
+ res = client.search.vessels(q="4606770")
118
+ print(res.meta.matched_on) # {"0": ["eni"]}
119
+ ```
120
+
121
+ Filters the API declares as repeatable take either one value or a list. A list is
122
+ sent as a repeated query parameter, so the values are OR-ed:
123
+
124
+ ```python
125
+ # ?filter.flag=PA&filter.flag=LR&filter.vesselType=Container%20Ship
126
+ client.search.vessels(filter_flag=["PA", "LR"], filter_vessel_type="Container Ship")
127
+
128
+ client.search.ports(filter_country=["NL", "BE"], filter_size=["LARGE", "MEDIUM"])
129
+ ```
130
+
131
+ These are `filter_flag` and `filter_vessel_type` on `search.vessels`;
132
+ `filter_country`, `filter_port_type`, `filter_size`, `filter_harbor_size` and
133
+ `filter_harbor_use` on `search.ports`; and `filter_ids` on `vessels.positions`,
134
+ which also accepts the comma-separated single-string form.
135
+
100
136
  ## Error Handling
101
137
 
102
138
  All methods raise specific exception types on non-2xx responses:
@@ -110,7 +146,7 @@ except VesselAPIError as err:
110
146
  if err.is_not_found:
111
147
  print("Port not found")
112
148
  elif err.is_rate_limited:
113
- print("Rate limited — back off")
149
+ print("Rate limited, backing off")
114
150
  elif err.is_auth_error:
115
151
  print("Check API key")
116
152
  print(err.status_code, err.message)
@@ -129,8 +165,14 @@ for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
129
165
  async for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
130
166
  print(vessel.name)
131
167
 
132
- # Collect a bounded set at once
168
+ # Collect every match at once. pagination_limit is the page size, not a total:
169
+ # .collect() walks to the end, so this is one request per page until exhausted.
133
170
  vessels = client.search.all_vessels(filter_vessel_type="Tanker", pagination_limit=50).collect()
171
+
172
+ # To take a fixed number, stop the iterator yourself.
173
+ from itertools import islice
174
+
175
+ first_50 = list(islice(client.search.all_vessels(filter_vessel_type="Tanker"), 50))
134
176
  ```
135
177
 
136
178
  ## Configuration
@@ -149,15 +191,24 @@ Retries use exponential backoff with jitter on 429 and 5xx responses. The `Retry
149
191
 
150
192
  ## Documentation
151
193
 
152
- - [API Documentation](https://vesselapi.com/docs) — endpoint guides, request/response schemas, and usage examples
153
- - [API Explorer](https://vesselapi.com/api-reference) — interactive API reference
154
- - [Dashboard](https://dashboard.vesselapi.com) — manage API keys and monitor usage
194
+ - [API Documentation](https://vesselapi.com/docs): endpoint guides, request/response schemas, and usage examples
195
+ - [API Explorer](https://vesselapi.com/api-reference): interactive API reference
196
+ - [Dashboard](https://dashboard.vesselapi.com): manage API keys and monitor usage
155
197
 
156
198
  ## Contributing & Support
157
199
 
158
200
  Found a bug, have a feature request, or need help? You're welcome to [open an issue](https://github.com/vessel-api/vesselapi-python/issues). For API-level bugs and feature requests, please use the [main VesselAPI repository](https://github.com/vessel-api/VesselApi/issues).
159
201
 
160
- For security vulnerabilities, **do not** open a public issue — email security@vesselapi.com instead. See [SECURITY.md](SECURITY.md).
202
+ For security vulnerabilities, **do not** open a public issue. Email security@vesselapi.com instead. See [SECURITY.md](SECURITY.md).
203
+
204
+ ## Data Sources & Attribution
205
+
206
+ Emissions and casualty data: © European Union. Source: European Maritime Safety Agency
207
+ (EMSA): THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European Marine Casualty
208
+ Information Platform (EMCIP). Reused under the European Commission reuse notice
209
+ (Commission Decision 2011/833/EU), which authorises reuse for commercial and
210
+ non-commercial purposes with acknowledgement of the source. Data may be transformed and
211
+ combined; EMSA does not endorse this service.
161
212
 
162
213
  ## License
163
214
 
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vessel-api-python"
7
- version = "1.3.1"
8
- description = "Python client for the Vessel Tracking API — maritime vessel tracking, port events, emissions, and navigation data."
7
+ version = "2.0.0"
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"
11
11
  license = "MIT"
@@ -14,7 +14,7 @@ authors = [
14
14
  ]
15
15
  keywords = ["vesselapi", "maritime", "vessel", "tracking", "ais", "sdk"]
16
16
  classifiers = [
17
- "Development Status :: 7 - Inactive",
17
+ "Development Status :: 5 - Production/Stable",
18
18
  "Intended Audience :: Developers",
19
19
  "License :: OSI Approved :: MIT License",
20
20
  "Programming Language :: Python :: 3",
@@ -49,6 +49,19 @@ Changelog = "https://github.com/vessel-api/vesselapi-python/releases"
49
49
  [tool.hatch.build.targets.wheel]
50
50
  packages = ["src/vessel_api_python"]
51
51
 
52
+ # Scope the sdist explicitly. Hatchling's default is "everything not gitignored",
53
+ # which pulls in the specification, the smoke tests and the CI workflows. Keep
54
+ # this list to what a consumer of the package actually needs.
55
+ [tool.hatch.build.targets.sdist]
56
+ include = [
57
+ "src/vessel_api_python",
58
+ "README.md",
59
+ "CHANGELOG.md",
60
+ "LICENSE",
61
+ "SECURITY.md",
62
+ "pyproject.toml",
63
+ ]
64
+
52
65
  [tool.ruff]
53
66
  target-version = "py39"
54
67
  line-length = 120
@@ -58,7 +71,12 @@ select = ["E", "F", "I", "W", "UP", "B", "SIM"]
58
71
  ignore = ["E501"] # Line length handled by formatter
59
72
 
60
73
  [tool.mypy]
61
- python_version = "3.9"
74
+ # 3.10 is the lowest version current mypy accepts. The package still supports
75
+ # 3.9 (see requires-python): that floor is enforced by ruff's target-version
76
+ # below, which rejects 3.10-only idioms, and verified by the 3.9 job in the CI
77
+ # matrix, which runs the full test suite. Setting 3.9 here is silently ignored
78
+ # by mypy, which then checks against whatever interpreter it happens to run on.
79
+ python_version = "3.10"
62
80
  strict = true
63
81
  warn_return_any = true
64
82
  warn_unused_configs = true