> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nozlerouting.com/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /v1/optimize

> The core Nozle endpoint. Takes a route, a list of stations with prices and route-relative positions, and a vehicle. Returns the fuel-cost-optimal sequence of stops along with a baseline comparison showing what a naive strategy would have cost.

**Prerequisites:** all stations must have `price_per_gallon` and `miles_from_route_start` set. Use /v1/stations/enrich-distances to compute distances if you don't have them. Stations must share a single `fuel_type` value (or have it unset) — mixed fuel grades in one request are rejected.

**Vehicle handling:** you can either provide direct specs (`mpg`, `tank_capacity_gallons`, `current_fuel_gallons`) or year/make/model and let us look up the specs. Direct specs are always preferred when available.

**Tuning:** `time_value_dollars_per_minute` controls how the optimizer trades cheaper fuel against longer detours. 0.1 = very cost-sensitive, 0.9 = very time-sensitive, 0.5 (default) = balanced.

**Baseline comparison:** the response includes savings vs. a naive strategy that refuels to full at the next station ahead whenever the tank reaches 25%. This is the dollar value the optimizer is delivering.

The `/v1/optimize` endpoint is the core of the Nozle API. Given a route, stations with prices, and a vehicle, it returns the cost-optimal sequence of fueling stops along with a baseline comparison showing savings vs. naive refueling.

## Algorithm

The optimizer uses dynamic programming to search for the stop sequence minimizing the objective function:

total\_cost = fuel\_cost + (time\_value\_dollars\_per\_minute × total\_detour\_minutes)

subject to:

* Tank capacity constraints (can't exceed `tank_capacity_gallons` at any stop)
* Destination reserve constraint (must arrive with at least `destination_reserve_fraction × tank_capacity_gallons`)
* Inter-stop buffer constraint (must arrive at each stop with at least `inter_stop_buffer_fraction × tank_capacity_gallons`)

## Baseline Comparison

The response includes a `baseline_comparison` object computing what a naive 25%-threshold refueling strategy would cost on the same route and station set. This provides the savings number partners use to demonstrate value to their customers.

For comparing costs fairly, the baseline uses **net fuel cost** (the dollar value of fuel actually consumed on the trip) rather than pump cost, since naive strategies often refuel to full and end with paid-for fuel in the tank.

## Infeasibility

If the optimizer cannot find a feasible plan with the given inputs, it returns a `422 INFEASIBLE_ROUTE` response with diagnostic details explaining why. Common causes:

* Insufficient `current_fuel_gallons` to reach the first available station
* Gap between consecutive stations exceeds vehicle range
* Reserve constraints can't be satisfied with available stations

The `details` field in error responses includes specific diagnostic information to help debug the issue.


## OpenAPI

````yaml POST /v1/optimize
openapi: 3.1.0
info:
  title: Nozle B2B API
  description: >-
    Fuel-route optimization for fleet operators. Takes a route, your stations
    with prices, and a vehicle. Returns the cost-optimal stopping sequence with
    a baseline comparison.


    ## Data Model


    **Nozle is compute-only.** We don't maintain a station database or pricing
    feed — partners bring their own station data, and we do the routing math.
    Specifically:


    - **You provide:** stations with current prices, a vehicle, and trip
    endpoints (or a precomputed route).

    - **We provide:** route geometry, distance enrichment for your stations, the
    optimization algorithm, and a baseline comparison showing what naive
    refueling would cost.


    This separation matters because (a) you almost certainly have more accurate
    price data than any third-party feed could provide, and (b) it keeps the API
    stateless — no syncing, no staleness, no licensing constraints on station
    coverage.


    ## Concepts


    **Route** — A driving path between two points, expressed as total distance,
    total duration, and an encoded polyline (Google Polyline Algorithm Format).
    You can compute one with `/v1/routes` or pass in a route you already have
    from another provider.


    **Station** — A fueling location with coordinates, a price per gallon, and a
    fuel type. After enrichment, stations also carry `miles_from_route_start`
    (their position along the route) and `detour_minutes` (time cost to visit
    them). You bring stations; we annotate them.


    **Vehicle** — A fuel-efficiency profile. Either direct specs (`mpg`,
    `tank_capacity_gallons`, `current_fuel_gallons`) or a year/make/model
    lookup. Direct specs always win when complete; lookup is a convenience for
    partners who don't track tank size.


    **Optimization** — A dynamic-programming search for the stop sequence that
    minimizes `(fuel_cost + λ * detour_time)` while respecting tank capacity and
    a destination fuel reserve. The knob `time_value_dollars_per_minute`
    controls λ — higher values make the optimizer more time-sensitive (skip
    cheap-but-distant stations), lower values prioritize cost.


    ## Authentication


    All endpoints require an `X-API-Key` header. Email
    `rohan.iyer@nozlerouting.com` to request a key.


    ## Pipeline


    Most partners use the three endpoints in sequence:


    1. **`POST /v1/routes`** — compute a driving route between two addresses.

    2. **`POST /v1/stations/enrich-distances`** — annotate your stations with
    route-relative positions and detour times.

    3. **`POST /v1/optimize`** — compute the optimal fueling plan, with savings
    vs. naive baseline.


    Partners with their own routing can skip step 1 and pass a route directly
    into steps 2 and 3.


    ## Quickstart


    Minimal Python example calling all three endpoints. Requires `requests`.
    Replace `YOUR_API_KEY` with the key you received from support.


    **[Download the full example](/examples/quickstart.py)** (`curl
    /examples/quickstart.py > quickstart.py`) for a complete version with error
    handling, structured-error parsing, and curl equivalents in comments for
    partners using other languages.


    ```python

    import requests


    BASE = 'https://api.nozlerouting.com'

    HEADERS = {'X-API-Key': 'YOUR_API_KEY'}


    # 1. Compute a route

    route = requests.post(f'{BASE}/v1/routes', headers=HEADERS, json={
        'origin': 'San Francisco, CA',
        'destination': 'Los Angeles, CA',
    }).json()['route']


    # 2. Enrich your stations with route-relative positions

    stations = [{
        'id': 'STATION_001', 'name': 'Pilot Lost Hills',
        'address': '14808 Warren St, Lost Hills, CA',
        'location': {'lat': 35.6151, 'lng': -119.6588},
        'price_per_gallon': 4.39, 'fuel_type': 'diesel',
    }]

    enriched = requests.post(f'{BASE}/v1/stations/enrich-distances',
        headers=HEADERS, json={'route': route, 'stations': stations}
    ).json()['stations']


    # 3. Optimize

    result = requests.post(f'{BASE}/v1/optimize', headers=HEADERS, json={
        'route': route,
        'stations': enriched,
        'vehicle': {
            'mpg': 6.5,
            'tank_capacity_gallons': 240.0,
            'current_fuel_gallons': 30.0,
        },
    }).json()


    print(f"Recommended stops: {len(result['stops'])}")

    print(f"Net cost:
    ${result['summary']['baseline_comparison']['optimizer_net_fuel_cost']:.2f}")

    ```


    ## Latency


    Approximate response times under typical load (single-region, low concurrent
    traffic):


    - **`/v1/routes`** — ~500-900ms (one Google Maps Directions API call
    dominates).

    - **`/v1/stations/enrich-distances`** — ~300ms-1s for 1-50 stations; scales
    roughly linearly with station count due to Mapbox matrix sizing.

    - **`/v1/optimize`** — ~500ms-2s. Optimizer runtime depends on station count
    and trip distance; trips with more stations and longer distance take longer.


    These are estimates, not SLAs. Set client-side timeouts at 10-15 seconds to
    handle tail latency from upstream providers.


    ## Errors


    All errors return a canonical shape with `error_code`, `message`, optional
    `details`, and `request_id` (always set, useful in support requests). Status
    codes follow standard HTTP semantics: 400 for validation errors, 403 for
    auth, 422 for infeasibility, 429 for rate limits, 500 for unexpected
    failures.


    ## Rate Limits


    Per API key: 60/minute on `/v1/routes`, 120/minute on the other two
    endpoints. Exceeding the limit returns 429 with a `Retry-After` header.


    ## Versioning


    The `/v1/` URL prefix is stable. Within v1, additive changes (new endpoints,
    new optional request fields, new response fields) ship without notice.
    Breaking changes (renamed or removed fields, changed types, changed
    semantics) ship as a new prefix (`/v2/`) with v1 supported in parallel for a
    documented deprecation window. Error codes are stable strings — we add to
    the list but never rename existing ones.
  contact:
    name: Nozle Support
    email: rohan.iyer@nozlerouting.com
  version: 1.0.0
servers: []
security: []
tags:
  - name: v1
    description: >-
      Stable v1 endpoints for fleet partners. These are the only endpoints
      partners should integrate with.
paths:
  /v1/optimize:
    post:
      tags:
        - v1
      summary: Compute the cost-optimal fueling plan for a route
      description: >-
        The core Nozle endpoint. Takes a route, a list of stations with prices
        and route-relative positions, and a vehicle. Returns the
        fuel-cost-optimal sequence of stops along with a baseline comparison
        showing what a naive strategy would have cost.


        **Prerequisites:** all stations must have `price_per_gallon` and
        `miles_from_route_start` set. Use /v1/stations/enrich-distances to
        compute distances if you don't have them. Stations must share a single
        `fuel_type` value (or have it unset) — mixed fuel grades in one request
        are rejected.


        **Vehicle handling:** you can either provide direct specs (`mpg`,
        `tank_capacity_gallons`, `current_fuel_gallons`) or year/make/model and
        let us look up the specs. Direct specs are always preferred when
        available.


        **Tuning:** `time_value_dollars_per_minute` controls how the optimizer
        trades cheaper fuel against longer detours. 0.1 = very cost-sensitive,
        0.9 = very time-sensitive, 0.5 (default) = balanced.


        **Baseline comparison:** the response includes savings vs. a naive
        strategy that refuels to full at the next station ahead whenever the
        tank reaches 25%. This is the dollar value the optimizer is delivering.
      operationId: post_optimize_v1_optimize_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OptimizeRequest'
        required: true
      responses:
        '200':
          description: Optimal plan computed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OptimizeResponse'
              example:
                stops:
                  - station_id: partner_xyz_001
                    station_name: Pilot
                    station_address: 456 Truck Rd, Flagstaff, AZ
                    miles_from_route_start: 487.3
                    gallons_to_purchase: 247.2
                    price_per_gallon: 4.29
                    total_cost_at_stop: 1060.49
                    detour_minutes: 3.2
                summary:
                  total_fuel_cost: 1847.32
                  total_fuel_gallons: 432.1
                  total_time_hours: 31.5
                  total_distance_miles: 2015.3
                  fuel_remaining_at_destination_gallons: 32.4
                  baseline_comparison:
                    strategy_description: >-
                      Refuel to full at the next station ahead whenever the tank
                      reaches 25% capacity.
                    naive_total_fuel_cost: 2089.74
                    naive_total_time_hours: 31.8
                    savings_dollars: 242.42
                    savings_percent: 11.6
                feasible: true
                warnings: []
                request_id: req_jkl345mno678
        '400':
          description: >-
            Validation error: missing prices or distances on stations, mixed
            fuel types, vehicle not found, optimizer rejected the input shape.
        '403':
          description: Missing or invalid API key.
        '422':
          description: >-
            The route is infeasible under the given vehicle and station
            constraints. Diagnostic details in the `details` field of the
            response.
        '429':
          description: Rate limit exceeded (120 requests/minute per key).
        '500':
          description: >-
            Optimizer or vehicle lookup service failed. Include the request_id
            when contacting support.
      security:
        - APIKeyHeader: []
components:
  schemas:
    OptimizeRequest:
      properties:
        route:
          $ref: '#/components/schemas/Route'
        stations:
          items:
            $ref: '#/components/schemas/Station'
          type: array
          maxItems: 5000
          minItems: 1
          title: Stations
        vehicle:
          $ref: '#/components/schemas/Vehicle'
        params:
          $ref: '#/components/schemas/OptimizationParams'
      type: object
      required:
        - route
        - stations
        - vehicle
      title: OptimizeRequest
    OptimizeResponse:
      properties:
        stops:
          items:
            $ref: '#/components/schemas/StopRecommendation'
          type: array
          title: Stops
        summary:
          $ref: '#/components/schemas/RouteSummary'
        feasible:
          type: boolean
          title: Feasible
        warnings:
          items:
            type: string
          type: array
          title: Warnings
        request_id:
          type: string
          title: Request Id
      type: object
      required:
        - stops
        - summary
        - feasible
        - request_id
      title: OptimizeResponse
    Route:
      properties:
        total_distance_miles:
          type: number
          exclusiveMinimum: 0
          title: Total Distance Miles
          description: Total driving distance from start to end, in miles.
        total_duration_minutes:
          type: number
          exclusiveMinimum: 0
          title: Total Duration Minutes
          description: Total driving time from start to end, in minutes (no stops).
        start:
          $ref: '#/components/schemas/Coordinates'
          description: Starting coordinates.
        end:
          $ref: '#/components/schemas/Coordinates'
          description: Ending coordinates.
        polyline:
          anyOf:
            - type: string
            - type: 'null'
          title: Polyline
          description: >-
            Encoded polyline tracing the route. Same format as Google Maps
            `overview_polyline.points`. Either `polyline` or `waypoints` is
            required.
        waypoints:
          anyOf:
            - items:
                $ref: '#/components/schemas/Coordinates'
              type: array
            - type: 'null'
          title: Waypoints
          description: >-
            Explicit list of lat/lng points along the route. Alternative to
            `polyline` if you don't have an encoded form. Either `polyline` or
            `waypoints` is required.
      type: object
      required:
        - total_distance_miles
        - total_duration_minutes
        - start
        - end
      title: Route
      description: |-
        Internal route representation. Flat shape — upstream adapters normalize
        Google/Mapbox responses into this before it hits B2B code.
    Station:
      properties:
        id:
          type: string
          maxLength: 200
          minLength: 1
          title: Id
          description: >-
            Partner-controlled identifier. Treated as opaque — round-trips
            unchanged through every endpoint. Use whatever lets you match the
            station back to your records (database key, address string,
            coordinate hash, etc.).
        name:
          type: string
          maxLength: 200
          minLength: 1
          title: Name
          description: Station name.
        address:
          type: string
          maxLength: 500
          minLength: 1
          title: Address
          description: Street address.
        location:
          $ref: '#/components/schemas/Coordinates'
          description: Station coordinates.
        brand:
          anyOf:
            - type: string
              maxLength: 100
            - type: 'null'
          title: Brand
          description: Optional brand (e.g. 'Shell', 'Pilot').
        price_per_gallon:
          anyOf:
            - type: number
              maximum: 100
              exclusiveMinimum: 0
            - type: 'null'
          title: Price Per Gallon
          description: >-
            Current price per gallon. Required for /v1/optimize. Pre-filter to
            your vehicle's fuel type before sending.
        fuel_type:
          anyOf:
            - type: string
              enum:
                - regular
                - midgrade
                - premium
                - diesel
            - type: 'null'
          title: Fuel Type
          description: >-
            Informational tag. The optimizer treats price_per_gallon as
            authoritative regardless of fuel_type. All stations in one
            /v1/optimize request must share this value (if set) — mixed types
            are rejected to catch integration mistakes.
        miles_from_route_start:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Miles From Route Start
          description: >-
            How far along the route the station sits. Populated by
            /v1/stations/enrich-distances. Required for /v1/optimize.
        detour_minutes:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Detour Minutes
          description: >-
            Round-trip detour time vs. driving past the station. Populated by
            /v1/stations/enrich-distances.
        metadata:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Metadata
          description: >-
            Optional partner-defined data passed through every endpoint
            unchanged. Use it for anything you want to associate with a station
            (loyalty tier, internal IDs, etc.).
      type: object
      required:
        - id
        - name
        - address
        - location
      title: Station
      description: |-
        Represents a gas station as it flows through the v1 pipeline. Fields
        accumulate as the station passes through endpoints:

          enrich-distances:  adds miles_from_route_start, detour_minutes
          optimize:          consumes all fields

        Partner-provided fields (id, name, address, location, brand, prices,
        metadata) are always preserved round-trip. Enrichment only adds computed
        fields; it never modifies what the partner sent.
    Vehicle:
      properties:
        mpg:
          anyOf:
            - type: number
              maximum: 200
              exclusiveMinimum: 0
            - type: 'null'
          title: Mpg
          description: Combined fuel efficiency in miles per gallon.
        tank_capacity_gallons:
          anyOf:
            - type: number
              maximum: 500
              exclusiveMinimum: 0
            - type: 'null'
          title: Tank Capacity Gallons
          description: Tank capacity in gallons.
        current_fuel_gallons:
          anyOf:
            - type: number
              maximum: 500
              minimum: 0
            - type: 'null'
          title: Current Fuel Gallons
          description: Current fuel level in gallons. Cannot exceed tank_capacity_gallons.
        year:
          anyOf:
            - type: integer
              maximum: 2030
              minimum: 1980
            - type: 'null'
          title: Year
          description: Vehicle year. Used with make and model for spec lookup.
        make:
          anyOf:
            - type: string
              maxLength: 100
              minLength: 1
            - type: 'null'
          title: Make
          description: Vehicle make (e.g. 'Freightliner').
        model:
          anyOf:
            - type: string
              maxLength: 100
              minLength: 1
            - type: 'null'
          title: Model
          description: Vehicle model (e.g. 'Cascadia').
      type: object
      title: Vehicle
      description: |-
        Vehicle parameters for optimization. Accepts either direct specs
        (preferred) or year/make/model for lookup. Direct specs override lookup
        values when both are provided.
    OptimizationParams:
      properties:
        time_value_dollars_per_minute:
          type: number
          maximum: 0.9
          minimum: 0.1
          title: Time Value Dollars Per Minute
          description: >-
            How much a minute of detour is worth, in dollars. Lower values favor
            cheaper stations even with longer detours; higher values favor
            closer stations even at higher prices. 0.1 = strongly
            cost-sensitive, 0.9 = strongly time-sensitive.
          default: 0.5
        destination_reserve_fraction:
          type: number
          maximum: 1
          minimum: 0
          title: Destination Reserve Fraction
          description: >-
            Minimum fraction of tank to arrive at destination with. Default 0.10
            (10% reserve).
          default: 0.1
        inter_stop_buffer_fraction:
          type: number
          maximum: 1
          minimum: 0
          title: Inter Stop Buffer Fraction
          description: >-
            Minimum fraction of tank to maintain between stops. Default 0.05 (5%
            buffer).
          default: 0.05
      type: object
      title: OptimizationParams
    StopRecommendation:
      properties:
        station_id:
          type: string
          title: Station Id
        station_name:
          type: string
          title: Station Name
        station_address:
          type: string
          title: Station Address
        miles_from_route_start:
          type: number
          title: Miles From Route Start
        gallons_to_purchase:
          type: number
          minimum: 0
          title: Gallons To Purchase
        price_per_gallon:
          type: number
          exclusiveMinimum: 0
          title: Price Per Gallon
        total_cost_at_stop:
          type: number
          minimum: 0
          title: Total Cost At Stop
        detour_minutes:
          type: number
          minimum: 0
          title: Detour Minutes
      type: object
      required:
        - station_id
        - station_name
        - station_address
        - miles_from_route_start
        - gallons_to_purchase
        - price_per_gallon
        - total_cost_at_stop
        - detour_minutes
      title: StopRecommendation
    RouteSummary:
      properties:
        total_fuel_cost:
          type: number
          minimum: 0
          title: Total Fuel Cost
        total_fuel_gallons:
          type: number
          minimum: 0
          title: Total Fuel Gallons
        total_time_hours:
          type: number
          minimum: 0
          title: Total Time Hours
        total_distance_miles:
          type: number
          minimum: 0
          title: Total Distance Miles
        fuel_remaining_at_destination_gallons:
          type: number
          minimum: 0
          title: Fuel Remaining At Destination Gallons
        baseline_comparison:
          $ref: '#/components/schemas/BaselineComparison'
      type: object
      required:
        - total_fuel_cost
        - total_fuel_gallons
        - total_time_hours
        - total_distance_miles
        - fuel_remaining_at_destination_gallons
        - baseline_comparison
      title: RouteSummary
    Coordinates:
      properties:
        lat:
          type: number
          maximum: 90
          minimum: -90
          title: Lat
          description: Latitude in degrees, -90 to 90.
        lng:
          type: number
          maximum: 180
          minimum: -180
          title: Lng
          description: Longitude in degrees, -180 to 180.
      type: object
      required:
        - lat
        - lng
      title: Coordinates
      description: WGS84 lat/lng pair.
    BaselineComparison:
      properties:
        strategy_description:
          type: string
          title: Strategy Description
        naive_total_fuel_cost:
          type: number
          minimum: 0
          title: Naive Total Fuel Cost
        naive_net_fuel_cost:
          type: number
          minimum: 0
          title: Naive Net Fuel Cost
        naive_total_time_hours:
          type: number
          minimum: 0
          title: Naive Total Time Hours
        optimizer_net_fuel_cost:
          type: number
          minimum: 0
          title: Optimizer Net Fuel Cost
        savings_dollars:
          type: number
          title: Savings Dollars
        savings_percent:
          anyOf:
            - type: number
            - type: 'null'
          title: Savings Percent
        infeasible_baseline:
          type: boolean
          title: Infeasible Baseline
          default: false
      type: object
      required:
        - strategy_description
        - naive_total_fuel_cost
        - naive_net_fuel_cost
        - naive_total_time_hours
        - optimizer_net_fuel_cost
        - savings_dollars
      title: BaselineComparison
      description: |-
        Comparison of the optimizer's plan against a naive baseline strategy.

        The naive strategy: drive until tank reaches 25%, then refuel to full
        at the next station ahead regardless of price. Repeat until destination.

        The savings figures use **net fuel cost** (dollars paid minus value of
        fuel still in the tank at destination) rather than raw cash-at-pump.
        This is the apples-to-apples comparison: both strategies are measured
        on what they actually cost the trip, not what was spent at the pump
        (which can be misleading because the naive strategy often arrives
        with a near-full tank that was paid for but unused).

        For trips where the naive strategy can't complete the route, the
        `savings_percent` field is null and `infeasible_baseline` is true —
        the comparison degenerates because there's no finite naive cost to
        compare against. In that case `savings_dollars` is set to the
        optimizer's net cost (representing "the optimizer found a feasible
        plan; naive driving could not"), but the percent figure is omitted
        rather than reporting a misleading "100% savings".
  securitySchemes:
    APIKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key

````