Integrate real-time maritime lineup data into your systems using our REST API.
All API requests require an X-API-Key header. You can find your API key in the API Keys page.
HTTP Header
X-API-Key: ps_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxReturns lineup records from your subscribed feeds. Supports filtering and cursor-based pagination.
| Parameter | Type | Description |
|---|---|---|
| source | string | "all" (default) or a specific feed UUID |
| port | string | UN/LOCODE to filter by port (e.g. FRLEH) |
| country | string | Country name to filter by, case-insensitive (e.g. France, Brazil) |
| port_name | string | Port name to filter by, case-insensitive partial match (e.g. Rouen) |
| from | string | Start date (YYYY-MM-DD) for ETA/ETB/ETD filter |
| to | string | End date (YYYY-MM-DD) for ETA/ETB/ETD filter |
| latest | boolean | If true, return only the most recent record per vessel+port |
| commodity | string | Comma-separated list of commodity values. Each value may be a normalized leaf, group, or subgroup name (e.g. "Crude,Containers,VLCC dirty"). Case-insensitive; matched against commodity_normalized, commodity_normalized_group, and commodity_normalized_subgroup. |
| merge | string | Controls how near-duplicate records are collapsed. Default "all" merges across every feed you subscribe to (same vessel call reported by multiple sources collapses to one row; source_id and source_name join with ";" alphabetically, deduped). "by_source" merges only within each feed (same vessel from two feeds stays as two rows). "none" returns raw pass-through, no merging. Merge key = vessel + port + commodity (vessel = IMO when present, else normalized name; commodity = signalCommodityTreeId when resolved, else normalized raw string). ±7d transitive ETA window. Split on disagreeing shipper / receiver / charterer. Latest-updatedAt wins for other fields; blank values (null, "", "N/A", "TBC", "TBN") fall back to the most recent non-blank sibling. Date filters from/to apply AFTER merge. |
| limit | number | Page size (default: 100, max: 500) |
| cursor | string | Opaque pagination cursor from previous response |
| Field | Type | Description |
|---|---|---|
| id | string | Unique record identifier (UUID) |
| source_id | string | Source feed identifier (UUID) |
| source_name | string | Source display name (or anonymous ID) |
| port_unlocode | string | Port UN/LOCODE |
| port_name | string | Port name |
| country_name | string | Full country name |
| berth_terminal | string | Berth or terminal name |
| vessel_imo | string | IMO number |
| vessel_name | string | Vessel name |
| vessel_type | string | Vessel type (Bulk Carrier, Tanker, etc.) |
| dwt | int | Deadweight tonnage |
| operation | string | load, disch, bunker, husbandry |
| commodity | string | Commodity as captured from the source (scraped cargo type or AI-classified). Records without a commodity are filtered out. |
| commodity_normalized | string | Signal normalized leaf commodity (e.g. 'VLCC dirty'). Null if not yet resolved. |
| commodity_normalized_group | string | Signal normalized commodity group / L2 (e.g. 'Crude'). Null if not yet resolved. |
| commodity_normalized_subgroup | string | Signal normalized commodity subgroup / L1 (e.g. 'Dirty'). Null if not yet resolved. |
| quantity | number | Quantity in specified unit |
| quantity_unit | string | Unit of measurement (MT, BBL, Units) |
| eta | date | Estimated time of arrival (ISO 8601) |
| etb | date | Estimated time of berthing (ISO 8601) |
| etd | date | Estimated time of departure (ISO 8601) |
| combined_date | date | Earliest of ETA, ETB, ETD (ISO 8601) |
| vessel_status | string | expected, anchored, berthed, departed |
| shipper | string | Shipper company |
| receiver | string | Receiver company |
| charterer | string | Charterer company |
| operator | string | Vessel operator |
| ship_agent | string | Ship agent |
| created_at | date | Record creation timestamp (ISO 8601) |
| updated_at | date | Last update timestamp (ISO 8601) |
Every /lineups query supports a CSV response by adding ?format=csv. Output is UTF-8 with a BOM, RFC 4180 quoting, and a stable column order — drop it straight into Excel without re-mapping.
cURL
curl -H "X-API-Key: ps_xxx" \
"https://api.port-signal.com/v1/lineups?source=<feedId>&format=csv" \
-o lineups.csvFrom the Data Explorer, click Copy Excel-ready URL to mint a share link. Each click creates a fresh, individually-revocable URL scoped to the current filters. The URL embeds a token, so no auth header is needed at refresh time.
=IMPORTDATA("<paste URL>").Treat the URL like a password.Anyone with it can read the data inside the scope. Entitlement is re-checked on every request — cancelling a subscription stops the link returning data — but revoke any unused link from the Data Explorer’s Share Links table.
Returns the list of data sources (feeds) you are currently subscribed to, including port, frequency, and quality ratings.
Returns the field schema and metadata for a specific source, including available fields, data types, and coverage statistics.
The API uses cursor-based pagination. Each response includes a nextCursor field. Pass it as the cursor query parameter to fetch the next page. When nextCursor is null, you have reached the end.
API requests are limited to 100 requests per minute per API key. The response headers include X-RateLimit-Remaining. If you exceed the limit, you will receive a 429 Too Many Requests response with a Retry-After header.
All errors follow a consistent format:
JSON
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key",
"details": null
}
}Common error codes: UNAUTHORIZED (401), FORBIDDEN (403), NOT_FOUND (404), VALIDATION_ERROR (400), RATE_LIMITED (429), INTERNAL_ERROR (500).
cURL
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.port-signal.com/v1/lineups?limit=10"Python
import requests
headers = {"X-API-Key": "YOUR_API_KEY"}
resp = requests.get(
"https://api.port-signal.com/v1/lineups",
headers=headers,
params={"limit": 10},
)
data = resp.json()
for record in data["data"]:
print(record["vessel_name"], record["commodity"])JavaScript
const resp = await fetch(
"https://api.port-signal.com/v1/lineups?limit=10",
{ headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const { data } = await resp.json();
console.log(data);These machine-readable files allow AI agents and automated tools to understand and interact with our API: