observed · 42m · Jul 29, 12:01 AM CT

on this page19 sections

a data company first

The dataset behind every page, documented at the source.

Carconomics is observed, not advertised; the whole market, not one search; evidence, not estimates. This page documents every public read endpoint exactly as it behaves today — parameters, response shapes, freshness semantics, and failure states — then walks through each tool built on top and what its evidence does and does not mean.

data-surface.ts
endpoints     = 7 public GET read surfaces
source        = observed trip quotes · ORD + LAX (+ NYC sample)
last_observed = Jul 29, 12:01 AM CT
last_publish  = Jul 29, 12:05 AM CT
auth          = none required today · fair use
posture       = documents what exists, not an aspirational spec

01 / contract

Conventions#

Every endpoint below is the exact surface the site itself renders from. Same origin, same JSON, same honest unavailable states.

conventions.txt
base       = https://carconomics.com
format     = JSON over plain GET · no auth header
groups     = tesla (default) | luxury — never blended in one response
markets    = ord (Chicago O'Hare) | lax (Los Angeles LAX)
prices     = trip totals in USD, before taxes
timestamps = ISO 8601 UTC
dates      = ISO yyyy-mm-dd, always inside the published window

Group rule: an absent or unrecognized group parameter means Tesla on every surface, so legacy URLs keep working unchanged. Where a group must be explicit and valid (Picks, quant stats), an invalid value is HTTP 400 — never silently normalized.

03 / endpoint

GET /api/picks#

The current Rental Value rankings with their complete score evidence — or an honest unavailable state with a machine-readable reason.

GET/api/picks

Top Value Picks per market: score, rank, axis evidence, robustness passes, and the vehicle card fields, for the current genuine observation.

paramtypedefaultnotes
group"tesla" | "luxury"teslainvalid or repeated = 400 invalid_group
market"ord" | "lax" | "both"bothinvalid or repeated = 400
picks-response.jsonc
{
  "status":          "ok",              // or "unavailable" + reason (still HTTP 200)
  "group":           "tesla",
  "formulaVersion":  "…",               // exact Rental Value formula generation
  "contentId":       "…",
  "generationId":    "…",
  "observedThrough": "<ISO timestamp>",
  "publicationKind": "observation",     // or curation_rebuild | repush
  "markets": [ {
    "market":      "ord",
    "marketLabel": "Chicago · ORD",
    "evidence":    { "status": "supported", "reasons": [], "marketFreshShare": 0.99,
                     "baseEligiblePopulation": 240, "rankedPopulation": 118 },
    "rows": [ {
      "listingId": "1543210", "score": 41, "rank": 1, "cohortSize": 118,
      "topValuePick": true, "percentile": 0.99,
      "peerKind": "model", "peerValue": "Model 3", "peerCount": 62,
      "axes": { /* five fixed axes, each { value, score } */ },
      "representativeQuote": { "startDate": "<ISO date>", "rentalDays": 3, "total": 141, "dailyRate": 47 },
      "robustness": [ /* three leave-a-week-out reruns with their own verdicts */ ],
      "vehicle": { "make": "Tesla", "model": "Model 3", "year": 2023, "city": "…",
                   "rating": 4.9, "trips": 112, "allStar": true, "image": "…", "url": "…" }
    } ]
  } ]
}
curl
$ curl -s "https://carconomics.com/api/picks?group=tesla&market=ord" \
    | jq '{status, top: .markets[0].rows[0] | {listingId, score, rank, topValuePick}}'
{ "status": "ok", "top": { "listingId": "1543210", "score": 41, "rank": 1, "topValuePick": true } }

freshness · Picks fail closed on source age: an observation older than the group’s stale threshold returns status: "unavailable" with reason stale-source-observation (or invalid-observation-timestamp / future-observation-timestamp); a group published without a picks artifact reads not-yet-published. Old rankings are never served as current.

statusbodymeaning
400{ "status": "invalid", "error": "invalid_group", "retryable": false }group was supplied but is not tesla/luxury, or was repeated
400{ "status": "invalid", "message": "Choose ORD, LAX, or both markets." }market was supplied but is not ord/lax/both
503{ "status": "unavailable", "message": "…" }KV unreachable, or the payload exceeded the fixed 128 KiB response budget; search stays available

04 / endpoint

GET /api/quant/stats#

A bounded projection of the duration-specific Stats evidence: one group, one market, one exact trip length per request.

GET/api/quant/stats

Reproducible panel evidence — current listing-equal cohorts plus history series with explicit supported/thin/unavailable/collecting states.

paramtypedefaultnotes
group"tesla" | "luxury"teslainvalid or repeated = 400 invalid_group
market"ord" | "lax"ordinvalid or repeated = 400 invalid_market
lengthsupported int3must be one of the group's supported lengths (tesla 1–14, luxury 1–8); anything else = 400 invalid_length, never silently normalized
quant-stats-response.jsonc
{
  "formulaVersion": "quant-stats-1.0",
  "status":         "ok",              // or "collecting" + reason before the first verified publication
  "group": "tesla", "market": "ord", "rentalDays": 3,
  "contentId": "…", "generationId": "…", "observedThrough": "<ISO timestamp>",
  "support": { "tier": "supported",    // supported ≥30 listings · thin 10–29 · unavailable <10 or <95% fresh
               "listings": 236, "freshListings": 234, "freshShare": 0.99, "reason": "…" },
  "current": {
    "selectedDuration":  { "medianPerDay": 52, "p10": 38, "p50": 52, "p90": 84,
                           "listings": 236, "ciLo": 49, "ciHi": 55, "complete": true },
    "durationCurve":     [ /* listing-equal median $/day per exact length */ ],
    "crossMarketPriceGap": [ /* same-model ORD vs LAX medians, each with its own interval */ ],
    "allStarShare":      { /* separately gated badge share + price comparison */ },
    "listingEqualCapturedDiscount": { /* p25/p50/p75 of captured host discounts */ }
  },
  "history": {
    "selectedDurationStatus": { "state": "ready", "completeDays": 21, "requiredDays": 14 },
    "selectedDuration":       [ /* daily p10/p50/p90 for this exact duration */ ],
    "repricingStatus":        { "state": "collecting", "requiredQualifyingDays": 28, "lookbackDays": 90 },
    "marketIndexStatus":      { "state": "ready", "supportedPoints": 34, "formulaVersion": "…" },
    "marketIndex":            [ /* same-listing daily index chain, reasons included */ ]
  },
  "definitions": { "quoteWindow": "…", "support": "…" }   // the vocabulary, in the payload itself
}
curl
$ curl -s "https://carconomics.com/api/quant/stats?group=tesla&market=ord&length=3" \
    | jq '{status, tier: .support.tier, medianPerDay: .current.selectedDuration.medianPerDay}'
{ "status": "ok", "tier": "supported", "medianPerDay": 52 }

freshness · the projection is computed from the latest verified analytics-v2 publication; unsupported cohorts arrive as explicit states with reasons, never as silently substituted durations or quote-weighted fallbacks.

statusbodymeaning
400{ "status": "invalid", "error": "invalid_group" | "invalid_market" | "invalid_length", "retryable": false }the exact failing parameter, named; repeated parameters also 400
503{ "status": "error", "error": "stats_unavailable", "retryable": true }the stats snapshot could not load
503{ "status": "error", "error": "stats_projection_too_large", "retryable": true }the payload exceeded the fixed 128 KiB cap (actual size is advertised via x-stats-bytes on success)

05 / endpoint

GET /api/listing-calendar#

One tracked listing's complete quote surface — every observed (pickup date × trip length) cell as compact tuples, with the vocabulary inputs needed to render it honestly.

GET/api/listing-calendar

The premium price-and-availability calendar's data: per-cell price, carried flag, and fair-value fields when (and only when) the snapshot's pricing validated.

paramtypedefaultnotes
listing1–12 digit idrequiredthe tracked listing; anything else = 400 listing_required
group"tesla" | "luxury"teslaabsent/invalid = tesla
listing-calendar-response.jsonc
{
  "status":       "ok",               // or "untracked" — this id is not in the current snapshot
  "group":        "tesla",
  "contentId":    "…", "generationId": "…",
  "listingId":    "1543210",
  "market":       "ord",
  "urlBase":      "https://turo.com/…",
  "windowStart":  "<ISO date>",       // UNclamped market window start — the offset base
  "windowDays":   60,
  "supportedLengths": [1, …, 14],
  "failedOffsets": [17],              // pickup dates the collection could not verify — disclosed, not smoothed
  "capHits":      0,
  "observedThrough": "<ISO timestamp>",
  "staleMinutes": 24,
  "pricingValidated": false,          // false = price-only calendar; fair/save are 0
  "todayISO":     "<market-local today>",  // request-time; cells before it render as the no-claim past band
  "cells": [
    // [dayOffset, days, price, carried, fair, save] — one tuple per observed cell
    [12, 3, 141, 0, 0, 0],
    [12, 7, 289, 0, 0, 0],
    [13, 3, 150, 1, 0, 0]             // carried=1: last-seen price, not fresh this refresh
  ]
}
curl
$ curl -s "https://carconomics.com/api/listing-calendar?listing=1543210" \
    | jq '{status, windowDays, cells: .cells | length, pricingValidated}'
{ "status": "ok", "windowDays": 60, "cells": 412, "pricingValidated": false }

freshness · carried rows ship with their flag and past-dated rows ship for the past band — the provider owns the honesty of the evidence, the client owns state rendering. A missing cell means no quote observed, never a claim the car cannot be booked.

statusbodymeaning
400{ "error": "listing_required" }listing is absent or not a 1–12 digit id
503{ "status": "unavailable" }live bindings exist but the snapshot could not load; Retry-After: 30

06 / endpoint

GET /api/preview-search + /api/preview-listing#

The NYC preview lane: a sampled Tesla tier around Newark, JFK, and Manhattan with the same planner math and a deliberately weaker, price-only vocabulary.

GET/api/preview-search

Run the shared flex planner over the sampled NYC quote matrix; one card per car with its best matching quote.

paramtypedefaultnotes
targetDateISO dateresolved from boundsclamped into the sample's window
startFlexint 0–105± days of pickup flexibility
minLength / maxLengthint4 / 0same No-max sentinel as the main planner
sortprice | perday | year | ratingpriceno value, no distance — the sample carries neither fair value nor origins
offsetint 0–20000page offset (page size 40)
GET/api/preview-listing

One sampled car's quote cells for the preview calendar; three-state vocabulary only.

paramtypedefaultnotes
listing1–12 digit idrequireda sampled NYC listing; anything else = 400 listing_required
preview-responses.jsonc
// /api/preview-search
{ "status": "ok", "marketId": "nyc", "observedAt": "<ISO timestamp>", "staleMinutes": 41,
  "sampled": { "capHits": 3, "capped": true },   // the sample publishes WITH page-cap hits
  "bounds": { "minDate": "…", "maxDate": "…" },
  "query": { /* echo */ }, "results": [ /* ≤40 cards */ ],
  "totalMatches": 96, "totalOptions": 5210, "totalCars": 129, "pageSize": 40 }

// /api/preview-listing
{ "status": "ok", "listingId": "…", "windowStart": "<ISO date>", "windowDays": 60,
  "lengths": [1, …, 14], "urlBase": "https://turo.com/…",
  "cells": [ [12, 3, 141], … ] }     // [dayOffset, days, price] — no carried/fair tuple slots

// either endpoint, any failure
{ "status": "pending" }              // best-effort surface; it never 500s the planner

freshness · sampled.capped is the tier’s defining disclosure: the sample is bounded by design, so a car or cell missing from it is a sampling fact, never an availability or market-coverage claim. Preview never computes fair value.

statusbodymeaning
400{ "error": "listing_required" }preview-listing only: listing is absent or malformed
200{ "status": "pending" }the sample blob is not readable right now — the honest fallback for every failure
200{ "status": "untracked", "listingId": "…" }preview-listing: the id is not in the current sample

07 / endpoint

GET /api/dashboard-context#

The small provenance-plus-drops payload the dashboard hydrates from: publication identity and the freshest detected price decreases.

GET/api/dashboard-context

Current publication ids and timestamps for one group, plus up to 12 recent listing-level price-drop events.

paramtypedefaultnotes
group"tesla" | "luxury"teslaabsent/invalid = tesla; arbitrary extra parameters are ignored for caching
dashboard-context-response.jsonc
{
  "source":          "live",           // "empty" = no publication yet
  "group":           "tesla",
  "contentId":       "…",
  "generationId":    "…",
  "checkedAt":       "<ISO timestamp>",
  "publishedAt":     "<ISO timestamp>",
  "observedThrough": "<ISO timestamp>",
  "drops": [ {                         // ≤12, listing-level, expire after 48h
    "listingId": "1543210", "market": "ord", "model": "Model 3", "year": 2023,
    "startDate": "<ISO date>", "rentalDays": 3,
    "oldPrice": 178, "newPrice": 141, "pct": 0.21,
    "droppedAt": "<ISO timestamp>", "city": "…", "image": "…", "url": "…"
  } ]
}
curl
$ curl -s "https://carconomics.com/api/dashboard-context?group=tesla" \
    | jq '{source, observedThrough, drops: .drops | length}'
{ "source": "live", "observedThrough": "<ISO timestamp>", "drops": 12 }

freshness · the Worker keeps a 45-second shared edge copy per group (X-Carconomics-Edge-Cache: HIT|MISS); the browser response itself is always no-store. There are no error shapes — an empty publication reads as source: "empty" with blank ids.

08 / contract

Freshness & provenance#

Every payload separates when prices were observed from when the artifact was published, and names the immutable snapshot it was computed against.

provenance.contract
observedThrough / checkedAt = when the prices were actually observed
publishedAt                 = when this exact artifact was committed
staleMinutes                = minutes since the observation, not the HTTP response
snapshotId / contentId      = immutable — same id ⇒ byte-identical evidence
generationId                = the collection generation behind the content

Thresholds: Tesla collection runs about hourly; a Tesla observation older than 180 minutes (three missed runs) is stale, and age-gated surfaces like Picks fail closed rather than serve it as current. Luxury’s threshold is 720 minutes, and a deliberately paused group ages by design — the site says so in prose instead of raising a false alarm. A new publication always mints a new snapshotId; search cursors are bound to it, which is exactly why a mid-pagination publication produces the 409 restart instead of a silently inconsistent page.

Carried vocabulary: inventory can flicker. A quote missing from one refresh is carried briefly with an explicit carried flag and dropped after repeated misses; fresh-only surfaces (Host Desk, action signals) exclude carried rows entirely. Nothing on any surface infers demand, bookings, or occupancy from disappearance.

09 / contract

Rate limits & caching#

The current posture, stated honestly: open fair-use reads, no keys, small edge windows, and no support yet for heavy programmatic volume.

caching.today
browser        = every JSON response is Cache-Control: no-store
edge (search)  = first-page GETs (no cursor/offset, no location params)
                 keep a 45s shared copy · X-Carconomics-Cache: HIT|MISS|BYPASS
edge (context) = /api/dashboard-context keeps a 45s copy per group
origin         = ~10-minute in-memory snapshot memo per group behind every read
keys           = none required today · no rate-limit headers are sent

Fair use: these endpoints exist so anyone can verify what the site claims, and light scripted reads are welcome. Location parameters are session-only and bypass shared caching entirely. Heavy programmatic use — bulk export, sustained polling, product integrations — is not supported on the current infrastructure; the search page’s bounded pagination is deliberate, not an obstacle to route around.

10 / contract

Errors#

The complete set of shapes actually returned. Invalid input is named precisely; infrastructure trouble is retryable; missing evidence is a state, not an error.

statusbodymeaning
400{ "status": "invalid", "error": "invalid_group" | "invalid_market" | "invalid_length", "retryable": false }picks + quant stats: the exact failing parameter; invalid input is never silently normalized
400{ "error": "listing_required" }listing-calendar + preview-listing: a missing/malformed listing id
409{ "error": "search_cursor_invalid", "code": "…", "restart": true }search only: the continuation outlived its snapshot — restart from page one
503{ "error": "search_unavailable", "retry": true }search: snapshot load failed with live bindings present; Retry-After: 30
503{ "status": "unavailable" | "error", … }picks / stats / calendar: unreachable storage or a breached fixed response budget (128 KiB caps on picks and quant stats)
200{ "status": "unavailable" | "collecting" | "pending" | "untracked", … }not an error: the evidence honestly does not support a result yet, with a machine-readable reason where one exists

Reading the last row: unavailable states are first-class results. A stale source observation, an unpublished picks artifact, or a still-collecting history panel all arrive as HTTP 200 with an explicit status — the same states the site renders — so a client that only checks HTTP codes will never mistake missing evidence for a working answer.

11 / contract

Keys & access#

Everything above documents what exists today. This card is the single exception.

API keys & programmatic access — coming soon. Today every read surface on this page is open at fair-use volumes with no key. If you build against it, keep request rates polite, honor the unavailable states, and expect the shapes documented here — they are the same ones the site itself consumes.

12 / guide

The Rentals planner#

Every search is a fully specified plan: which pickup dates, which trip lengths, which cars — resolved identically on the client and the server.

Flexible dates, exact totalsopen rentals

Flexible pickup mode starts from one target pickup date and widens it by ±0–10 days; exact date range mode tests every trip that starts and drops off inside the chosen window. The length chips set a hard minimum trip length and an optional maximum — No max searches through the current data ceiling (14 days for Tesla). Model chips filter Tesla by model and Luxury by make, and Find the cheapest dates asks the broad question: every stored pickup date across all supported lengths, or one selected length.

Every displayed number is a real quoted trip total before taxes, quoted independently per duration — because host discounts make the price curve uneven, a 5-day trip can genuinely cost less than a 4-day one. A shared URL reproduces the exact same plan, so what you send someone is what they see.

13 / guide

By car vs by exact trip#

Two projections of the same matched set: shortlist vehicles first, or pin the exact pickup and length directly.

One matched set, two viewstry by exact trip

By car shows one card per vehicle carrying its cheapest matching trip, with a matches count for that car’s other qualifying options. By exact trip expands every (car × pickup date × length) match into its own row — useful once the vehicle is chosen and the question is purely which dates. Both views rank the identical matched set; the gap between totalVehicles and totalTrips in the response is the real depth of options, not padding. Sorting and filters apply before either projection, so the highlighted picks agree across views.

14 / guide

The price & availability calendar#

One listing's entire observed quote surface on a calendar — with a vocabulary that refuses to claim more than the evidence shows.

Every observed cell, honestly labeledopen any car card

Open any car card and the calendar shows every observed pickup date for the selected trip length. Color encodes price within that one trip length — switch lengths and the scale recomputes, so shades are never comparable across lengths. A gold cell is a last-seen price carried across a briefly missed refresh; a dot means no quote was observed for that cell, which is a collection fact and never a claim the car cannot be booked. Collection-failed dates are listed outright rather than smoothed over.

Fair-value shading joins the calendar only when the snapshot’s pricing model validated; otherwise it stays price-only rather than borrowing an unverified model. Clicking a cell links to the listing with those exact dates prefilled.

15 / guide

Top Picks#

A fixed, rules-based ranking of resilient rental-price value. Placement cannot be bought, and weak evidence removes the badge instead of softening it.

Price-only, robustness-testedopen picks

Rental Value scores a price basket — pickup offsets 7–27 across 1, 3, and 7-day trips — on five fixed axes, then requires a top rank, three leave-a-week-out robustness passes, and qualification in two consecutive genuine observations before the Top Value Pick badge appears. Ratings, host identity, distance, clicks, sponsorship, and commissions never enter the score or the order. When the source observation is stale or invalid the whole ranking goes unavailable rather than serving yesterday’s picks as today’s. The full axis and robustness evidence ships in the API payload, so every badge is checkable.

16 / guide

Trends & stats#

Duration-specific, listing-equal market evidence with explicit support tiers — the page tells you when a cohort is too thin to trust.

Supported, thin, or honestly absentopen stats

Every panel answers for one market and one exact trip length, and one listing gets one vote regardless of how many quotes it has. Support is tiered: fewer than 10 listings (or under 95% fresh) is unavailable, 10–29 is thin, 30 or more is supported. The cheapest-dates heatmap normalizes color within each trip-length row, so intensity compares dates inside a row, never across rows. History panels stay in a collecting state until their day-count and completeness gates pass — no gap-filling, no substituted durations. Each panel states what it can and cannot prove; “after controls” is an association, not causation.

17 / guide

Host Desk#

Competitive context for one tracked listing from fresh like-for-like peers — and deliberately nothing about demand or revenue.

Fresh peers, bounded claimsopen host desk

Paste a tracked listing and Host Desk builds a peer band from fresh, same-market, same-vehicle listings — trying a supported ±2-model-year cohort first, then widening honestly. Supported bands expose p25/median/p75 and your percentile; thin cohorts show only observed min/median/max and never enter the supported-only headline. Carried (last-seen) quotes are excluded — a competitive read deserves fresh evidence only. The scope is deliberately narrow: observed prices and discount overshoots, never demand, occupancy, revenue, or a recommended price, because the data cannot support those claims.

18 / guide

The energy study#

Observed rental prices joined to official EPA and EIA energy data — with the rental and energy components always shown separately.

Official sources or nothingopen the study

The Luxury EV-vs-gas comparison combines each listing’s observed three-day rental median with an adjustable trip-energy scenario built strictly from official sources: EPA efficiency for the exact year/make/model and EIA metro gasoline and state residential electricity prices. Miles and electricity cost are yours to adjust; the rental and energy components stay visibly separate so neither can hide inside the other. The per-listing Tesla calculator is the same discipline for one car. Missing, stale, or ambiguous official inputs produce a collecting state — the study never invents an efficiency number, and an energy failure can never delay price publication.

19 / guide

The NYC preview#

A sampled Tesla tier around Newark, JFK, and Manhattan — the same planner, a smaller sample, and a vocabulary that says so.

Sampled tier, same mathopen the preview

The preview runs the exact planner mechanics — same combo expansion, same length resolver, same sorting code — over a bounded NYC sample instead of a complete market sweep. The sample publishes with its page-cap hits disclosed, so a car or quote missing from it is a sampling fact, never an availability claim. Its vocabulary is deliberately weaker than the live markets’: price-only, no fair value, no distance sort, three calendar states instead of six. It exists to show the mechanism on a new market honestly before that market earns full collection.

Prices can change at any moment. Always confirm the final listing before booking. Sample values in response snippets are illustrative — run the curl for real ones. Carconomics is not affiliated with Turo, EPA, EIA, or any vehicle manufacturer.