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.

Get an API key Read the quickstart Base URL https://api.marineos.io
Identification
HIN + engine serial
Original build
Per-hull spec & MSRP
Change ledger
Append-only events
Valuation (preview)
V_MOS + confidence range

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. 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. 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. 3

    Make your first request

    curl
    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.

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.

PlanMonthly lookups
Free10
Technician500
Business2,000
Team10,000
EnterpriseUnlimited
  • 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
GET

/api/v1/health

No auth

Liveness check. Returns service status, version, and current timestamp. Useful for uptime monitors.

Response — 200

JSON
{
  "status": "ok",
  "service": "MarineOS API",
  "version": "1.0.0",
  "timestamp": "2026-05-05T17:42:18.219Z"
}
request
curl https://api.marineos.io/api/v1/health
const 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"
GET

/api/v1/me

Auth required

Returns the calling account, its plan, current usage against the monthly limit, and the API key being used.

Response — 200

JSON
{
  "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
  }
}
request
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"])
GET

/api/v1/lookup/:query

Auth required

The 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

ParamDescription
queryHIN (12 chars) or engine serial. Case-insensitive. Whitespace and dashes are stripped.

Success — 200 (vessel)

JSON
{
  "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)

JSON
{
  "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

JSON
{
  "error": "not_found",
  "message": "No record matches the supplied identifier.",
  "query": "ABC99999X999",
  "meta": {
    "remaining": 1585,
    "plan": "small_business",
    "requestId": "req_01HX8T7P0N3B0R"
  }
}
request
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"])
GET

/api/v1/widget/lookup/:query

Auth + domain

Same 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

JSON
{
  "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"
  }
}
request
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.

marineos client
#!/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.

StatusCodeMeaning
400bad_requestEmpty or malformed query parameter.
401unauthorizedMissing X-API-Key header.
403forbiddenInvalid or deactivated key, or request origin is not in the key's allowed domain.
404not_foundThe supplied identifier does not match any record in our database.
429rate_limitedMonthly lookup quota exceeded. Upgrade your plan or wait until the cycle resets.
500server_errorUnexpected server-side error. Retry with exponential backoff; contact support if persistent.

Embeddable widget

Live · v1.0

A 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.

HTML
<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.

AttributeDefaultDescription
data-api-keyrequiredYour MarineOS API key. Use a domain-restricted key — keys are exposed in browser code.
data-themelightlight or dark. Switches the entire widget palette.
data-accent#0ea5e9Hex color for the CTA button, focus ring, and confidence bars.
data-placeholderEnter HIN or serial…Override the input placeholder.
data-ctaLook upOverride the submit button text.
data-show-bomtrueSet to false to hide the bill-of-materials table.
data-base-urlautoOverride 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.

HTML
<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.

JavaScript
// 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 allowedDomain in 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.