API Reference · v1.0
The dealer-governed identification & valuation API for marine.
MarineOS resolves marine identifiers — Hull Identification Numbers (HINs) and engine serials — into structured vessel and powertrain data, original build records, append-only change-ledger events, and a confidence-scored Verified Used Boat Value (V_MOS). One REST call, one dealer-governed source of truth across manufacturers, dealers, registries, and standards-based parsers. Built for retailers, OEMs, marketplaces, lenders, and insurers.
Valuation API Preview · Q3 2026
The Valuation API will return a confidence-scored MarineOS Verified Used Boat Value (V_MOS) for any vessel MarineOS can identify. V_MOS is computed from market comps blended with the vessel's original build record and verified change ledger — the dealer-governed configuration truth that incumbents cannot produce alone.
Output shape (preview)
{
"hin": "BWC12345A404",
"v_mos": 257284,
"currency": "USD",
"confidence_range": { "low": 238000, "high": 276000 },
"data_confidence_score": 0.91,
"inputs": {
"market_comp_weight": 0.55,
"adjusted_market_median": 251000,
"build_msrp": 318000,
"depreciation_factor": 0.71,
"verified_upgrade_adjustments": 12400,
"verified_damage_penalties": 0
},
"as_of": "2026-09-15T00:00:00Z"
}
The valuation engine is a MarineOS trade-secret asset and is being built behind a feature flag during the dealer pilot phase. Endpoint, authentication, and pricing tier will be documented here at general availability. The Valuation API is designed to complement industry references like J.D. Power, ABOS / Price Digests, and marketplace listing data rather than replace them — adding the vessel-specific configuration and change-ledger truth that only dealers and OEMs can produce.
Quickstart #
Make your first lookup in under a minute.
-
1
Create an account
Sign up at app.marineos.io as a data / API partner. API keys are available to data-partner, manufacturer, and dealer accounts. The Free plan starts you with 10 lookups per month — no card required.
-
2
Generate an API key
Open your dashboard and create a key. Keys are prefixed
mos_live_. Optionally restrict the key to a single domain for browser use. -
3
Make your first request
curl https://api.marineos.io/api/v1/lookup/BWC12345A404 \ -H "X-API-Key: mos_live_REPLACE_WITH_YOUR_KEY"
Authentication #
All authenticated endpoints require an X-API-Key header.
X-API-Key: mos_live_REPLACE_WITH_YOUR_KEY
- Single-account scope. Each key is bound to one MarineOS account. Usage and rate limits are tracked against that account.
- Domain restriction (optional). Configure an allowed domain in the dashboard to lock the key to a single origin — required when the key is exposed in browser code (see widget endpoint).
- Rotation is immediate. When you rotate a key, the old key stops working at once. Have the new key ready to paste into every integration and widget that used the old one.
- Never ship keys client-side unless they are domain-restricted and used only with the widget endpoint.
Rate limits #
Limits are tracked per calendar month and reset at the start of each billing cycle.
| Plan | Monthly lookups |
|---|---|
| Free | 10 |
| Technician | 500 |
| Business | 2,000 |
| Team | 10,000 |
| Enterprise | Unlimited |
- When the monthly quota is exceeded, the API returns
429 Too Many Requests. - Every successful response includes
meta.remaining, the number of lookups left in the current cycle. - Need higher limits or burst pricing? Contact us about Enterprise.
Endpoints #
All endpoints are versioned under /api/v1. Responses are JSON.
Available routes
- GET /api/v1/health No auth
- GET /api/v1/me Auth required
- GET /api/v1/lookup/:query Auth required
- GET /api/v1/widget/lookup/:query Auth + domain
/api/v1/health
No authLiveness check. Returns service status, version, and current timestamp. Useful for uptime monitors.
Response — 200
{
"status": "ok",
"service": "MarineOS API",
"version": "1.0.0",
"timestamp": "2026-05-05T17:42:18.219Z"
}
curl https://api.marineos.io/api/v1/healthconst res = await fetch("https://api.marineos.io/api/v1/health");
const health = await res.json();
console.log(health.status); // "ok"import requests
r = requests.get("https://api.marineos.io/api/v1/health")
print(r.json()["status"]) # "ok"/api/v1/me
Auth requiredReturns the calling account, its plan, current usage against the monthly limit, and the API key being used.
Response — 200
{
"account": {
"id": "acc_8c2f9a",
"email": "nick@example-marina.com",
"name": "Nick Larsen",
"company": "Example Marina",
"plan": "small_business"
},
"usage": {
"lookupCount": 412,
"lookupLimit": 2000,
"remaining": 1588
},
"apiKey": {
"id": "key_3a91f0",
"name": "Inventory backend",
"allowedDomain": null
}
}
curl https://api.marineos.io/api/v1/me \
-H "X-API-Key: mos_live_REPLACE_WITH_YOUR_KEY"const res = await fetch("https://api.marineos.io/api/v1/me", {
headers: { "X-API-Key": "mos_live_REPLACE_WITH_YOUR_KEY" },
});
const me = await res.json();
console.log(me.usage.remaining);import requests
r = requests.get(
"https://api.marineos.io/api/v1/me",
headers={"X-API-Key": "mos_live_REPLACE_WITH_YOUR_KEY"},
)
print(r.json()["usage"]["remaining"])/api/v1/lookup/:query
Auth requiredThe primary resolver. The path parameter :query accepts a 12-character HIN (e.g. BWC12345A404) or a manufacturer engine serial (e.g. 0R123456). MarineOS routes the query to the correct resolver and returns the unified record.
Path parameters
| Param | Description |
|---|---|
| query | HIN (12 chars) or engine serial. Case-insensitive. Whitespace and dashes are stripped. |
Success — 200 (vessel)
{
"type": "vessel",
"query": "BWC12345A404",
"vessel": {
"hin": "BWC12345A404",
"make": "Boston Whaler",
"model": "270 Dauntless",
"year": 2024,
"lengthFt": 27.0,
"hullMaterial": "fiberglass"
},
"meta": {
"remaining": 1587,
"plan": "small_business",
"requestId": "req_01HX8T7N2K4E3V"
}
}
Success — 200 (engine)
{
"type": "engine",
"query": "0R123456",
"engine": {
"serial": "0R123456",
"manufacturer": "Mercury",
"model": "Verado 300",
"horsepower": 300,
"year": 2018
},
"meta": {
"remaining": 1586,
"plan": "small_business",
"requestId": "req_01HX8T7N9P2A1Q"
}
}
Not found — 404
{
"error": "not_found",
"message": "No record matches the supplied identifier.",
"query": "ABC99999X999",
"meta": {
"remaining": 1585,
"plan": "small_business",
"requestId": "req_01HX8T7P0N3B0R"
}
}
curl https://api.marineos.io/api/v1/lookup/BWC12345A404 \
-H "X-API-Key: mos_live_REPLACE_WITH_YOUR_KEY"const res = await fetch(
"https://api.marineos.io/api/v1/lookup/BWC12345A404",
{ headers: { "X-API-Key": "mos_live_REPLACE_WITH_YOUR_KEY" } }
);
if (res.status === 404) {
console.log("Not found");
} else if (res.status === 429) {
console.log("Rate limited");
} else {
const data = await res.json();
console.log(data.vessel);
}import requests
r = requests.get(
"https://api.marineos.io/api/v1/lookup/BWC12345A404",
headers={"X-API-Key": "mos_live_REPLACE_WITH_YOUR_KEY"},
)
if r.status_code == 404:
print("Not found")
elif r.status_code == 429:
print("Rate limited")
else:
print(r.json()["vessel"])/api/v1/widget/lookup/:query
Auth + domainSame resolver as /lookup but returns per-field confidence scores and provenance source tiers. Designed to back the embeddable retailer widget. Requires that the API key has an allowedDomain configured — requests from any other origin are rejected with 403.
Source tiers
- manufacturer — direct OEM record (highest confidence)
- dmv — state titling / registration database
- parsed-hin — derived from the HIN itself per USCG standard
- inferred — heuristic match based on adjacent records (lowest)
Success — 200
{
"type": "vessel",
"query": "BWC12345A404",
"vessel": {
"make": { "value": "Boston Whaler", "confidence": 0.99, "source": "manufacturer" },
"model": { "value": "270 Dauntless", "confidence": 0.96, "source": "manufacturer" },
"year": { "value": 2024, "confidence": 0.98, "source": "parsed-hin" },
"lengthFt": { "value": 27.0, "confidence": 0.92, "source": "manufacturer" },
"hullMaterial": { "value": "fiberglass", "confidence": 0.74, "source": "inferred" }
},
"meta": {
"remaining": 1584,
"plan": "small_business",
"requestId": "req_01HX8T7Q1M0C5D"
}
}
curl https://api.marineos.io/api/v1/widget/lookup/BWC12345A404 \
-H "X-API-Key: mos_live_REPLACE_WITH_YOUR_KEY" \
-H "Origin: https://your-store.com"// Use only with a domain-restricted key on the configured origin.
const res = await fetch(
"https://api.marineos.io/api/v1/widget/lookup/BWC12345A404",
{ headers: { "X-API-Key": "mos_live_REPLACE_WITH_YOUR_KEY" } }
);
const data = await res.json();
console.log(data.vessel.make.confidence, data.vessel.make.source);import requests
r = requests.get(
"https://api.marineos.io/api/v1/widget/lookup/BWC12345A404",
headers={
"X-API-Key": "mos_live_REPLACE_WITH_YOUR_KEY",
"Origin": "https://your-store.com",
},
)
data = r.json()
print(data["vessel"]["make"]["confidence"], data["vessel"]["make"]["source"])Code samples #
Drop-in clients you can adapt. Replace mos_live_REPLACE_WITH_YOUR_KEY with your real key. The base URL https://api.marineos.io is canonical; the direct Railway URL https://api-production-5d51.up.railway.app also works during DNS propagation.
#!/usr/bin/env bash
set -euo pipefail
API_KEY="mos_live_REPLACE_WITH_YOUR_KEY"
BASE="https://api.marineos.io"
QUERY="${1:-BWC12345A404}"
curl -sS "$BASE/api/v1/lookup/$QUERY" \
-H "X-API-Key: $API_KEY" | jq .// MarineOS minimal client (Node 18+ / browsers with fetch)
const BASE = "https://api.marineos.io";
export async function marineosLookup(query, apiKey) {
const res = await fetch(`${BASE}/api/v1/lookup/${encodeURIComponent(query)}`, {
headers: { "X-API-Key": apiKey },
});
if (res.status === 404) return { found: false };
if (res.status === 429) throw new Error("Rate limit exceeded");
if (!res.ok) throw new Error(`MarineOS ${res.status}`);
const body = await res.json();
return { found: true, ...body };
}
// Usage
const r = await marineosLookup("BWC12345A404", "mos_live_REPLACE_WITH_YOUR_KEY");
console.log(r);"""MarineOS minimal client."""
import requests
BASE = "https://api.marineos.io"
class MarineOSError(Exception):
pass
def lookup(query: str, api_key: str) -> dict | None:
r = requests.get(
f"{BASE}/api/v1/lookup/{query}",
headers={"X-API-Key": api_key},
timeout=10,
)
if r.status_code == 404:
return None
if r.status_code == 429:
raise MarineOSError("Rate limit exceeded")
r.raise_for_status()
return r.json()
if __name__ == "__main__":
print(lookup("BWC12345A404", "mos_live_REPLACE_WITH_YOUR_KEY"))Error codes #
All errors return a JSON body with error (machine-readable) and message (human-readable) fields.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | Empty or malformed query parameter. |
| 401 | unauthorized | Missing X-API-Key header. |
| 403 | forbidden | Invalid or deactivated key, or request origin is not in the key's allowed domain. |
| 404 | not_found | The supplied identifier does not match any record in our database. |
| 429 | rate_limited | Monthly lookup quota exceeded. Upgrade your plan or wait until the cycle resets. |
| 500 | server_error | Unexpected server-side error. Retry with exponential backoff; contact support if persistent. |
Embeddable widget
Live · v1.0A drop-in HIN / serial lookup widget for retailer storefronts, parts catalogs, and quoting tools. Three lines, zero build step, ~14kb gzipped, isolated via Shadow DOM. See it live on a sample retailer site →
Install
Paste these two tags anywhere in your HTML — the widget mounts on every [data-marineos-widget] element on the page.
<script src="https://api.marineos.io/widget.js" async></script>
<div data-marineos-widget data-api-key="mos_live_REPLACE_WITH_YOUR_KEY"></div>
Configuration
All options are set via data-* attributes on the mount element. Only data-api-key is required.
| Attribute | Default | Description |
|---|---|---|
data-api-key | required | Your MarineOS API key. Use a domain-restricted key — keys are exposed in browser code. |
data-theme | light | light or dark. Switches the entire widget palette. |
data-accent | #0ea5e9 | Hex color for the CTA button, focus ring, and confidence bars. |
data-placeholder | Enter HIN or serial… | Override the input placeholder. |
data-cta | Look up | Override the submit button text. |
data-show-bom | true | Set to false to hide the bill-of-materials table. |
data-base-url | auto | Override the API origin (advanced — for testing or self-hosted). |
Themed example
Match your brand by setting data-theme and data-accent. Compact mode hides the BOM and shortens the CTA.
<div data-marineos-widget
data-api-key="mos_live_..."
data-theme="dark"
data-accent="#f59e0b"
data-show-bom="false"
data-cta="Search"></div>
SPA & dynamic mounts
For React, Vue, or other single-page apps, call window.MarineOSWidget.mount(element, options) after the element is in the DOM. The loader auto-mounts on initial page load.
// After your component mounts
window.MarineOSWidget.mount(elementRef.current, {
apiKey: 'mos_live_...',
theme: 'light',
accent: '#e63946',
onResult: (data) => console.log('lookup result', data)
});
Notes
- Domain-restrict your key. The widget exposes the API key in browser code — set
allowedDomainin the dashboard so it can only be used from your origin(s). - Style isolation. The widget renders inside a Shadow DOM — your site's CSS will not bleed in.
- Backed by
/api/v1/widget/lookup/:query— per-field confidence scores and provenance tiers (manufacturer / dealer / crowdsourced). - Live demo: demo.marineos.io shows the widget on a sample retailer storefront with light, dark, and custom accent variants.
Support #
Talk to a human about Enterprise SLAs, custom integrations, or higher rate limits.