The marketplace
XGM_KERNEL=amazon runs the desk's market as one continuous online marketplace with a fulfilment network, seen
from the seat of a third-party wholesale seller: you buy products from suppliers, ship them into the fulfilment
network, set your own prices (or let the default repricer do it), and the network sells, ships and returns them
hour by hour, charging the marketplace's published fee card. The money is held in a seller balance and released
to your cash by settlement statements. Everything else on the site (carts, checkout, payments, agents, the record, the
console, owned goods and the exchange) works as it does on the platform's other markets.
One market and the marketplace clock
There is one market, the US Marketplace (id us). It runs on a marketplace clock: one tick is one
marketplace hour, and the clock runs XGM_TIME_SCALE times faster than real time β by default 24, so one real
hour is one marketplace day, a marketplace week passes in 7 real hours and a tick closes every 2 minutes 30 seconds
of real time (interval "2m30s"). At time scale 1 the marketplace runs in real time. Every date the marketplace
shows is a marketplace date: tick t opens at the marketplace's epoch (2026-01-01 00:00, a Thursday) + t hours, and
the market's seasons (the peak surcharge, fourth-quarter storage, slow receiving) follow the weeks of that marketplace
year. A marketplace day runs from midnight to midnight: ticks 24d to 24d + 23 are day d.
GET /api/v1/markets/us says where the clock is: current_tick, tick_sim_at (the marketplace date and time the
open hour began), sim_now, time_model: "continuous", ticks_per_day: 24, time_scale, and when the open hour
closes in real time (tick_closes_at, seconds_to_close). The menu, history and tape rows, orders and the record carry
sim_at next to their tick. Things a person must act on stay in real time: an order's payment deadline
(closes_at), the notice of a fee amendment, the monthly participation fee, payouts and refunds of real money, and
the terms.
What happens every marketplace hour (at the close of each tick):
- new supplier offers appear (each stays open for days, below) and the desk decides on each once, when it appears;
- every order paid in this hour or before fills as a lot; unpaid orders whose payment window or offer ended expire;
- for every account's stock: lots due arrive (and become sellable), removals requested this hour are carried out,
the price in effect meets the hour's buy box and the stock sells its share of the hour's sales, first in first
out; customer refunds and returned units due this hour are worked. The money of these events accrues on the
lot (
accrued_today, by kind) until the day closes.
What happens at the end of every marketplace day (the close of its last hour, 23:00β24:00):
- each lot's money of the day is posted to the seller balance β one ledger entry per lot and kind (
sale_credit,referral_fee,fulfilment_fee, β¦, noteday N) β together with the day's storage, aged-inventory and utilization charges of its on-hand units; - the record gets its daily entries (units sold and the buy box of the day), and lots with nothing left and no return still on its way settle;
- every 14th day, settlement statements release the seller balance (below).
Until a day closes, what a lot earned that day shows as accrued_today on GET /api/v1/positions and on the stock
view; its cash_flows, pnl_so_far and the seller balance count posted money only.
The listings and what an offer shows
The market itself is the wholesale market's: about a thousand listings in twelve categories, built on real FMCG
products of a real wholesale seller's assortment (real titles, brands, product types, reference prices, size tiers
and case packs; every ASIN, MSKU, FNSKU and UPC lightly altered, so none is a real identifier; no real sales, sellers or
costs). Products ramp up, sell and phase out over years; a listing's next product takes over under a new SKU. Each
listing carries its category's typical crowd of competing sellers with their repricers, a sticky buy box price that
sinks when the crowd grows and recovers when it thins, and a sales rank among every product of the marketplace
category. Demand follows the hour of the day and the day of the week (quiet nights, an evening peak) and the buy box:
the buy box and the crowd change at some hour of the first marketplace day of each week (the epoch's weekday,
Thursday), and from that hour the listing's demand moves with the new price. The categories' demand seasons follow
the marketplace calendar like the fee seasons. The rank is published an hour late (rank_as_of); sales_rank_avg
and rank_drops cover the last 30 days, rank_drops_long the last 90. Sales velocities are never published, in
any form.
A deal is a supplier's offer of a product (a single item, a multipack or a bundle). Offers appear at every hour and
stay open for days β from one day to three weeks, never beyond the product's time on the listing; the menu
(GET /api/v1/markets/us/deals) shows every offer open now, each viewed at the current hour. The offer's terms
(unit_cost, max_qty) are fixed when it appears; its facts (the buy box, signals, fees, lead time, net margin) are
those of the current hour. Besides the wholesale facts (unit_cost, list_price = the hour's buy box price,
sales_rank, rank_as_of, sales_rank_avg, rank_drops, rank_drops_long, nsellers, sku, asin, msku,
fnsku, upc, family, listing_type, category, category_code, brand, pack, case_pack, max_qty) it shows:
| field | meaning |
|---|---|
key, tick |
the offer's key (us-<hour it appeared>-<n>) and the hour it appeared |
valid_until, hours_left, sim_valid_until |
the first hour it is no longer open (a tick), the marketplace hours left, and that time as a marketplace date |
size_tier |
small_standard, large_standard, small_bulky or large_bulky (from the product's weight and dimensions) |
unit_weight_lb, dimensions_in, cubic_feet, shipping_weight_lb |
the product's physicals (below) |
units_per_carton |
units in one inbound carton |
prep |
{label, kit, per_unit}: whether the unit needs a label (and kitting, for multipacks and bundles) and the seller-side prep charge per unit (below) |
referral_rate, referral_fee, referral_min |
the referral fee at the buy box price (the rate is the fee / price, the minimum included) |
fulfilment_fee, fulfilment_base, peak_surcharge, fuel_surcharge |
the fulfilment fee per unit sold at the buy box price this hour, and its parts |
fba_fee |
the same fulfilment fee (the key the wholesale pages read) |
fees_at_price |
referral + fulfilment per unit sold at the buy box price |
storage_per_day, storage_per_month |
the storage of one unit on hand for a marketplace day (and a month), at this week's rate |
peak_season, fee_schedule |
whether the peak surcharge applies this week; the fee schedule version in force |
inbound_route, inbound_per_unit |
the default route and its inbound cost per unit for the offer's max_qty |
inbound_routes[] |
every route: {route, label, available, per_unit, lead_typical, lead_max, reason?}, lead times in days |
lead_time |
{typical, min, max, distribution, unit: "days"}: the product's lead time in days by the default route for an order filled now |
return_threshold |
the category's returns-processing threshold (below) |
net_margin |
list_price β fees_at_price β unit_cost β inbound_per_unit: before storage, returns and anything unsold |
GET /api/v1/markets/us β rules publishes the whole rule book: the fee schedule in force (rules.fees.schedule,
its schedule_version and digest), storage per cu ft and month and day, the categories with their referral tiers and
return thresholds, the calendar of the marketplace year, the inbound routes and lead times in days, the stock, pricing
and payout rules in days and hours, the capacity rule and the pending fee amendments.
Buying: the payment window and the fill
You order an open offer (POST /api/v1/orders or a cart line) with a quantity, an inbound route and, if you like, a
starting pricing rule. An account may order at most the offer's max_qty across all its orders of that offer. The
order is a reservation: it must be paid within one real hour of placing it and while its offer is still open,
whichever comes first β closes_at is the real time that window ends and expires_tick the first marketplace hour
starting at or after it. Once paid (the hour it was paid in is its paid_tick), it fills at the close of that
marketplace hour as a lot, at the offer's unit cost plus the route's inbound fee. An unpaid order expires at the
close of the hour before expires_tick. The fulfilment-network capacity (below) is checked when you order and again
when you pay, before any money moves (a card is charged, or a hosted payment opened, only once it fits); a payment
confirmed by the provider is never refused for capacity.
The fee card
The fees below are the defaults (2026 approximations of a large marketplace's US fee card, in the desk's currency).
The schedule in force is always the one in rules.fees.schedule; it changes only by announced amendments (below).
Per-event fees are rounded to the cent per unit; storage, aged-inventory and utilization charges accrue hour by hour
on the units on hand and are posted once a day in whole cents (the fraction of a cent is carried to the next day, so
a month of daily charges adds up to the month's charge).
Referral fee β per unit sold
A share of the whole sale price at the rate of the tier the price falls in (so a price just over a tier's limit pays the higher rate on all of it), never less than the minimum per unit:
| category | rate | minimum |
|---|---|---|
Grocery & Gourmet Food (GRO) |
8 % up to 15.00, 15 % above | none |
Beauty & Personal Care (BPC), Health & Household (HHS), Baby Products (BAB) |
8 % up to 10.00, 15 % above | 0.30 |
Automotive (AUT) |
12 % | 0.30 |
every other category (TLS, ACS, OFF, PET, HOM, GAR, SPO) |
15 % | 0.30 |
For example, grocery at 15.00 pays 1.20 and at 15.01 pays 2.25; beauty at 3.00 pays the 0.30 minimum.
Fulfilment fee β per unit sold
By size tier, shipping weight and the sale price's band (under 10.00 / 10.00β50.00 / over 50.00). The shipping weight is the unit weight for small standard; for the other tiers the greater of the unit weight and the dimensional weight (length Γ width Γ height / 139, bulky sides counted at least 2 in).
| small standard, up to | < 10 | 10β50 | > 50 |
|---|---|---|---|
| 2 oz (0.125 lb) | 2.43 | 3.32 | 3.58 |
| 4 oz | 2.50 | 3.39 | 3.65 |
| 6 oz | 2.56 | 3.45 | 3.71 |
| 8 oz | 2.67 | 3.57 | 3.83 |
| 10 oz | 2.77 | 3.68 | 3.94 |
| 12 oz | 2.83 | 3.77 | 4.03 |
| 14 oz | 2.89 | 3.87 | 4.13 |
| 1 lb | 2.95 | 3.96 | 4.22 |
| large standard, up to | < 10 | 10β50 | > 50 |
|---|---|---|---|
| 0.25 lb | 2.91 | 3.73 | 3.99 |
| 0.5 lb | 3.20 | 4.02 | 4.28 |
| 0.75 lb | 3.49 | 4.31 | 4.57 |
| 1 lb | 3.78 | 4.60 | 4.86 |
| 1.25 lb | 4.10 | 4.92 | 5.18 |
| 1.5 lb | 4.43 | 5.25 | 5.51 |
| 1.75 lb | 4.75 | 5.57 | 5.83 |
| 2 lb | 4.97 | 5.79 | 6.05 |
| 2.25 lb | 5.19 | 6.01 | 6.27 |
| 2.5 lb | 5.41 | 6.23 | 6.49 |
| 2.75 lb | 5.63 | 6.45 | 6.71 |
| 3 lb | 5.85 | 6.67 | 6.93 |
| 20 lb | 6.15 + 0.08 per started 0.25 lb above 3 lb | 6.97 + 0.08 β¦ | 7.23 + 0.08 β¦ |
| bulky, up to 50 lb | < 10 | 10β50 | > 50 |
|---|---|---|---|
| small bulky | 6.73 + 0.38 per started lb above 1 lb | 7.55 + 0.38 β¦ | 7.81 + 0.38 β¦ |
| large bulky | 8.53 + 0.38 per started lb above 1 lb | 9.35 + 0.38 β¦ | 9.61 + 0.38 β¦ |
Peak surcharge from mid-October to mid-January (weeks 41 to 1 of the marketplace year inclusive, counted 0β51 as
rules.calendar.week_of_year from the marketplace date: week 0 is 1β7 January, week 51 runs from 24 to 31 December): small standard +0.19 up to 0.25 lb,
+0.23 up to 1 lb; large standard +0.26 up to 1 lb, +0.33 up to 3 lb, +0.48 up to 20 lb; small bulky +0.80; large bulky
+1.30. A fuel and logistics surcharge of 3.5 % applies to the base fee and the peak surcharge. Example: a
small-standard unit of 0.1 lb sold at 8.00 pays 2.52 (2.71 in peak season), at 12.00 pays 3.44.
Storage β per cubic foot of units on hand
| JanuaryβSeptember | OctoberβDecember (weeks 39 to 51) | |
|---|---|---|
| standard tiers | 0.78 per cu ft and month | 2.40 |
| bulky tiers | 0.56 | 1.40 |
Storage is charged per marketplace day on the units on hand hour by hour (from the hour a lot arrives to the hour
its units leave): cu ft Γ the month's rate Γ 12 / 365 a day. A 0.1 cu ft standard unit pays 0.0026 a day off-season
and 0.0079 in the fourth quarter. The rate follows the week of the marketplace year (rules.calendar);
rules.fees.storage_per_cuft_day publishes the day's rate.
Aged-inventory surcharge, on top of storage, by the age of the units since they arrived (per cu ft and month; from a year on at least a minimum per unit and month):
| age (days) | 181β210 | 211β240 | 241β270 | 271β300 | 301β330 | 331β365 | 366β455 | 456 + |
|---|---|---|---|---|---|---|---|---|
| per cu ft / month | 0.50 | 1.00 | 1.50 | 5.45 | 5.70 | 5.90 | 6.90 (min 0.30 / unit) | 7.90 (min 0.35 / unit) |
The age is the lot's whole marketplace days in stock at the end of the day.
Storage utilization surcharge, on top of storage, when the account's stock is slow for its sales: the weeks of supply = the average cu ft on hand over the last 90 days / the average cu ft shipped per week over them. Above 22 weeks every unit on hand pays per cu ft and month:
| weeks of supply above | 22 | 28 | 36 | 44 | 52 |
|---|---|---|---|---|---|
| standard tiers | 0.44 | 0.76 | 1.16 | 1.58 | 1.88 |
| bulky tiers | 0.23 | 0.46 | 0.63 | 0.76 | 1.30 |
Exempt: accounts storing under 25 cu ft on average, and accounts within 52 weeks (364 days) of their first lot on the market.
The monthly invoice view. Storage, aged-inventory and utilization charges are posted daily; the stock view
(GET /api/v1/stock β invoices, and the console) groups them by marketplace month:
{account_id, market, month ("YYYY-MM"), days, from_tick, to_tick, by_kind, total}.
Low-inventory-level fee β per unit shipped
When a SKU's days of supply run short β days of supply = the unit-days on hand / the units shipped, over the last 30 days and over the last 90 (today's hours included), taking the greater of the two β every unit shipped pays:
| days of supply under | 14 | 21 | 28 |
|---|---|---|---|
| small standard (up to 1 lb) | 0.89 | 0.63 | 0.32 |
| large standard up to 3 lb | 0.97 | 0.70 | 0.36 |
| large standard up to 20 lb | 1.11 | 0.87 | 0.47 |
| small bulky | 1.85 | 1.02 | 0.51 |
| large bulky | 2.09 | 1.15 | 0.57 |
Exempt while the SKU shipped fewer than 20 units over the last 7 days, for a new product β within 26 weeks (182 days) of the first arrival of the SKU on the account, however often it sold out since β and for a new seller: within 52 weeks (364 days) of the account's first lot on the market. Keeping stock in the pipeline avoids it; keeping too much brings the storage and utilization charges.
Removal and disposal β per unit
| shipping weight up to | 0.5 lb | 1 lb | 2 lb | above 2 lb |
|---|---|---|---|---|
| standard tiers | 0.84 | 1.53 | 2.27 | 2.89 + 1.06 per started lb above 2 lb |
| shipping weight up to | 1 lb | 2 lb | 4 lb | 10 lb | above 10 lb |
|---|---|---|---|---|---|
| bulky tiers | 3.12 | 4.43 | 6.82 | 10.12 | 13.84 + 1.06 per started lb above 10 lb |
Disposal costs the same as removal by default (its own table in the schedule).
Returns β refund administration and returns processing
When a customer returns a unit, the sale price is refunded (refund), the referral fee is refunded
(referral_refund) less the refund administration fee β 20 % of that referral fee, at most 5.00 per unit
(refund_admin_fee) β and the fulfilment fee is not refunded.
Returns processing fee, per returned unit: charged only on a SKU whose return rate over the last 28 days (returned / shipped) is above its category's threshold, with at least 25 units shipped in that window, and only for the returns above the threshold.
| category | GRO | OFF | BPC | HHS | GAR | HOM | TLS | SPO | AUT | BAB | PET | other |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| threshold | 2.9 % | 4.4 % | 5.5 % | 5.5 % | 7.7 % | 8.1 % | 8.7 % | 8.7 % | 9.1 % | 9.3 % | 10.2 % | 4.8 % |
The fee: small standard 1.78 / 1.89 / 2.05 / 2.21 up to 0.25 / 0.5 / 0.75 / 1 lb; large standard 2.36 / 2.62 / 2.89 / 3.15 / 3.62 up to 0.5 / 1 / 1.5 / 2 / 3 lb, then 3.91 + 0.07 per started lb above 3 lb; bulky tiers the fulfilment fee's base at the unit's price band.
Inbound β charged with the order
The order debit is the units at unit_cost plus the inbound fee per unit of the route you choose (the order's
fee_amount):
- carrier: parcel and express charge per billable lb (the greater of the weight and the dimensional weight at 1,728 / 139 lb per cu ft) plus per carton (every carton shipped, a partial last one too: cartons = units Γ· units per carton, rounded up), with a minimum per unit; LTL charges per pallet (pallets = units Γ cu ft Γ 1.15 / 60, at least one);
- placement: free ("optimized") from 5 identical full cartons, otherwise the minimal-split fee per unit (small standard 0.21 / 0.25 / 0.30 up to 0.375 / 0.75 / 1 lb; large standard 0.30 / 0.38 / 0.47 / 0.60 / 0.85 / 1.20 up to 0.75 / 1.5 / 3 / 5 / 12 / 20 lb; bulky 1.60 / 2.60 / 3.90 / 5.10 / 6.50 up to 5 / 12 / 28 / 42 / 50 lb);
- seller-side prep for units that need a label: 0.30 small standard, 0.40 large standard, 0.60 small bulky, 0.75 large bulky, plus 0.35 for kitting (multipacks and bundles). The marketplace offers no prep or labelling service under the 2026 rules: this is what a prep service charges you to label and kit the units before they ship, collected with the order so the order shows its whole landed cost (it is not a marketplace fee; its days are part of the lead time).
Shipments may arrive with a defect (labels, packaging, carton contents): the route's defect risk is 1 % for
parcel, 5 % for express and 3 % for LTL, and a defective shipment pays an inbound_defect_fee per unit when it
arrives (small standard 0.32; large standard 0.42 / 0.62 / 1.74 up to 1 / 3 / 20 lb; bulky 2.50). Now and then a
lot loses part of its units in the network; they never become sellable. Units lost on a partnered route
(parcel, ltl) are reimbursed when the lot arrives, at their landed cost (unit cost + inbound per unit), as a
reimbursement credit to the lot; on your own carrier (express) the loss is yours.
Product physicals
Every product has a weight, dimensions, a size tier and a carton, synthesized deterministically (no real measurements): from the title's net content where it states one (ounces, fluid ounces, pounds, grams, millilitres, "Pack of N", β¦), packed densely like the packaged goods it is, and otherwise from the size tier's typical weights and densities. One product has one size everywhere; a multipack weighs its units plus packaging, a bundle its parts. The tiers:
| tier | weight | longest Γ median Γ shortest side |
|---|---|---|
| small standard | β€ 1 lb | β€ 15 Γ 12 Γ 0.75 in |
| large standard | β€ 20 lb | β€ 18 Γ 14 Γ 8 in |
| small bulky | β€ 50 lb | β€ 37 Γ 28 Γ 20 in, length + girth β€ 130 in |
| large bulky | β€ 50 lb | β€ 59 Γ 33 Γ 33 in, length + girth β€ 130 in |
Units per carton: the supplier's case pack where there is one (multipacks: the case pack Γ· the pack), otherwise as many units as fit 1.5 cu ft and 50 lb; one per carton for bulky units. Units need a marketplace label unless the product ships under its manufacturer's barcode (its FNSKU is its ASIN); multipacks and bundles need a label and kitting.
Inbound routes and lead times
| route | cost | eligible | defect risk | |
|---|---|---|---|---|
parcel |
partnered parcel (default) | 0.40 per billable lb + 1.00 per carton, at least 0.15 per unit | always | 1 % |
express |
express, own carrier | 1.10 per billable lb + 2.50 per carton, at least 0.40 per unit | always | 5 % |
ltl |
partnered LTL pallets | 225.00 per pallet | from 5 identical full cartons | 3 % |
A lot's lead time, in days from the hour its order fills, is the supplier's processing (7 days) + dispatch (0β3 days) + prep (0β2 days, for labelled or kitted units) + transit + receiving at the fulfilment centre + now and then a delay (6β14 extra days on 15 % of lots, 12β20 on 4 %), the trip capped at 28 days (35 for LTL; 49 / 56 in peak season). The lot arrives β and starts selling β at the hour fill hour + 24 Γ the lead days. By the default route, for a labelled unit off peak, lead times run 9β35 days, typically 12:
| lead time (days) | β€ 14 | β€ 21 | β€ 28 |
|---|---|---|---|
| share of lots (parcel, labelled, off peak) | 62 % | 86 % | 97 % |
Express is faster (71 % within 14 days), LTL slower (11β42 days, typically 15). From mid-October to the end of the
year the fulfilment centres receive slowly: lead times run 15β56 days (typically 23; LTL up to 63). An offer's
lead_time and inbound_routes[] give the distribution for that product for an order filled now;
GET /api/v1/markets/us/deals/{key}/inbound?qty= quotes every route for a quantity (below). The drawn lead time is
revealed when the lot arrives; until then you know its window (arrives_window in ticks, arrives_window_days).
Example (a large-standard unit of 1.2 lb and 0.08 cu ft, 6 per carton, labelled): 30 units by parcel 1.05 per unit (optimized placement); 24 units by parcel 1.43 (4 cartons: minimal split); 30 by express 2.14; 30 by LTL 7.90 (one pallet for 30 units); LTL is not available for 24.
Your stock and your price
A paid order fills at the close of its paid hour as a lot on the account's trading account. The lots of one SKU form the account's stock of it: once a lot arrives it sells, first in, first out (the oldest lot first), at your price, hour by hour, for as long as it takes β there is no horizon. A lot is
- inbound while in transit: it sells nothing and pays no storage;
- on hand from its arrival hour: it sells, pays storage and ages;
- awaiting returns when it has no unit left but customer returns of its sales are still on their way;
- settled at the end of the first day on which nothing is left and every return has come back: its
realized_pnl= its cash flows β its cost basis, exactly as its trade on record shows.
Pricing rules
You set a pricing rule per market and MSKU, at any time; it takes effect from the next marketplace hour
(effective_from_tick: the hour already running keeps the price it opened with, and past hours never change). The
kinds:
| kind | price | parameters |
|---|---|---|
default (or no rule) |
the default repricer: the buy box price, never under your break-even, never over 2 Γ the newest lot's list price | none |
featured_offer |
follows the buy box: buy box Γ (1 + offset_pct) + offset |
offset, offset_pct (β0.9 β¦ 1.0), min_price (required), max_price |
fixed |
your price | price, min_price (required), max_price |
beat_lowest |
an alias of featured_offer that many under the buy box: {"kind": "beat_lowest", "offset_pct": 0.03} = 3 % under |
as featured_offer, non-negative |
Every price is clamped to [min_price, max_price] and rounded to the cent. The break-even of the default repricer
is the lowest price from which every higher price covers the unit-weighted landed cost of your units on hand (unit
cost + inbound) after the referral and fulfilment fees (it accounts for the referral tiers' cliffs and the price bands);
a product that no price covers is not offered. An order or a cart line can carry the starting rule (pricing): it
applies to the SKU from the hour after the fill if the SKU has no rule yet.
The guardrail. An offer priced above 1.5 Γ the median buy box of the product's last 13 weeks (91 days) is deactivated as a potential pricing error for that hour: it sells nothing (its price is still reported to you). An offer is also inactive without stock on hand, without a buy box price (rules that follow it), or once the product has left the listing.
Your share of the buy box. The listing's sales go round its sellers; every account is one more seller against the
listing's crowd of competing sellers (accounts never compete with each other). At the buy box price your weight is
one seller's β on average 1 / (nsellers + 1) of the listing's sales, with some luck from week to week; under it you
win more of them (up to a cap), a little above it you keep a reduced share, further above a small and quickly vanishing
one. The rivals' repricers follow an undercut: for a few weeks after an hour you sold under the buy box, they
match any price you set under it, so it wins no more than the buy box itself β a permanent undercut only lowers your
price; an occasional one wins some extra share. The size of the effect is not published: the record and your own
sales show it.
Prices are private. Your own price, rule and rule history are yours: the public record of your trades shows the units sold and the buy box, never your price.
Customer returns
A share of the units sold comes back (the share depends on the product and its category; it is not published):
- the refund 3β21 days after the sale (
refund= βunits Γ the sale price,referral_refund,refund_admin_fee, and the returns processing fee where the SKU is above its threshold); - the unit 5β14 days after the refund: sellable units go back into the stock of the lot they came from (and sell again from the next hour); unsellable ones are disposed of at the disposal fee.
Both fall on the sale's hour of the day; their money accrues and is posted at that day's close like every other.
Seller balance and disbursements
Every money flow of your lots β sale credits, fees, refunds, storage β is posted at the day close to the account's seller balance of the market, not to its cash. The seller balance is held and reaches your cash (what orders and payouts spend) only by settlement statements:
- Scheduled: every 14 marketplace days (
rules.stock.settlement_period_days), at a day close, a statement releases the entries posted up to 7 days before (funds_hold_days = 7): money reaches your cash 7β20 days after the day that earned it.GET /api/v1/accountβwallets[].next_release_tick(andnext_release_at, real time) says when. - Reserve: a statement keeps back a reserve for the refunds still to come: the account's refund rate on the market over the last 91 days (refunds / gross sales) Γ the gross sales of the last 14 days. It is released by the next statements as it falls.
- Negative balance: if a statement's entries plus the reserve and carry are negative, the deficit is carried to the
next statement; if that one is negative too, it is charged to your cash (
negative_balance_charge; your cash may go negative, and then orders and payouts wait until it is positive again). - On demand: once per marketplace day and market you may ask for a disbursement (
POST /api/v1/disbursements, or the console): it releases everything posted before the start of the day seven days ago (the same 7-day hold, counted back from today), and the reserve too when you have no lot on that market any more; refused (400) when nothing would be paid, 409 when it was used today (marketplace day) or when a raised hold leaves its cutoff where the last release ended.wallets[].eligible_nowandon_demand_availablesay what it would pay. A raised hold never releases a day twice: a wallet's next statement is the first whose cutoff passes the last one released.
Every statement lists its entries by kind with the opening reserve and carry, the net, the new reserve and carry, and
what was disbursed or charged (GET /api/v1/statements: from_tick, to_tick = its cutoff, close_tick). The
transfer appears in both wallets of the ledger (disbursement), so cash + seller balance is always the account's whole
money.
Removals and maximum age
You may have units removed (shipped back to you: they become owned goods in your inventory, at the lot's unit
cost; on a competition account they are gone like a disposal) or disposed of, at the fees above: POST /api/v1/removals {market, msku, qty, kind: removal|disposal,
position_id?} (the oldest units first, or the named lot). Only units on hand can be ordered out: a request
for more units than are on hand and not already in your pending requests is refused (400, with the number you can
order out), and so is one naming a lot that has not arrived yet (400; request it again once it is on hand). A
removal is carried out within the marketplace hour β at the close of the hour it was requested in
(effective_tick) β on the units on hand; if fewer are left by then (units sold in the meantime), the rest of the
request stays open for the following hours. A request may be refused (400) while the market is catching up on its
closes (try again in a moment); a request can be cancelled while nothing of it has been worked (409 once a close
worked it). On an account without an account holder a removal is a disposal. Its fee accrues on the lot and is
posted at the day close.
Stock has no horizon, but a safety maximum age: units still on hand 546 days (78 weeks) after their lot
arrived are removed automatically to the account holder's owned goods (disposed of on an account without a holder),
at the removal fee (auto_removal_at_tick). Competition accounts write their lots off 112 days after the fill,
without a fee, so a competition can end.
Fulfilment-network capacity
An account may hold at most max(400, 6 Γ the cu ft it shipped over the last 91 days) cubic feet per market: on hand
+ inbound + every order of the account not yet filled (unpaid or paid) + the new line. A line beyond it is refused
(400) with the numbers, when it is ordered and again when it is paid; GET /api/v1/account β fulfilment reports
capacity_cuft, used_cuft, inbound_cuft and ordered_cuft per market.
Fee amendments
The fee card changes only forward, by amendments the operator schedules with at least 30 days' notice in
real time: an amendment names the real time it takes effect from and applies from the first marketplace day that
starts then (midnight, marketplace time: a day's storage and surcharges are charged under one schedule); it is published in rules.pending_amendments ({version, effective_tick, effective_at,
effective_sim_at, digest, note}) as soon as it is scheduled. From that day on every fee of every account is charged
under the new schedule (rules.fees.schedule_version, and each offer's fee_schedule); nothing already posted or
accrued changes, and the market itself (its demand, prices and the desk's record) never moves with the fees.
The record and the desk
The desk's record of executed trades works as on the wholesale market, by marketplace days: the desk decides on each
offer once, when it appears, and its lots use the default repricer and the genesis fee schedule, are held 112 days
and show what they sold each day, the buy box and their result once settled. Your trades appear in the record with
their units and the buy box per day, open until the lot settles (with no fixed horizon: open_ended), never with
your price. The series of the record (/trades, /history, a lot's reveal) have one entry per marketplace day
(step_hours: 24); the tape, the trade statistics and a listing's signals are served by day by default
(resolution=hour|day|week, up to 120 days of tape or statistics and 400 days of signals per call).
Determinism
The market β every listing's demand hour by hour, the crowd, the buy box, ranks, the offers and the desk's record β is a pure function of the market's seed and the marketplace hour: no account, order or price of yours moves it, and fee amendments do not either. What happens to your stock is deterministic given the market and what the accounts did: every draw (lead times, inbound issues, your share of the buy box, returns) comes from a stream keyed by the account, the SKU, the lot and the hour, so re-running an hour β or catching up on a day of hours after a restart β gives the same result, ledger entry for ledger entry.
Renamed fields and the API version
The API stays /api/v1; a client recognises the continuous marketplace by time_model: "continuous" in
GET /api/v1/markets/{id} (and in rules). What changes for a client written for the classic markets, where a tick is the period of one menu:
| was | on the continuous marketplace |
|---|---|
four market ids m15, m45, h2, h10 |
one market us; the old ids are accepted as aliases in paths and parameters, answers say us |
| a tick = the period of one menu | a tick = one marketplace hour: ticks_per_day 24, time_scale, tick_sim_at, sim_now; sim_at on the menu, history and tape rows, orders and the record |
| one menu per tick | offers open for days: valid_until, hours_left, sim_valid_until |
| an order fills at the close of its tick | expires_tick (payment window), paid_tick; fills at the close of the paid hour |
storage_per_session |
storage_per_day, storage_per_month; rules storage_per_cuft_day, storage_per_unit_day |
| lead times in ticks | in days: lead_time.unit / lead_unit = "days", arrives_window_days, lead_time_days |
sales_by_tick, buy_box_by_tick, price_by_tick, nsellers_by_tick, revealed_ticks |
sales_by_day, buy_box_by_day, price_by_day, nsellers_by_day, revealed_entries, with step_hours 24 |
settlement_horizon_ticks only |
also settlement_horizon_days, path_step_hours |
signal rows' rank_drops_prev |
rank_drops_step (the rank drops over the row's hours); rows by day with to_tick, ticks |
| tape, statistics and signals per tick | a resolution parameter: hour, day (the default) or week; answers add resolution, step_ticks |
| rule periods in ticks | max_age_days, competition_writeoff_days, settlement_period_days, funds_hold_days, capacity_window_days, guardrail_window_days, days_of_supply_windows_days, publication_delay_hours, drops_window_days, drops_window_long_days |
| money posted at every close | posted once per lot and kind at each day close; accrued_today on lots until then; invoices (by marketplace month) on the stock view |
| on demand once per tick | once per marketplace day |
| amendments from a tick | effective_tick is the first marketplace hour of a day; effective_sim_at |
Configuration
XGM_TIME_SCALE (default 24; a divisor of 3600) sets the clock. XGM_AMAZON_CONFIG=/path/to/amazon.json overrides any
field of the engine's configuration (lists stand for tuples): every market field of the wholesale configuration
(categories, catalogue, demand, sellers, buy box, rank, supply, the desk β these generate the calibrated weekly
series the hourly market is drawn from), the fee schedule, physicals and returns of the weekly fields below, and the
marketplace's own, in marketplace days and hours:
| group | knobs |
|---|---|
| clock | time_scale (must equal XGM_TIME_SCALE), sim_epoch (the marketplace date and time of tick 0, a whole hour), hours_per_tick (1) |
| the market hour by hour | hour_profile (24 values), weekday_profile (7, Monday first), change_hour_span, rank_delay_hours, rank_scatter_blend_hours, drops_window_days, drops_window_long_days |
| supplier offers | offer_life_days, offers_alive, deal_share, supplier_processing_days, supplier_spread_hours |
| the desk | desk_horizon_days, desk_coverage_days, desk_period_days, desk_threshold_continuous, desk_explore_prob |
| stock and pricing | max_age_days, competition_horizon_days, dos_short_days, dos_long_days, utilization_window_days, guardrail_window_days, guardrail_factor, default_max_vs_list, capital_holding_rate (per week) |
| returns | returns (per category: the share of units returned and the share of returns sellable again; a list of [code, rate, sellable] replaces, a dict {code: [rate, sellable]} merges), refund_lag_days, return_back_lag_days |
| fee schedule | fees: a partial schedule β a dict keyed by category code, size tier or route code merges into the default table entry by entry, a list replaces the table (the fields are listed in rules.fees.schedule); the windows and periods it counts in weeks (rules.fees.schedule_week_fields) are read as weeks of seven marketplace days |
| physicals | physicals_from_titles, large_bulky_share, unit_weight_lb, density_lb_cuft, content_density_lb_cuft, content_factor, content_tare_lb, multipack_slack, multipack_tare_lb, bundle_slack, bundle_tare_lb, bundle_pad_in, carton_max_cuft, carton_max_lb |
For example:
{
"fees": {"referral": {"GRO": {"tiers": [[15.0, 0.08], [null, 0.15]], "min_fee": 0.0}},
"storage_standard": [0.78, 2.40],
"routes": {"parcel": {"per_lb": 0.45}}},
"returns": {"PET": [0.05, 0.6]},
"max_age_days": 364,
"refund_lag_days": [3, 14],
"guardrail_factor": 1.6
}
The configuration β the clock included β is part of the market's frozen identity: changing it, or XGM_TIME_SCALE,
needs a fresh XGM_DB_PATH β to change the fees of a running market, schedule an amendment instead (xgames amazon
fees amend, see Deploying). A fresh database records the market's history back to XGM_HISTORY_DAYS
(real days) before now; at time scale 24 that is 24 marketplace days per real day, and a history of more than 20,000
marketplace hours is refused at start unless XGM_ALLOW_LONG_BACKFILL=1. xgames amazon report and xgames amazon
calibrate print what a configuration produces and how its public series, aggregated to weeks, compare with a real
marketplace's.
API
Everything of the wholesale market's API applies (GET /api/v1/stock per SKU and lot, positions with stage, the
signals of a listing). The seller's endpoints β reads with your key, writes with the same access as placing orders
(an agent with trade access, or a competition account):
| Method & path | Description |
|---|---|
GET /markets/us/deals/{key}/inbound?qty= |
every route's inbound quote for qty units (default and at most the offer's max_qty): {market, offer_key, tick, qty, max_qty, unit_cost, default_route, quotes[] {route, label, available, reason, cartons, identical_cartons, pallets, placement, carrier, placement_fee, prep, per_unit, total, lead, lead_typical, lead_min, lead_max, lead_unit, defect_fee, defect_risk, default}}, lead times in days (404 when the offer is not open now) |
POST /orders, POST /cart/items {β¦, route?, pricing?} |
an order or a cart line with its inbound route (default parcel) and a starting pricing rule; the order answers with expires_tick and closes_at |
PUT /pricing/{market}/{msku} {kind, offset?, offset_pct?, price?, min_price, max_price?, note?} |
set the rule: {market, msku, sku, rule, effective_from_tick, effective_from_at, preview {price_at_current_buy_box, active, reason, default_min, default_max, buy_box, β¦}}; 400 with the reason when the rule is invalid; 403 for a min_price under break-even (default_min) unless autonomous funding is on; set again in the same hour, it replaces the pending rule |
DELETE /pricing/{market}/{msku} |
back to the default repricer from the next marketplace hour |
GET /pricing?market=, GET /pricing/{market}/{msku} |
your rules; one rule with its history |
POST /removals {market, msku, qty, kind, position_id?} |
request a removal or disposal of on-hand units (201); 400 for more units than are on hand and not already in pending requests, or for a named lot not arrived yet; a disposal is 403 unless autonomous funding is on (the account holder disposes on the console) |
GET /removals?status=&market=, DELETE /removals/{id} |
your removal requests; cancel one still requested (409 once any of it is done) |
GET /stock |
your stock per SKU and lot (accrued_today, lead_time_days, arrives_window_days, today, days_of_supply) and the monthly invoices |
GET /statements?market=&limit=, GET /statements/{id} |
settlement statements, one with its entries |
POST /disbursements {market} |
disburse on demand: 201 with the statement and the new available_balance; 409 already used this marketplace day; 400 nothing to pay |
GET /ledger?wallet=&kind=&statement_id= |
your ledger by wallet (cash, us for the seller balance, or all); seller-balance entries carry the day's last tick and day N |
GET /account |
adds available_balance (cash), seller_balance, reserve, wallets[] (per market, with eligible_now, next_release_tick, next_release_at and on_demand_available) and fulfilment {market: {capacity_cuft, used_cuft, inbound_cuft, ordered_cuft}} |
Smoke clients
client/smoke/ holds dependency-free clients (the standard library and xgames_client) for a local test
deployment (local payments, agent keys issued with its own key secret β never a live one), run at a fast
XGM_TIME_SCALE. They trade as five separate agents, each with its own simulated account holder who registers,
accepts the terms and pays on the web pages, and they act twice a marketplace day (at 02:00 and 14:00), not every hour:
| strategy | what it does |
|---|---|
default |
the default repricer, partnered parcel; the holder pays each order's checkout by card twelve marketplace hours after the order was placed (inside its payment window); one order is left unpaid and must expire |
undercut |
follows the Buy Box 3 % under it, never under the break-even the pricing preview quotes, on the cheapest available route; proposes into its cart, the holder pays the cart |
manual |
a fixed price re-set from the Buy Box it observes at every pass, back to the default repricer now and then; paid from the topped-up balance |
liquidator |
lists over the Buy Box, then orders the slow stock out (removals to owned goods, disposals by the holder on the console); cancels one request |
cash |
the default repricer; disburses on demand whenever something is eligible (once a marketplace day), reads its statements |
They check from the outside, as the marketplace days pass: no server error; the fields of every answer (with the
marketplace-time ones); no hidden word, no marketplace mark and no time named the weekly engine's way in what is served; each wallet's
ledger (every balance_after follows from the entry before; the sums are the reported balances); the seller balance =
the entries no statement released yet + reserve + carry; an order's expires_tick = the end of its payment window or
of the offer, whichever comes first; a paid order fills at the close of the hour it was paid in (its lot opens in that
hour); an unpaid one expires and costs nothing; a lot arrives its lead time in days after the fill and sells nothing
before; a lot's money reaches the seller balance only at a day close, once per kind and day (day N); the monthly
invoices add up to the month's postings of every charge, by kind, by SKU and in total; every statement (its released
entries add up to net; opening reserve + carry + net = reserve + carry + disbursed β charged; its transfer appears on
both sides; a scheduled one closes a day d with (d β 7) mod 14 = 13 and releases through the end of day d β 7, an
on-demand one through the end of the day before the hold); units per lot; a lot's entries = its cash flows, and once
settled its entries + the order's debit = its realized P&L; a lot's past daily sales and prices never change; a rule
set in one hour prices from the next (checked hour by hour on the last settled hour of each pool); removals are carried
out within the hour and become the holder's owned goods; one on-demand disbursement per marketplace day; another agent
never sees your lots, rules or statements, and the public record of your lots carries no price of yours. A run that
exercised too little β a strategy that never ordered on its order days, no fill, rule hour, invoice, scheduled
statement, removal or on-demand disbursement checked in a run long enough for them β is inconclusive and fails like
a violation (--no-coverage-gate reports it without failing). They print a summary and write a JSON report (P&L per
strategy, fees by kind with the daily holding postings, returns and refunds, statements and disbursements, removals,
pricing changes, fills and their delay after the payment, the HTTP status counts, errors):
python -m client.smoke.amazon_smoke --spawn --out /tmp/amazon-smoke # starts and stops its own local server (45 marketplace days)
python -m client.smoke.amazon_smoke --spawn --days 45 --example 40 --out /tmp/amazon-smoke # with the example below beside it
python -m client.smoke.amazon_smoke --base http://localhost:8000 --key-secret <its key secret> --out /tmp/amazon-smoke
python -m client.smoke.amazon_smoke --help # the run length and pacing options
--spawn starts xgames serve on a free localhost port with a fresh temporary database, the continuous marketplace
at XGM_TIME_SCALE=3600 (a marketplace hour every real second: 45 marketplace days take about 18 real minutes), the
genesis about three weeks of marketplace time before now, a small history, local payments, a random key secret, and
the smoke configuration client/smoke/continuous_smoke_config.json (a small catalogue, the supplier's processing one
day instead of seven: lots arrive 3β29 days after the fill).
client/examples/10_amazon_seller.py is the same seller flow as one readable script: the market's clock and rules,
an open offer's fee breakdown and how long it stays open, the inbound quotes in days, an order with a starting pricing
rule paid and filled at the close of its hour, the stock day by day (the arrival, the units sold, a live price change
from the next hour, a removal of slow stock within the hour, an on-demand disbursement), then the statements, the
monthly invoices and the lot's daily record:
python client/examples/10_amazon_seller.py http://localhost:8000 <an issued key> --days 30 --local-holder
Operator note.
XGM_KERNEL=amazon_weeklyruns the earlier seller engine instead, with four markets of weekly sessions; it is kept for its record and its tests, and its documentation is served on such a deployment in place of this page. The fee schedule's fields named after its sessions (settlement_period_sessions,funds_hold_sessions,low_inventory_new_sessions,utilization_new_seller_sessions,capacity_window,reserve_window,refund_window,returns_window) count weeks of seven marketplace days here; the weekly-only fields (max_age_sessions,competition_horizon, the session windows) are not read by the continuous marketplace β set their day equivalents above.