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

# Rate-based pricing

> How the engine turns your rate rows into a price, and which pricing models you can and cannot express with them.

Rate-based pricing lets a customer pick any start and end, and the engine builds the price from your rate rows. A handful of rows covers every duration a customer can choose — 30 minutes, 19 hours, seven weeks — without you listing a price for each one.

That flexibility has a cost: the price for a duration you never thought about is decided by rules you did not write. This page is those rules. For the field-by-field reference, see [Price Tables](/docs/concepts/catalog/price-tables).

## How it works

### Two engines, one switch

Rate-based resolution is not one algorithm. Which one runs depends on the **time basis** of your rows, set by the **Starting at pricing** checkbox on the table:

|                         | Elapsed duration (default)            | Starting at                                  |
| ----------------------- | ------------------------------------- | -------------------------------------------- |
| What a "day" means      | 24 hours from pickup                  | A calendar day, midnight to midnight         |
| How the price is built  | The longest row that fits, repeatedly | The cheapest combination per calendar period |
| Does price beat length? | Only between rows of the same length  | Yes, across lengths                          |
| 23:00 → 01:00           | 2 hours                               | 2 days                                       |

<Warning>
  These are two questions welded to one setting. *When does the longer rate kick in* and *how is a day counted* cannot be chosen independently. You cannot have "whichever combination is cheapest" together with 24-hours-from-pickup counting.
</Warning>

The rest of this page describes **Elapsed**, the default and the one most rentals use. Starting at is covered in [Starting at pricing](/docs/concepts/catalog/price-tables#starting-at-pricing).

### The walk

On Elapsed, the engine starts at the pickup instant and moves forward:

<Steps>
  <Step title="Find the rows that fit">
    A row is eligible for the remaining time only if its full length still fits. A 1-week row is not eligible for a 6-day booking — not at any price.
  </Step>

  <Step title="Take the longest one">
    Among the rows that fit, the longest wins. Price is compared **only** when two rows are the same length.
  </Step>

  <Step title="Advance and repeat">
    The clock moves forward by that row's length, and the search runs again on what is left.
  </Step>

  <Step title="Round up the remainder">
    When the time left is shorter than every row, the engine bills one unit of your **shortest** row and stops.
  </Step>
</Steps>

### The rules that decide every price

<AccordionGroup>
  <Accordion title="A row must fit before it can win">
    Eligibility comes before price. A row whose length exceeds the time remaining is not a candidate, so the tier you designed for long rentals does nothing until the booking is long enough to contain it.
  </Accordion>

  <Accordion title="Price only breaks ties between rows of the same length">
    The engine does not compare a week against seven days and pick the cheaper total. It picks the week because it is longer. **An over-priced long row is not priced out — it is preferred.**

    Tiering produces a discount because you priced the longer block lower, not because the engine shops for the cheapest total.
  </Accordion>

  <Accordion title="The first block bills the base price, later blocks bill the additional price">
    The first block the walk takes uses **Base price**. Every block after that uses that row's **Additional price**, falling back to the base price when you leave it empty.

    Each row has its own additional price, and which one applies depends on where the walk lands. On a table with 4-hour and 8-hour rows, hours 9–16 are priced by the *8-hour row's* additional price.
  </Accordion>

  <Accordion title="Any remainder rounds up to your shortest row">
    A booking that ends 10 minutes into a gap no row can fill is billed a full unit of the shortest row on the table. If your shortest row is 2 hours, every remainder costs 2 hours.
  </Accordion>

  <Accordion title="Restrictions decide eligibility before anything else">
    A row's **weekday**, **time of day** and **date range** restrictions are checked first. A restricted row is not a candidate for a segment it does not cover, however long or cheap it is. When a stretch of the booking matches no row at all, the engine uses a **fallback price** if any row offers one.
  </Accordion>
</AccordionGroup>

## Usage

### What happens between two tiers

Take a listing with four rows and no additional prices set:

| Period  | Base price |
| ------- | ---------- |
| 2 hours | €20        |
| 4 hours | €30        |
| 8 hours | €50        |
| 1 day   | €60        |

A **19-hour** booking does not cost €60. The day row needs a full 24 hours to be eligible, so it never enters the running:

| Step | Time left | Row taken            | Price                          |
| ---- | --------- | -------------------- | ------------------------------ |
| 1    | 19h       | 8 hours              | €50 (base)                     |
| 2    | 11h       | 8 hours              | €50 (additional, unset → base) |
| 3    | 3h        | 2 hours              | €20                            |
| 4    | 1h        | 2 hours (rounded up) | €20                            |

**19 hours costs €140. A full day costs €60.**

This is the single most common surprise in rate-based pricing, and the cause is always the same: **the additional prices are empty, so they fall back to the base price.** Set them and the gap behaves:

With 8-hour additional €6, 4-hour additional €3, 2-hour additional €2:

| Duration | Price |
| -------- | ----- |
| 18h      | €58   |
| 19h      | €60   |
| 20h      | €59   |
| 22h      | €61   |
| 23h      | €63   |
| 24h      | €60   |

<Warning>
  Close, but not monotonic — 22 and 23 hours still cost more than a full day. The gap between two tiers is assembled from the lower tier's blocks, so it overshoots near the top. No combination of additional prices removes this entirely. **If a duration matters commercially, give it its own row.**
</Warning>

### What happens at a tier boundary

The boundary is exact, and it is not symmetrical. Take a table with a 1-week row at €300, a day row at €60, and the rows above:

| Booking                  | Price    | Why                                                                                       |
| ------------------------ | -------- | ----------------------------------------------------------------------------------------- |
| 6 days, 23 hours, 30 min | **€379** | The week row does not fit. Six day-blocks plus a 23.5-hour tail of 8h + 8h + 4h + 2h + 2h |
| Exactly 7 days           | **€300** | The week row fits, and it is the longest                                                  |
| 7 days and 30 min        | **€302** | Week row, then 30 minutes rounded up to one 2-hour block                                  |

A booking **30 minutes shorter** costs **€79 more**. Customers see this on the storefront.

The fix is not a pricing setting. It is [booking limits](/docs/catalog/listings/limits): set a **duration increment** so 6 days 23 hours 30 minutes is not offerable in the first place. The storefront only presents increment-aligned end times.

### What you can and cannot express

| You want                                           | Elapsed                                                   | Starting at                                                                                |
| -------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Longer rentals cheaper per unit                    | Yes — price the longer row lower                          | Yes                                                                                        |
| Longer rentals **more** per unit                   | Yes                                                       | **No** — the engine takes the cheaper combination, so a premium long row is never selected |
| The week rate only from exactly 7 × 24h            | Yes — this is the default behaviour                       | No — boundaries are calendar-aligned                                                       |
| The week rate as soon as day-rates would exceed it | **No**                                                    | Yes, within calendar alignment                                                             |
| A day means 24 hours from pickup                   | Yes                                                       | No                                                                                         |
| A day means a calendar day                         | No                                                        | Yes                                                                                        |
| Cheaper prices on set dates (a promotion)          | Yes — a cheaper row of the same length wins automatically | Yes                                                                                        |
| Higher prices on set dates (a surcharge)           | Only by date-restricting the competing cheaper row        | Same                                                                                       |
| Different prices on weekends                       | Yes — the **weekday** restriction                         | Yes                                                                                        |
| Different prices per location or sales channel     | **Not available**                                         | **Not available**                                                                          |

### Techniques

<AccordionGroup>
  <Accordion title="Put a row where the price should change">
    **Period** is a free number and a unit, not a fixed list. If your price should change at six days, add a **6 days** row. Under Elapsed it becomes eligible at exactly six days and wins as the longest fit.

    This is the answer to most "the price is wrong between X and Y" problems. Rows are cheap; the gaps between them are what cost you.
  </Accordion>

  <Accordion title="Add a hidden row for remainders">
    Remainders round up to your shortest row, so a table whose shortest row is 2 hours bills 2 hours for a 5-minute overrun.

    Hidden rows still price. Add a 30-minute row, mark it **Hidden**, and remainders round to 30 minutes instead — without offering a 30-minute rental on the storefront.
  </Accordion>

  <Accordion title="Constrain durations instead of pricing every one of them">
    Rate-based plus a **duration increment** gives you a finite, checkable price ladder without listing fixed prices. Set the increment to your shortest tier, or a divisor of it, and the durations that produce odd results stop being bookable.

    This is what makes rate-based practical for a catalog where fixed prices would mean thousands of rows.
  </Accordion>

  <Accordion title="Give at least one row a fallback price">
    If you use weekday or time-of-day restrictions, some stretch of some booking will eventually match no row. A **fallback price** on at least one row gives that stretch something to resolve against. Where several rows offer one, the cheapest applicable wins.
  </Accordion>
</AccordionGroup>

### Checking your table

Price a real booking on the storefront rather than reading the rows. Three durations catch nearly everything:

* **One just below a tier** — 23 hours on a day/week table. Shows whether your additional prices are set.
* **One just above a tier** — 25 hours. Shows what the remainder rounds up to.
* **One in the middle of your widest gap** — 19 hours on a 8h/24h table. Shows the worst case a customer can hit.

If any of the three costs more than a longer booking, the table needs another row or better additional prices.

## Related

<CardGroup cols={2}>
  <Card title="Price Tables" icon="table" href="/docs/concepts/catalog/price-tables">
    The full field reference and price resolution across tables
  </Card>

  <Card title="Build a price table" icon="wrench" href="/docs/guides/catalog/build-a-price-table">
    Setting one up step by step
  </Card>

  <Card title="Price a listing" icon="tag" href="/docs/guides/catalog/price-a-listing">
    Choosing a pricing shape for one listing
  </Card>

  <Card title="Booking limits" icon="sliders" href="/docs/catalog/listings/limits">
    Duration increments, minimums and maximums
  </Card>
</CardGroup>
