Dispatch now prices rides with a basic model inspired by public information on Uber and Lyft (docs/pricing.md): base + per mile + per minute, times a per-zone surge multiplier from recent requests against available drivers, plus a booking fee, with a minimum. Riders get an upfront quote per ride type and book against it; drivers see and earn a configurable share (75%) of the fare. Rate cards are per market, chosen by pickup location: San Francisco (third-party 2026 rates) and Fairbanks, AK (its own UberX and UberXL rates, Comfort from Anchorage, no Black). Everything is configurable via PRICING_FILE or POST /test/reset. Tests use a simple test card by default so auction scenarios keep their numbers; new scenarios cover SF rates, Fairbanks rates and surge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.9 KiB
Pricing model
RideRN prices rides with a basic model inspired by what's publicly known about Uber and Lyft. Their actual models are proprietary machine-learning systems; this is a transparent approximation to build on, not a copy. The code is server/pricing.mts; the dispatch server applies it (server/dispatch.mts).
What's publicly known
- Base formula. A base fare, plus a per-minute rate times estimated time, plus a per-mile rate times estimated distance, plus a booking fee, with a minimum fare. Rates differ by city and ride type. (Ridester, RideGuru)
- Surge / Prime Time. A multiplier on that base when demand outstrips drivers. Lyft applies Prime Time as a percentage of the subtotal (TaxiFareFinder). Measuring Uber over four weeks in San Francisco and Manhattan, Northeastern researchers found surge is set per zone (a jigsaw of areas), updated every 5 minutes, usually lasts under 10 minutes, and strongly reduces demand while barely drawing more drivers (paper, summary).
- Upfront pricing. Since 2016 Uber quotes a price before the trip, predicted from route, time of day and demand. In 2017 its "route-based pricing" used machine learning to estimate what groups of riders would pay for a route and time, so the same trip could cost more from a wealthier neighborhood (Bloomberg, Hoodline).
- Driver pay is separate. After Uber's 2023 dynamic pay change, a study of 1.5M UK trips found its cut rose from about 25% to 29%, larger on higher fares (Oxford, paper). Lyft guarantees drivers at least 70% of rider payments each week, and says they averaged about 88% after external fees (Lyft).
- APIs don't help. Uber's price-estimate endpoint is restricted to approved partners and its terms forbid competitive price comparison (Uber docs). Automated quote collection from their apps would breach their terms.
The model
fare = max(minimumFare, (baseFare + perMile × miles + perMinute × minutes) × surge + bookingFee)
earnings = fare × driverShare
- Rate card for the pickup's market and the ride type. A ride type with no card in a market isn't offered there.
- Surge for the pickup's zone (below). The booking fee isn't surged.
- Minimum fare applies to the total.
- The rider gets an upfront quote with a price per ride type. Booking uses the quote's price if it hasn't expired (2 minutes); otherwise dispatch sends fresh prices to confirm.
- The auction (see
docs/multi-phone-simulator.md) starts from the quoted fare: its rounds are multiples of it. - The driver sees and earns
driverShare(75%) of what the rider pays; the rest is the platform's cut.
Deliberately left out: pricing by predicted willingness to pay, i.e. by who the rider seems to be or where they're coming from. Surge by zone responds to supply and demand; neighborhood-based pricing is the practice that drew criticism, and a basic model doesn't need it.
Base rates (San Francisco)
Uber no longer publishes rate cards, so these come from third parties.
| Type | Base | Per mile | Per minute | Booking fee | Minimum | Source |
|---|---|---|---|---|---|---|
| UberX | $2.50 | $1.55 | $0.32 | $3.00 | $7.00 | RideWise, March 2026 (checked against receipts); minimum from TaxiFareFinder |
| Comfort | $3.00 | $1.85 | $0.38 | $3.00 | $9.00 | Estimate: scaled from reported national Comfort vs X averages |
| UberXL | $3.00 | $2.10 | $0.40 | $3.00 | $9.25 | Estimate: TaxiFareFinder SF base and minimum, national per-mile average |
| Black | $8.00 | $3.75 | $0.60 | $3.00 | $15.00 | Estimate: older SF Black rates and national averages |
Sources disagree: TaxiFareFinder lists older UberX rates of $2.00 + $1.21/mi + $0.22/min. Worth checking against a few real receipts before relying on any of them.
For the simulator's 5 km, 10 minute fixture trip these give UberX $13.52, Comfort $15.55, UberXL $16.52, Black $28.65.
Base rates (Fairbanks, AK)
From uphail.com's Fairbanks page (September 2026). Fairbanks lists only UberX and UberXL, so Comfort uses Anchorage's rates from the same source. Neither city lists Black, so Black isn't offered in Fairbanks.
| Type | Base | Per mile | Per minute | Booking fee | Minimum | Source |
|---|---|---|---|---|---|---|
| UberX | $2.77 | $1.86 | $0.31 | $2.55 | $8.43 | Fairbanks |
| Comfort | $3.74 | $1.92 | $0.40 | $3.58 | $11.75 | Anchorage (substitute) |
| UberXL | $3.25 | $2.25 | $0.36 | $2.55 | $8.70 | Fairbanks |
Other estimator sites disagree widely for Fairbanks: taxis-fare.com lists UberX at $2.55 + $1.75/km + $0.35/min, apparently per-mile figures labeled per km, and taxi-allo.com's figures don't fit the usual structure. Uphail's are the most complete and match what other searches return, but they're still third-party estimates: check them against real Fairbanks receipts.
For the 5 km, 10 minute fixture trip: UberX $14.20, Comfort $17.29, UberXL $16.39.
Markets
A market is a center and a radius; a pickup uses the rate cards of the nearest market it's within (marketFor in server/pricing.mts), or defaultMarket if none.
| Market | Center | Radius |
|---|---|---|
sf San Francisco |
37.7749, −122.4194 | 60 km |
fairbanks Fairbanks |
64.8378, −147.7164 | 60 km (includes North Pole and Fort Wainwright) |
The default market is San Francisco, so pickups anywhere else (Anchorage included) get San Francisco prices; that keeps the app usable in development wherever you are. A launch would rather refuse pickups outside its markets.
Surge
The city is a grid of zones, zoneSizeDegrees (0.02°, about 2 km) across. For the pickup's zone:
- demand = ride requests from that zone in the last
demandWindowMs(10 minutes) - supply = drivers there who are connected, driving, and have room for a ride (drivers report their position)
- multiplier = 1 + (demand ÷ max(supply, 1) −
threshold) ×sensitivity, rounded down tostep(0.1), between 1 andmaxMultiplier(3)
With the defaults (threshold 1, sensitivity 0.5): 2 requests for 1 driver → 1.5×; 3 for 1 → 2×; as many requests as drivers → no surge. A zone's multiplier is recomputed at most every intervalMs (60 s); the 2015 study measured Uber at 5 minutes. The rider sees "Busy area · prices are 1.5× higher right now".
Configuring it
Everything above is a setting, since the right values depend on the market:
| Setting | Default |
|---|---|
markets, defaultMarket |
San Francisco and Fairbanks; default sf |
rateCards |
{ sf: { x, comfort, xl, black }, fairbanks: { x, comfort, xl } } |
surge |
{ enabled: true, zoneSizeDegrees: 0.02, intervalMs: 60000, demandWindowMs: 600000, threshold: 1, sensitivity: 0.5, step: 0.1, maxMultiplier: 3 } |
driverShare |
0.75 |
quoteTtlMs |
120000 |
- At startup:
PRICING_FILE=path/to/pricing.json npm run dispatch, with any subset of the settings; rate cards merge per market and ride type, so a file can adjust one rate or add a market (with itsmarketsentry andrateCards). - Per test:
POST /test/resetwith{ "pricing": { ... } }, orcy.resetDispatch({ pricing, rates }). - By market: add a
marketsentry and itsrateCards;marketFor(pickup)picks it for pickups in range.
Tests default to a simple UberX card (TEST_PRICING in cypress/support/e2e.ts: $2.50 + $0.85/min, so the fixture trip is exactly $11.00) with surge off, so auction scenarios don't depend on the real rates. The base-rates, fairbanks-rates and surge-pricing scenarios cover the real cards, markets and surge.