vessel-api-python 1.3.1__tar.gz → 2.0.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.
- vessel_api_python-2.0.1/CHANGELOG.md +138 -0
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/PKG-INFO +82 -30
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/README.md +79 -27
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/pyproject.toml +22 -4
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/__init__.py +9 -75
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/_client.py +0 -4
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/_constants.py +1 -1
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/_errors.py +49 -1
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/_models.py +50 -396
- vessel_api_python-2.0.1/src/vessel_api_python/_services.py +3019 -0
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/_transport.py +5 -2
- vessel_api_python-1.3.1/.github/workflows/ci.yml +0 -46
- vessel_api_python-1.3.1/.github/workflows/publish.yml +0 -24
- vessel_api_python-1.3.1/Makefile +0 -21
- vessel_api_python-1.3.1/examples/async_basic.py +0 -67
- vessel_api_python-1.3.1/examples/basic.py +0 -86
- vessel_api_python-1.3.1/openapi/swagger.json +0 -5779
- vessel_api_python-1.3.1/src/vessel_api_python/_services.py +0 -1225
- vessel_api_python-1.3.1/tests/__init__.py +0 -0
- vessel_api_python-1.3.1/tests/conftest.py +0 -34
- vessel_api_python-1.3.1/tests/test_client.py +0 -102
- vessel_api_python-1.3.1/tests/test_errors.py +0 -136
- vessel_api_python-1.3.1/tests/test_iterator.py +0 -136
- vessel_api_python-1.3.1/tests/test_services.py +0 -177
- vessel_api_python-1.3.1/tests/test_smoke.py +0 -601
- vessel_api_python-1.3.1/tests/test_transport.py +0 -215
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/.gitignore +0 -0
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/LICENSE +0 -0
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/SECURITY.md +0 -0
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/_iterator.py +0 -0
- {vessel_api_python-1.3.1 → vessel_api_python-2.0.1}/src/vessel_api_python/py.typed +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
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.1] - 2026-10-08
|
|
7
|
+
|
|
8
|
+
Documentation only. No method, parameter or model changed.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- The bundled OpenAPI spec named the satellite fallback parameter `sat`; the
|
|
13
|
+
API's parameter is `filter.sat`. Descriptions corrected.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- README: the satellite fallback note says when it fires (no recent terrestrial
|
|
18
|
+
position stored); the radius example describes the time window it searches
|
|
19
|
+
(default 2 hours, at most 4).
|
|
20
|
+
|
|
21
|
+
## [2.0.0] - 2026-08-16
|
|
22
|
+
|
|
23
|
+
Vessel ownership, classification and inspection data, and NAVTEX messages, were
|
|
24
|
+
retired from the VesselAPI service on **10 August 2026**. This release removes
|
|
25
|
+
the corresponding client surface and brings the SDK back in line with the API.
|
|
26
|
+
|
|
27
|
+
Every change below applies to both the sync and async clients.
|
|
28
|
+
|
|
29
|
+
### Removed (breaking)
|
|
30
|
+
|
|
31
|
+
These endpoints no longer exist. Calling them returned HTTP 410 from 10 August
|
|
32
|
+
2026 onward; the methods are now gone so the failure surfaces at import or call
|
|
33
|
+
time rather than as a runtime error.
|
|
34
|
+
|
|
35
|
+
| Removed | Replacement |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `vessels.classification()` | none |
|
|
38
|
+
| `vessels.inspections()` | none |
|
|
39
|
+
| `vessels.inspection_detail()` | none |
|
|
40
|
+
| `vessels.ownership()` | none |
|
|
41
|
+
| `client.navtex` (whole service: `list()`, `list_all()`) | none |
|
|
42
|
+
|
|
43
|
+
Also removed:
|
|
44
|
+
|
|
45
|
+
- **Search filters** `filter_class_society` and `filter_owner` on
|
|
46
|
+
`search.vessels()`. The API began rejecting both with HTTP 400, so any call
|
|
47
|
+
passing them was already failing.
|
|
48
|
+
- **Vessel fields** `builder`, `class_society`, `owner_name`, `manager_name`.
|
|
49
|
+
- **Models** for the removed endpoints (26 classes), plus the four
|
|
50
|
+
backwards-compatibility aliases `Classification`, `Inspection`,
|
|
51
|
+
`InspectionDetail` and `Ownership`: 30 public names in total.
|
|
52
|
+
|
|
53
|
+
**Migrating from 1.x:** remove calls to the methods above. There is no
|
|
54
|
+
alternative source for that data through this API. Drop `filter_class_society`
|
|
55
|
+
and `filter_owner` from any search call. If you read `owner_name`,
|
|
56
|
+
`manager_name`, `builder` or `class_society` from a vessel, those attributes are
|
|
57
|
+
gone.
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **Search**: `q` (free-text across name, IMO, MMSI, ENI and callsign) and
|
|
62
|
+
`filter_eni` on `search.vessels()`.
|
|
63
|
+
- **Repeatable filters.** `filter_flag` and `filter_vessel_type` on
|
|
64
|
+
`search.vessels()`, and `filter_country`, `filter_port_type`, `filter_size`,
|
|
65
|
+
`filter_harbor_size` and `filter_harbor_use` on `search.ports()`, now accept
|
|
66
|
+
`str | list[str]` (exported as `MultiFilter`). A list matches any of the
|
|
67
|
+
values. Passing a plain string behaves exactly as before.
|
|
68
|
+
- **`filter_sat`** on `vessels.position()`. ⚠️ Satellite lookups draw on a
|
|
69
|
+
prepaid balance and are charged per call. Omit it to serve stored positions.
|
|
70
|
+
- **`time_from` / `time_to`** on `port_events.by_port()`, `by_ports()`,
|
|
71
|
+
`by_vessels()` and their iterators.
|
|
72
|
+
- **Vessel fields**: `eni`, `teu`, `vessel_subtype`, `name_ais`,
|
|
73
|
+
`summer_draught`, `draught_calculated_avg`, `draught_observed_max`,
|
|
74
|
+
`speed_calculated_avg`, `speed_observed_max`. All are declared by the API and
|
|
75
|
+
were previously discarded by the models. Each is optional, and how often one
|
|
76
|
+
is populated varies by field and by vessel.
|
|
77
|
+
- **Search metadata**: `FindVesselsResponse.meta` (`matched_on`, `query`) via
|
|
78
|
+
the new `VesselSearchMeta` model, and `ResolutionMeta.suggested_id_type`, a
|
|
79
|
+
hint to retry with the other identifier type when a lookup misses.
|
|
80
|
+
- **Error handling**: `VesselPaymentRequiredError` (402, satellite credits
|
|
81
|
+
exhausted) and `VesselForbiddenError` (403, key suspended or feature not on
|
|
82
|
+
your plan), plus `is_payment_required` / `is_forbidden` properties. All errors
|
|
83
|
+
now expose the API's machine-readable `code` and `error_type`. Branch on
|
|
84
|
+
those rather than on `message`, which may be reworded.
|
|
85
|
+
|
|
86
|
+
### Changed (breaking)
|
|
87
|
+
|
|
88
|
+
- **Parameters the API requires are now required in Python too.** They are
|
|
89
|
+
keyword-only with no default, so omitting one raises `TypeError` at the call
|
|
90
|
+
instead of returning HTTP 400 from the server. Previously they defaulted to
|
|
91
|
+
`None`, which type-checked cleanly and then failed at runtime.
|
|
92
|
+
- `location.*_bounding_box()`: `lat_min`, `lat_max`, `lon_min`, `lon_max`.
|
|
93
|
+
- `location.*_radius()`: `latitude`, `longitude`, `radius`.
|
|
94
|
+
- `search.dgps()`, `search.light_aids()`, `search.modus()`,
|
|
95
|
+
`search.radio_beacons()`: `filter_name`.
|
|
96
|
+
- `port_events.by_ports()`: `filter_port_name`.
|
|
97
|
+
`port_events.by_vessels()`: `filter_vessel_name`.
|
|
98
|
+
- `vessels.positions()`: `filter_ids`.
|
|
99
|
+
- The matching `all_*` iterators enforce the same set. 38 methods in total,
|
|
100
|
+
on both the sync and async clients.
|
|
101
|
+
- **`vessels.positions()` parameter order changed.** `filter_ids` now precedes
|
|
102
|
+
`filter_id_type`. Both are keyword-only, so callers are unaffected.
|
|
103
|
+
- `search.vessels()` and `search.ports()` are unaffected: the API marks their
|
|
104
|
+
`filter_name` optional, and it stays optional here.
|
|
105
|
+
- `filter_id_type` is unchanged. The API requires it, but the SDK sends it on
|
|
106
|
+
every call and it keeps its `"imo"` default.
|
|
107
|
+
- **Iterator signatures are now explicit.** 50 of the `all_*` / `*_all`
|
|
108
|
+
iterators accepted `**kwargs` and forwarded blindly. They now declare exactly
|
|
109
|
+
the parameters they support, so mistyped keywords raise `TypeError` instead of
|
|
110
|
+
being silently ignored, and type checkers and editors can see them. This one
|
|
111
|
+
is breaking only in a narrow case:
|
|
112
|
+
- Anyone passing valid keyword arguments is unaffected.
|
|
113
|
+
- Passing `pagination_next_token` to an iterator is now rejected at the call.
|
|
114
|
+
It was already broken: the iterator supplies that token itself, so it
|
|
115
|
+
raised `got multiple values for keyword argument` at first iteration.
|
|
116
|
+
|
|
117
|
+
**Migrating from 1.x:** any call that relied on the server rejecting a missing
|
|
118
|
+
bound or name now fails at the call site instead. Supply the values listed above.
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
|
|
122
|
+
- **`vessels.all_positions()` could not be time-bounded.** It did not accept
|
|
123
|
+
`time_from` / `time_to` at all, although `positions()` supported them, so
|
|
124
|
+
auto-paginating a historical window was impossible without hand-rolling the
|
|
125
|
+
token loop.
|
|
126
|
+
- **`ports.inbound()` and `ports.inbound_all()` required an ETA window** they
|
|
127
|
+
should not have. `eta_from` and `eta_to` were mandatory since 1.2.0, although
|
|
128
|
+
the service treats both as optional and defaults them to now and 72 hours
|
|
129
|
+
ahead. Both are now optional.
|
|
130
|
+
- The User-Agent reported `1.1.0` regardless of the installed version.
|
|
131
|
+
- Source distributions previously included files beyond the package itself. The
|
|
132
|
+
sdist is now scoped to the library, README, licence and metadata.
|
|
133
|
+
|
|
134
|
+
### Attribution
|
|
135
|
+
|
|
136
|
+
Emissions and casualty data: © European Union. Source: European Maritime Safety
|
|
137
|
+
Agency (EMSA), THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European
|
|
138
|
+
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:
|
|
4
|
-
Summary: Python client for the Vessel Tracking API
|
|
3
|
+
Version: 2.0.1
|
|
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 ::
|
|
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
|
[](https://github.com/vessel-api/vesselapi-python/actions/workflows/ci.yml)
|
|
51
38
|
[](https://pypi.org/project/vessel-api-python/)
|
|
52
39
|
[](https://pypi.org/project/vessel-api-python/)
|
|
53
40
|
[](LICENSE)
|
|
54
41
|
|
|
55
|
-
Python client for the [Vessel Tracking API](https://vesselapi.com)
|
|
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`, `
|
|
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
|
-
**
|
|
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,48 @@ 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
|
|
125
|
-
|
|
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
|
|
|
128
|
-
#
|
|
134
|
+
# filter_sat=True falls back to satellite AIS when no recent terrestrial position is stored.
|
|
135
|
+
# ⚠️ Satellite lookups draw on a prepaid balance and are charged per call.
|
|
136
|
+
# Omit it unless you need that fallback.
|
|
137
|
+
|
|
138
|
+
# Find vessels with an AIS position within 10 km of Rotterdam in the last 2 hours
|
|
139
|
+
# (the default time window, at most 4 hours).
|
|
129
140
|
nearby = client.location.vessels_radius(latitude=51.9225, longitude=4.47917, radius=10000)
|
|
130
141
|
for v in nearby.vessels or []:
|
|
131
142
|
print(f"{v.vessel_name} at {v.latitude}, {v.longitude}")
|
|
132
143
|
```
|
|
133
144
|
|
|
145
|
+
## Search
|
|
146
|
+
|
|
147
|
+
`search.vessels` accepts `q`, a unified search across IMO, MMSI, ENI, callsign and
|
|
148
|
+
name. One value can return several vessels, and `_meta.matched_on` maps each result's
|
|
149
|
+
index to the fields it matched on.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
res = client.search.vessels(q="4606770")
|
|
153
|
+
print(res.meta.matched_on) # {"0": ["eni"]}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Filters the API declares as repeatable take either one value or a list. A list is
|
|
157
|
+
sent as a repeated query parameter, so the values are OR-ed:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
# ?filter.flag=PA&filter.flag=LR&filter.vesselType=Container%20Ship
|
|
161
|
+
client.search.vessels(filter_flag=["PA", "LR"], filter_vessel_type="Container Ship")
|
|
162
|
+
|
|
163
|
+
client.search.ports(filter_country=["NL", "BE"], filter_size=["LARGE", "MEDIUM"])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
These are `filter_flag` and `filter_vessel_type` on `search.vessels`;
|
|
167
|
+
`filter_country`, `filter_port_type`, `filter_size`, `filter_harbor_size` and
|
|
168
|
+
`filter_harbor_use` on `search.ports`; and `filter_ids` on `vessels.positions`,
|
|
169
|
+
which also accepts the comma-separated single-string form.
|
|
170
|
+
|
|
134
171
|
## Error Handling
|
|
135
172
|
|
|
136
173
|
All methods raise specific exception types on non-2xx responses:
|
|
@@ -144,7 +181,7 @@ except VesselAPIError as err:
|
|
|
144
181
|
if err.is_not_found:
|
|
145
182
|
print("Port not found")
|
|
146
183
|
elif err.is_rate_limited:
|
|
147
|
-
print("Rate limited
|
|
184
|
+
print("Rate limited, backing off")
|
|
148
185
|
elif err.is_auth_error:
|
|
149
186
|
print("Check API key")
|
|
150
187
|
print(err.status_code, err.message)
|
|
@@ -163,8 +200,14 @@ for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
|
|
|
163
200
|
async for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
|
|
164
201
|
print(vessel.name)
|
|
165
202
|
|
|
166
|
-
# Collect
|
|
203
|
+
# Collect every match at once. pagination_limit is the page size, not a total:
|
|
204
|
+
# .collect() walks to the end, so this is one request per page until exhausted.
|
|
167
205
|
vessels = client.search.all_vessels(filter_vessel_type="Tanker", pagination_limit=50).collect()
|
|
206
|
+
|
|
207
|
+
# To take a fixed number, stop the iterator yourself.
|
|
208
|
+
from itertools import islice
|
|
209
|
+
|
|
210
|
+
first_50 = list(islice(client.search.all_vessels(filter_vessel_type="Tanker"), 50))
|
|
168
211
|
```
|
|
169
212
|
|
|
170
213
|
## Configuration
|
|
@@ -183,15 +226,24 @@ Retries use exponential backoff with jitter on 429 and 5xx responses. The `Retry
|
|
|
183
226
|
|
|
184
227
|
## Documentation
|
|
185
228
|
|
|
186
|
-
- [API Documentation](https://vesselapi.com/docs)
|
|
187
|
-
- [API Explorer](https://vesselapi.com/api-reference)
|
|
188
|
-
- [Dashboard](https://dashboard.vesselapi.com)
|
|
229
|
+
- [API Documentation](https://vesselapi.com/docs): endpoint guides, request/response schemas, and usage examples
|
|
230
|
+
- [API Explorer](https://vesselapi.com/api-reference): interactive API reference
|
|
231
|
+
- [Dashboard](https://dashboard.vesselapi.com): manage API keys and monitor usage
|
|
189
232
|
|
|
190
233
|
## Contributing & Support
|
|
191
234
|
|
|
192
235
|
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
236
|
|
|
194
|
-
For security vulnerabilities, **do not** open a public issue
|
|
237
|
+
For security vulnerabilities, **do not** open a public issue. Email security@vesselapi.com instead. See [SECURITY.md](SECURITY.md).
|
|
238
|
+
|
|
239
|
+
## Data Sources & Attribution
|
|
240
|
+
|
|
241
|
+
Emissions and casualty data: © European Union. Source: European Maritime Safety Agency
|
|
242
|
+
(EMSA): THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European Marine Casualty
|
|
243
|
+
Information Platform (EMCIP). Reused under the European Commission reuse notice
|
|
244
|
+
(Commission Decision 2011/833/EU), which authorises reuse for commercial and
|
|
245
|
+
non-commercial purposes with acknowledgement of the source. Data may be transformed and
|
|
246
|
+
combined; EMSA does not endorse this service.
|
|
195
247
|
|
|
196
248
|
## License
|
|
197
249
|
|
|
@@ -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
|
[](https://github.com/vessel-api/vesselapi-python/actions/workflows/ci.yml)
|
|
17
4
|
[](https://pypi.org/project/vessel-api-python/)
|
|
18
5
|
[](https://pypi.org/project/vessel-api-python/)
|
|
19
6
|
[](LICENSE)
|
|
20
7
|
|
|
21
|
-
Python client for the [Vessel Tracking API](https://vesselapi.com)
|
|
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`, `
|
|
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
|
-
**
|
|
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,48 @@ 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
|
|
91
|
-
|
|
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
|
|
|
94
|
-
#
|
|
100
|
+
# filter_sat=True falls back to satellite AIS when no recent terrestrial position is stored.
|
|
101
|
+
# ⚠️ Satellite lookups draw on a prepaid balance and are charged per call.
|
|
102
|
+
# Omit it unless you need that fallback.
|
|
103
|
+
|
|
104
|
+
# Find vessels with an AIS position within 10 km of Rotterdam in the last 2 hours
|
|
105
|
+
# (the default time window, at most 4 hours).
|
|
95
106
|
nearby = client.location.vessels_radius(latitude=51.9225, longitude=4.47917, radius=10000)
|
|
96
107
|
for v in nearby.vessels or []:
|
|
97
108
|
print(f"{v.vessel_name} at {v.latitude}, {v.longitude}")
|
|
98
109
|
```
|
|
99
110
|
|
|
111
|
+
## Search
|
|
112
|
+
|
|
113
|
+
`search.vessels` accepts `q`, a unified search across IMO, MMSI, ENI, callsign and
|
|
114
|
+
name. One value can return several vessels, and `_meta.matched_on` maps each result's
|
|
115
|
+
index to the fields it matched on.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
res = client.search.vessels(q="4606770")
|
|
119
|
+
print(res.meta.matched_on) # {"0": ["eni"]}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Filters the API declares as repeatable take either one value or a list. A list is
|
|
123
|
+
sent as a repeated query parameter, so the values are OR-ed:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# ?filter.flag=PA&filter.flag=LR&filter.vesselType=Container%20Ship
|
|
127
|
+
client.search.vessels(filter_flag=["PA", "LR"], filter_vessel_type="Container Ship")
|
|
128
|
+
|
|
129
|
+
client.search.ports(filter_country=["NL", "BE"], filter_size=["LARGE", "MEDIUM"])
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
These are `filter_flag` and `filter_vessel_type` on `search.vessels`;
|
|
133
|
+
`filter_country`, `filter_port_type`, `filter_size`, `filter_harbor_size` and
|
|
134
|
+
`filter_harbor_use` on `search.ports`; and `filter_ids` on `vessels.positions`,
|
|
135
|
+
which also accepts the comma-separated single-string form.
|
|
136
|
+
|
|
100
137
|
## Error Handling
|
|
101
138
|
|
|
102
139
|
All methods raise specific exception types on non-2xx responses:
|
|
@@ -110,7 +147,7 @@ except VesselAPIError as err:
|
|
|
110
147
|
if err.is_not_found:
|
|
111
148
|
print("Port not found")
|
|
112
149
|
elif err.is_rate_limited:
|
|
113
|
-
print("Rate limited
|
|
150
|
+
print("Rate limited, backing off")
|
|
114
151
|
elif err.is_auth_error:
|
|
115
152
|
print("Check API key")
|
|
116
153
|
print(err.status_code, err.message)
|
|
@@ -129,8 +166,14 @@ for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
|
|
|
129
166
|
async for vessel in client.search.all_vessels(filter_vessel_type="Tanker"):
|
|
130
167
|
print(vessel.name)
|
|
131
168
|
|
|
132
|
-
# Collect
|
|
169
|
+
# Collect every match at once. pagination_limit is the page size, not a total:
|
|
170
|
+
# .collect() walks to the end, so this is one request per page until exhausted.
|
|
133
171
|
vessels = client.search.all_vessels(filter_vessel_type="Tanker", pagination_limit=50).collect()
|
|
172
|
+
|
|
173
|
+
# To take a fixed number, stop the iterator yourself.
|
|
174
|
+
from itertools import islice
|
|
175
|
+
|
|
176
|
+
first_50 = list(islice(client.search.all_vessels(filter_vessel_type="Tanker"), 50))
|
|
134
177
|
```
|
|
135
178
|
|
|
136
179
|
## Configuration
|
|
@@ -149,15 +192,24 @@ Retries use exponential backoff with jitter on 429 and 5xx responses. The `Retry
|
|
|
149
192
|
|
|
150
193
|
## Documentation
|
|
151
194
|
|
|
152
|
-
- [API Documentation](https://vesselapi.com/docs)
|
|
153
|
-
- [API Explorer](https://vesselapi.com/api-reference)
|
|
154
|
-
- [Dashboard](https://dashboard.vesselapi.com)
|
|
195
|
+
- [API Documentation](https://vesselapi.com/docs): endpoint guides, request/response schemas, and usage examples
|
|
196
|
+
- [API Explorer](https://vesselapi.com/api-reference): interactive API reference
|
|
197
|
+
- [Dashboard](https://dashboard.vesselapi.com): manage API keys and monitor usage
|
|
155
198
|
|
|
156
199
|
## Contributing & Support
|
|
157
200
|
|
|
158
201
|
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
202
|
|
|
160
|
-
For security vulnerabilities, **do not** open a public issue
|
|
203
|
+
For security vulnerabilities, **do not** open a public issue. Email security@vesselapi.com instead. See [SECURITY.md](SECURITY.md).
|
|
204
|
+
|
|
205
|
+
## Data Sources & Attribution
|
|
206
|
+
|
|
207
|
+
Emissions and casualty data: © European Union. Source: European Maritime Safety Agency
|
|
208
|
+
(EMSA): THETIS-MRV (EU MRV, Regulation (EU) 2015/757) and the European Marine Casualty
|
|
209
|
+
Information Platform (EMCIP). Reused under the European Commission reuse notice
|
|
210
|
+
(Commission Decision 2011/833/EU), which authorises reuse for commercial and
|
|
211
|
+
non-commercial purposes with acknowledgement of the source. Data may be transformed and
|
|
212
|
+
combined; EMSA does not endorse this service.
|
|
161
213
|
|
|
162
214
|
## License
|
|
163
215
|
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vessel-api-python"
|
|
7
|
-
version = "
|
|
8
|
-
description = "Python client for the Vessel Tracking API
|
|
7
|
+
version = "2.0.1"
|
|
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 ::
|
|
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
|
-
|
|
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
|