Metadata-Version: 2.4
Name: datalastic
Version: 0.1.0
Summary: Python SDK for the Datalastic Maritime API
Author: Datalastic
License: MIT License
        
        Copyright (c) 2026 Datalastic
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://datalastic.com
Project-URL: Documentation, https://datalastic.com/documentation-api/
Project-URL: Repository, https://github.com/datalastic/datalastic-python
Keywords: datalastic,maritime,vessel,ais,ports,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# Datalastic Python SDK

The official Python library for the [Datalastic](https://datalastic.com) Maritime API. This Python maritime API client covers real-time AIS vessel tracking, port search, sea route calculation, maritime intelligence data, and asynchronous report management.

## Installation

```bash
pip install datalastic
```

Requires Python 3.8 or later and the `requests` library.

## Quick start

```python
from datalastic import Client

client = Client("YOUR_API_KEY")

# Real-time position of a single vessel
vessel = client.vessels.get(mmsi="636092297")
print(vessel.name, vessel.lat, vessel.lon, vessel.speed)

# Account usage
stat = client.stat()
print(stat.requests_remaining)
```

## Authentication

Every request includes your API key as the `api-key` query parameter. For report submissions (`reports.submit`), the client places the key inside the JSON request body instead. You never need to set an HTTP header. Pass the key once when constructing the client:

```python
client = Client("YOUR_API_KEY")

# Optional: override the default 30-second timeout
client = Client("YOUR_API_KEY", timeout=10)
```

## Vessels

The Datalastic Python SDK exposes eight vessel methods. At least one of `uuid`, `mmsi`, or `imo` is required for all single-vessel lookups.

### `vessels.get` -- real-time basic tracking

```python
vessel = client.vessels.get(mmsi="636092297")
vessel = client.vessels.get(imo="9320403")
vessel = client.vessels.get(uuid="...")
```

Returns a `Vessel`.

### `vessels.pro` -- real-time pro tracking

Adds draught, departure/destination ports, ATD, and ETA fields.

```python
vessel = client.vessels.pro(mmsi="636092297")
print(vessel.current_draught, vessel.dest_port, vessel.eta_UTC)
```

Returns a `VesselPro`.

### `vessels.bulk` -- lookup many vessels at once

Pass a list or a comma-separated string for each identifier type. Identifiers are sent as repeated query parameters.

```python
result = client.vessels.bulk(mmsi=["636092297", "538005989"])
result = client.vessels.bulk(imo="9320403,9301491")
print(result.total)
for vessel in result.vessels:
    print(vessel.name)
```

Returns a `VesselBulkResult`.

### `vessels.in_radius` -- find vessels near a point, port, or vessel

`radius` (nautical miles) is required. An anchor is also required: either `lat` + `lon`, `port_uuid`, `port_unlocode`, `uuid`, `mmsi`, or `imo`. Optional filters include `type`, `type_specific`, `exclude`, `nav_status`, `next`, and `empty`.

```python
# By coordinates
near = client.vessels.in_radius(lat=51.95, lon=4.14, radius=50, type="cargo")

# By port UN/LOCODE
near = client.vessels.in_radius(port_unlocode="NLRTM", radius=20)

# Include vessels with no recent AIS position
near = client.vessels.in_radius(lat=51.95, lon=4.14, radius=50, empty=True)
```

Returns a `VesselInRadiusResult`.

### `vessels.history` -- historical track

```python
history = client.vessels.history(mmsi="636092297", days=7)
history = client.vessels.history(
    imo="9320403", from_date="2026-01-01", to_date="2026-02-01"
)
for point in history.positions:
    print(point["lat"], point["lon"])
```

`from_date` and `to_date` are sent to the API as `from` and `to`. Returns a `VesselHistory`.

### `vessels.info` -- vessel specifications

```python
info = client.vessels.info(imo="9320403")
print(info.gross_tonnage, info.deadweight, info.year_built)
```

Returns a `VesselInfo`.

### `vessels.find` -- search the vessel database

At least one real search criterion is required. `fuzzy` and `next` alone do not qualify. `vessel_type` is sent to the API as the `type` parameter. Supports the `empty` boolean to include vessels with no recent AIS data.

```python
matches = client.vessels.find(name="MAERSK", vessel_type="cargo")
matches = client.vessels.find(
    country_iso="DK",
    gross_tonnage_min=50000,
    year_built_min=2010,
)
# Include vessels not currently transmitting
matches = client.vessels.find(name="NORDIC", empty=True)
```

Returns a `list[VesselInfo]`.

### `vessels.estimated` -- SAT-E estimated position (add-on)

Uses the extended base URL. Returns the latest satellite-estimated position when terrestrial AIS is unavailable.

```python
est = client.vessels.estimated(mmsi="636092297")
print(est.estimated_position)
```

Returns a `VesselEstimated`.

## Ports

### `ports.find` -- search ports

At least one parameter is required. `lat` and `lon` must be provided together.

```python
ports = client.ports.find(name="ROTTERDAM")
ports = client.ports.find(country_iso="NL")
ports = client.ports.find(lat=51.95, lon=4.14, radius=30)
ports = client.ports.find(unlocode="NLRTM")
ports = client.ports.find(port_type="sea")
```

Returns a `list[Port]`.

### `ports.get` -- retrieve a single port with terminal detail

```python
port = client.ports.get(unlocode="NLRTM")
print(port.port_name, len(port.terminals))

port = client.ports.get(name="ROTTERDAM")
```

Returns a `PortDetail`.

## Sea routes (add-on)

### `routes.calculate`

Calculates the sea route between an origin and a destination. Each endpoint can be specified as a coordinate pair, a port UUID, or a port UN/LOCODE.

```python
# By coordinates
route = client.routes.calculate(
    lat_from=51.95, lon_from=4.14,
    lat_to=40.65, lon_to=-74.05,
)

# By UN/LOCODE
route = client.routes.calculate(
    port_unlocode_from="NLRTM",
    port_unlocode_to="USNYC",
)

print(route.from_point)
print(route.to_point)
print(route.route["total_dist"])
```

Returns a `SeaRoute`.

## Maritime intelligence (add-ons)

All intel methods call the `maritime_reports` base URL and return lists of typed records.

### `intel.dry_dock`

Dry dock schedule records. Filter by `imo`, `name`, `dry_dock_from`, or `dry_dock_to`.

```python
records = client.intel.dry_dock(imo="9320403")
for r in records:
    print(r.vessel_name, r.dry_dock_date, r.country_name)
```

Returns a `list[DryDockRecord]`.

### `intel.casualties`

Vessel casualty records. Filter by `imo`, `name`, `from_date`, or `to_date`.

```python
records = client.intel.casualties(
    imo="9320403", from_date="2025-01-01", to_date="2026-01-01"
)
```

Returns a `list[CasualtyRecord]`.

### `intel.inspections`

Port state control inspection records. Filter by `imo`, `name`, `from_date`, or `to_date`.

```python
records = client.intel.inspections(imo="9320403")
for r in records:
    print(r.inspection_date, r.inspection_authority, r.detention)
```

Returns a `list[InspectionRecord]`.

### `intel.spd`

Sale, purchase, and demolition records. Filter by `imo`, `name`, `from_date`, or `to_date`.

```python
records = client.intel.spd(imo="9320403")
for r in records:
    print(r.sales_report_date, r.buyer, r.sales_price_usd_mio)
```

Returns a `list[SPDRecord]`.

### `intel.ownership`

Beneficial ownership and management records. Filter by `imo`, `name`, `beneficial_owner`, `operator`, `technical_manager`, `commercial_manager`, or `updated_from`.

```python
records = client.intel.ownership(beneficial_owner="MAERSK")
for r in records:
    print(r.vessel_name, r.beneficial_owner_country, r.technical_manager)
```

Returns a `list[OwnershipRecord]`.

### `intel.class_society`

Classification society records. Filter by `imo`, `name`, `fuzzy`, `beneficial_owner`, `beneficial_owner_imo`, `technical_manager`, `technical_manager_imo`, or `updated_from`.

```python
records = client.intel.class_society(imo="9320403")
for r in records:
    print(r.class1_code, r.special_survey_date)
```

Returns a `list[ClassSocietyRecord]`.

### `intel.engine`

Engine and propulsion records. Filter by `imo`, `name`, `fuzzy`, or `updated_from`.

```python
records = client.intel.engine(imo="9320403")
for r in records:
    print(r.engine_designation, r.engine_builder, r.mco, r.mco_unit)
```

Returns a `list[EngineRecord]`.

### `intel.companies`

Maritime company records. Filter by `company_imo`, `name`, or `updated_from`.

```python
records = client.intel.companies(company_imo="0123456")
for r in records:
    print(r.short_name, r.company_type, r.country_code)
```

Returns a `list[CompanyRecord]`.

## Reports

Reports are asynchronous. Submit a request, then poll by ID until the status is `"done"`.

### `reports.submit`

The API key is placed in the JSON body by the client. Pass extra report parameters as keyword arguments.

```python
report = client.reports.submit(
    "inradius_history",
    port_uuid="...",
    days=30,
)
print(report.report_id, report.status)
```

Valid report types:

| Type | Description |
|---|---|
| `inradius_history` | Historical positions within a radius |
| `vessel_list` | Filtered vessel list |
| `port_list` | Filtered port list |
| `request_usage` | Account request usage |
| `dry_dock_dates` | Dry dock schedule export |
| `casualty` | Casualty data export |
| `inspections` | PSC inspection data export |
| `sales_purchase_demolitions` | SPD transactions export |
| `ownership` | Ownership data export |
| `class_society` | Classification society export |
| `engine` | Engine data export |
| `companies` | Company data export |

Returns a `Report`.

### `reports.get`

Poll the status of a single report.

```python
report = client.reports.get(report.report_id)
if report.status == "done":
    print(report.result_url)
```

Returns a `Report`.

### `reports.list_all`

List every report associated with the API key.

```python
for r in client.reports.list_all():
    print(r.report_id, r.report_type, r.status)
```

Returns a `list[Report]`.

## Error handling

All exceptions inherit from `DatalasticError`.

```python
from datalastic import (
    Client,
    DatalasticError,
    AuthenticationError,
    InsufficientCreditsError,
    NotFoundError,
    RateLimitError,
    APIError,
)

client = Client("YOUR_API_KEY")

try:
    vessel = client.vessels.get(mmsi="636092297")
except AuthenticationError:
    print("Invalid or expired API key")
except InsufficientCreditsError:
    print("Account credits exhausted")
except NotFoundError:
    print("Vessel not found")
except RateLimitError:
    print("Rate limit exceeded")
except APIError as exc:
    print("API error", exc.status_code, exc)
except DatalasticError:
    print("Other SDK error")
```

| Exception | HTTP status | When raised |
|---|---|---|
| `AuthenticationError` | 401 | Invalid or expired API key |
| `InsufficientCreditsError` | 402 | No credits remaining |
| `NotFoundError` | 404 | Resource does not exist |
| `RateLimitError` | 429 | More than 600 requests per minute |
| `APIError` | other / timeout | Any other error; carries `status_code` |
| `DatalasticError` | -- | Base class for all of the above |

Input validation errors (`ValueError`) are raised before any HTTP request is made, so they are never wrapped in a `DatalasticError`.

Every model exposes the raw API payload as `.raw` for fields not surfaced as named attributes.

## Model reference

### Vessel

| Field | Type | Notes |
|---|---|---|
| `uuid` | str | Datalastic vessel UUID |
| `name` | str | Vessel name |
| `mmsi` | str | MMSI identifier |
| `imo` | str | IMO number |
| `eni` | str | ENI number (inland waterways) |
| `country_iso` | str | Flag state ISO code |
| `type` | str | Vessel type |
| `type_specific` | str | Specific vessel type |
| `lat` | float | Last known latitude |
| `lon` | float | Last known longitude |
| `speed` | float | Speed over ground (knots) |
| `course` | float | Course over ground (degrees) |
| `navigation_status` | str | AIS navigational status |
| `heading` | float | True heading (degrees) |
| `destination` | str | AIS-reported destination |
| `last_position_epoch` | int | Unix timestamp of last AIS fix |
| `last_position_UTC` | str | UTC datetime of last AIS fix |
| `eta_epoch` | int | ETA as Unix timestamp |
| `eta_UTC` | str | ETA as UTC datetime string |
| `raw` | dict | Full API response payload |

### VesselPro

All `Vessel` fields plus:

| Field | Type | Notes |
|---|---|---|
| `current_draught` | float | Current draught (metres) |
| `dest_port` | str | Destination port name |
| `dest_port_unlocode` | str | Destination port UN/LOCODE |
| `dep_port` | str | Last departure port name |
| `dep_port_unlocode` | str | Last departure port UN/LOCODE |
| `atd_epoch` | int | Actual time of departure as Unix timestamp |
| `atd_UTC` | str | Actual time of departure as UTC string |

### VesselEstimated

All `VesselPro` fields plus:

| Field | Notes |
|---|---|
| `estimated_position` | SAT-E estimated position data |

### VesselBulkResult

| Field | Notes |
|---|---|
| `total` | Count of matching vessels |
| `vessels` | `list[Vessel]` |
| `raw` | Full API response payload |

### VesselInRadiusResult

| Field | Notes |
|---|---|
| `point` | The anchor point used for the search |
| `total` | Count of vessels found |
| `vessels` | `list[Vessel]` (each vessel's `.raw` includes `distance`) |
| `raw` | Full API response payload |

### VesselHistory

`uuid`, `name`, `mmsi`, `imo`, `eni`, `country_iso`, `type`, `type_specific`, `positions` (list of position dicts), `raw`

### VesselInfo

`uuid`, `name`, `name_ais`, `mmsi`, `imo`, `eni`, `country_iso`, `country_name`, `callsign`, `type`, `type_specific`, `gross_tonnage`, `deadweight`, `teu`, `liquid_gas`, `length`, `breadth`, `draught_avg`, `draught_max`, `speed_avg`, `speed_max`, `year_built`, `is_navaid`, `home_port`, `raw`

### Port

`uuid`, `port_name`, `country_iso`, `country_name`, `unlocode`, `port_type`, `lat`, `lon`, `area_lvl1`, `area_lvl2`, `raw`

### PortDetail

All `Port` fields plus `terminals` (list).

### SeaRoute

| Field | Notes |
|---|---|
| `from_point` | Origin point data from the API |
| `route` | Route details dict (includes `total_dist`) |
| `to_point` | Destination point data from the API |
| `raw` | Full API response payload |

### ApiStat

`user_id`, `key_status`, `requests_made`, `requests_remaining`, `raw`

### Report

`report_id`, `report_type`, `status`, `result_url`, `created_at`, `updated_at`, `params`, `raw`

### DryDockRecord

`id`, `imo`, `vessel_name`, `special_survey_date`, `dry_dock_date`, `iopp_issue_date`, `iopp_exp_date`, `technical_manager`, `country_code`, `country_name`, `website`, `email`, `phone`, `address`, `linkedin`, `modified_at`, `raw`

### CasualtyRecord

`id`, `imo`, `vessel_name`, `casualty_date`, `casualty_type`, `casualty_details`, `modified_at`, `raw`

### InspectionRecord

`id`, `imo`, `vessel_name`, `vessel_type_code`, `flag_code`, `inspection_date`, `inspection_authority`, `inspection_port`, `inspection_type`, `detention`, `ship_deficiencies`, `deficiency_description`, `technical_ism_manager`, `country_code`, `website`, `email`, `phone`, `address`, `company_imo`, `modified_at`, `raw`

### SPDRecord

`id`, `sales_report_date`, `imo`, `vessel_name`, `flag_name`, `vessel_type_code`, `built_year`, `dwt_design`, `gt`, `ldt`, `seller`, `buyer`, `sales_price_usd_mio`, `sales_price_usd_per_ldt`, `destination`, `sales_type`, `dry_dock_date`, `special_survey_date`, `sales_note`, `previous_sales_record`, `modified_at`, `raw`

### OwnershipRecord

`id`, `imo`, `vessel_name`, `beneficial_owner`, `beneficial_owner_country`, `operator`, `operator_country`, `flag_name`, `vessel_type_code`, `built_year`, `buyer`, `dwt_design`, `class1_code`, `technical_manager`, `technical_manager_country`, `commercial_manager`, `commercial_manager_country`, `modified_at`, `raw`

### ClassSocietyRecord

`imo`, `vessel_name`, `vessel_type_code`, `flag_name`, `built_year`, `dwt_design`, `special_survey_date`, `dry_dock_date`, `class1_code`, `beneficial_owner_imo`, `beneficial_owner`, `technical_manager_imo`, `technical_manager`, `draft_design`, `nt`, `gt`, `loa`, `lbp`, `depth`, `beam_moduled`, `engine_builder`, `engine_designer`, `propulsion_type_code`, `modified_at`, `raw`

### EngineRecord

`imo`, `vessel_name`, `vessel_type_code`, `propulsion_type_code`, `mco`, `mco_unit`, `mco_rpm`, `trading_category_code`, `built_year`, `gt`, `engine_designation`, `engine_builder`, `engine_designer`, `modified_at`, `raw`

### CompanyRecord

`id`, `short_name`, `long_name`, `company_type`, `country_code`, `company_imo`, `website`, `company_status`, `email`, `phone`, `address`, `linkedin`, `parent_company_imo`, `parent_company_name`, `modified_at`, `raw`

## License

MIT. See [LICENSE](LICENSE).
