The same product often sells for two very different prices on the same day. Walmart has a blender on clearance at $24. On Amazon, the same blender, same UPC, has a Buy Box price of $54 and sells 300 units a month. Someone buys 20 at Walmart, ships them to an Amazon warehouse, and takes the difference.
That’s online arbitrage (OA). It’s legal. In the US, the first-sale doctrine lets you resell genuine goods you bought. Brands hate it, retailers try to limit it, and Amazon puts up with it while making it harder every year. That combination is where the money is: the friction keeps most people out.
Doing this by hand means scanning products one by one in a phone app. The people who make real money screen thousands of products a day. In this guide you’ll build a Python script that takes a list of retail deals (UPC + buy price), pulls live Amazon data from the Keepa API, works out fees and profit, and flags the traps: price spikes, Amazon selling on the listing, crowded offers. Then we’ll go through the capital you actually need, what it earns, and how sellers lose their accounts.
How the money works
- You find a product cheaper at a retailer (Walmart, Target, Home Depot, Kohl’s, Best Buy, brand outlet stores) than its Amazon selling price.
- You buy it, ideally stacking cashback, a discounted gift card and a rewards credit card on top of the sale price.
- You ship it to a prep center (a warehouse that labels and bags items for Amazon), which sends it to Amazon FBA.
- Amazon sells it, ships it, handles returns, and pays you after fees. Since March 2026, US sales are held until 7 days after delivery (Amazon’s “DD+7” reserve) before they can be paid out.
Example math (one unit, US marketplace):
| Line | Amount |
|---|---|
| Amazon sale price (Buy Box) | $34.99 |
| Referral fee (15%, most categories) | −$5.25 |
| FBA fulfillment fee (small item, ~1 lb) | −$4.50 |
| Prep center + inbound shipping | −$1.50 |
| Cost of goods (clearance price) | −$14.00 |
| Profit | $9.74 |
| ROI (profit ÷ cost) | 70% |
Most experienced OA sellers target at least 30% ROI and $3+ profit per unit. Anything below that gets eaten by returns, storage fees and the occasional price crash.
What you need
| Item | Cost | Notes |
|---|---|---|
| Amazon Professional seller account | $39.99/month | Individual plan ($0.99/item) works for testing but you lose Buy Box eligibility in practice |
| Keepa subscription (browser charts) | €19/month | Price/rank history for manual checks |
| Keepa API access | from €49/month | Cheapest plan gives 20 tokens/minute (~28,800/day). Check keepa.com → API for current plans |
| SellerAmp SAS (optional) | from $19.95/month | Phone scanner and quick eligibility checks |
| Prep center | ~$0.75–$2.00/unit | Pick one in a no-sales-tax state (Delaware, Oregon, Montana, New Hampshire) |
| Rewards credit card | $0 | 2% flat cashback cards are the baseline |
| Python 3.10+ | $0 | pip install keepa |
| Inventory capital | $1,000 minimum, $15k+ for scale | See the capital section. This is the real cost |
Outside the US? Amazon lets sellers from many countries (Indonesia included at the time of writing; check Amazon’s current list) register on Amazon.com. Many international OA sellers buy online with a US address at a prep center and get paid through Payoneer or Amazon’s currency converter. Expect more friction: some US retailers decline foreign cards, and identity verification takes longer.
Fees change, sometimes twice a year. Before you buy anything, check Amazon’s current FBA fee page or use the Revenue Calculator in Seller Central. In 2024 Amazon added an inbound placement fee and a low-inventory-level fee, and monthly storage for standard-size items has been around $0.78 per cubic foot Jan–Sep and $2.40 Oct–Dec (confirm on Amazon’s current rate card). Bulky, slow items eat profit fast.
Step 1: Get a deal list
The script needs a CSV of candidates: a product code (UPC or EAN), what it costs you, and where it’s from. Where the list comes from is your edge:
- Retailer clearance and sale pages. Walmart, Target and Kohl’s clearance sections show UPCs on many product pages. Copy them into a sheet as you browse.
- Brand outlet sites and seasonal sales (Black Friday, end-of-season clearance). Volume deals show up here first.
- Sourcing tools. Tactical Arbitrage (plans start around $59–$69/month; the full online-arbitrage tier costs more) crawls retailer sites and matches products to Amazon for you. That’s the paid version of this script.
- Deal groups. Paid OA Discord groups ($30–$100/month) post leads. Everyone in the group buys the same lead, so the price drops fast. Use them to learn, not as your main source.
Save it as deals.csv:
code,buy_cost,source
0885911425308,24.00,walmart clearance
0070537100028,11.47,target circle deal
0812512010118,39.99,kohls 30% code
Use your real landed cost: price after coupons, plus sales tax if you pay it, minus nothing yet. Cashback is handled separately.
Step 2: Set up Keepa
Buy an API plan at keepa.com (the API key is separate from the browser subscription), then:
python -m venv oa
source oa/bin/activate # Windows: oa\Scripts\activate
pip install keepa
export KEEPA_KEY="your-api-key" # Windows: set KEEPA_KEY=your-api-key
python -c "import keepa, os; api = keepa.Keepa(os.environ['KEEPA_KEY'], check_key=True); print(api.tokens_left)"
If it prints a number, you’re connected. (check_key=True matters: without it the library doesn’t contact Keepa at startup and prints 0 even for a bad key.) A basic product lookup costs 1 token. The screener also requests Buy Box data, which adds 2 tokens, so plan on about 3 tokens per product. The €49 plan (20 tokens/minute) works out to roughly 9,000 products a day.
Step 3: The screener
Save this as screen.py:
import csv
import os
import sys
import keepa
# --- your rules ---
MIN_ROI = 0.30 # 30% return on cost
MIN_PROFIT = 3.00 # dollars per unit
MAX_RANK = 150_000 # sales rank ceiling (category-dependent, see below)
MAX_SPIKE = 1.25 # skip if current price > 125% of 90-day average
PREP_AND_SHIP = 1.50 # prep center + inbound shipping per unit
CASHBACK = 0.04 # stacked cashback/gift card/card rewards as a fraction of cost
DEFAULT_FBA = 6.00 # used when Keepa has no fee data (flagged in output)
DEFAULT_REFERRAL = 15.0 # percent
# Keepa price-type indexes
AMAZON, NEW, SALES_RANK, NEW_COUNT, BUY_BOX = 0, 1, 3, 11, 18
def stat(arr, i, money=True):
"""Read one value from a Keepa stats array. -1 means no data."""
if not arr or i >= len(arr) or arr[i] is None or arr[i] < 0:
return None
return arr[i] / 100 if money else arr[i]
def analyze(p, buy_cost):
stats = p.get("stats") or {}
cur, avg90 = stats.get("current"), stats.get("avg90")
buy_box = stat(cur, BUY_BOX)
sell = buy_box or stat(cur, AMAZON) or stat(cur, NEW)
if not sell:
return None
fees = p.get("fbaFees") or {}
fba = fees.get("pickAndPackFee")
fba = fba / 100 if fba else None
referral_pct = p.get("referralFeePercentage") or DEFAULT_REFERRAL
net_cost = buy_cost * (1 - CASHBACK)
profit = sell - sell * referral_pct / 100 - (fba or DEFAULT_FBA) - PREP_AND_SHIP - net_cost
roi = profit / net_cost
flags = []
if not buy_box:
flags.append("NO_BUYBOX")
avg_bb = stat(avg90, BUY_BOX)
if avg_bb and sell > avg_bb * MAX_SPIKE:
flags.append(f"SPIKE(avg90 ${avg_bb:.2f})")
if stat(cur, AMAZON):
flags.append("AMZ_ON_LISTING")
if fba is None:
flags.append("FEE_ESTIMATED")
rank = stat(cur, SALES_RANK, money=False)
if rank is None or rank > MAX_RANK:
flags.append("SLOW")
return {
"asin": p["asin"],
"title": (p.get("title") or "")[:70],
"sell": round(sell, 2),
"profit": round(profit, 2),
"roi": round(roi * 100, 1),
"rank": rank,
"sold_month": p.get("monthlySold"),
"new_offers": stat(cur, NEW_COUNT, money=False),
"flags": " ".join(flags),
}
def main(path):
api = keepa.Keepa(os.environ["KEEPA_KEY"], check_key=True)
results = []
with open(path, newline="") as f:
for row in csv.DictReader(f):
# One UPC can map to several ASINs (bundles, variations), so query per code
try:
# buybox=True is required: without it Keepa leaves the Buy Box price
# out of the stats (+2 tokens per product)
products = api.query([row["code"]], domain="US", stats=90, buybox=True,
history=False, product_code_is_asin=False,
progress_bar=False)
except Exception as e:
print(f"skip {row['code']}: {e}", file=sys.stderr)
continue
for p in products:
r = analyze(p, float(row["buy_cost"]))
if r:
r.update(code=row["code"], buy_cost=row["buy_cost"], source=row["source"])
results.append(r)
results.sort(key=lambda r: r["roi"], reverse=True)
with open("screened.csv", "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=list(results[0].keys()) if results else ["asin"])
w.writeheader()
w.writerows(results)
print(f"{len(results)} listings checked, tokens left: {api.tokens_left}\n")
for r in results:
if r["roi"] >= MIN_ROI * 100 and r["profit"] >= MIN_PROFIT and not r["flags"]:
print(f"BUY? {r['asin']} ${r['buy_cost']} -> ${r['sell']} "
f"profit ${r['profit']} ROI {r['roi']}% rank {r['rank']} {r['title']}")
if __name__ == "__main__":
main(sys.argv[1] if len(sys.argv) > 1 else "deals.csv")
Run it:
python screen.py deals.csv
You get every matched listing in screened.csv (sorted by ROI, with flags) and a short list of clean candidates in the terminal. The script waits automatically when you run out of tokens, so you can feed it a 5,000-row file and leave it running.
What the flags mean:
SPIKE: the Buy Box is way above its 90-day average. Someone ran out of stock and the price jumped. By the time your units arrive, it’s back down. This one filter saves beginners more money than anything else.NO_BUYBOX: no new offer holds the Buy Box right now (often the item is out of stock or the listing is suppressed), so the script fell back to Amazon’s or the lowest new price. Treat the profit number as a guess.AMZ_ON_LISTING: Amazon itself sells it. Amazon wins the Buy Box most of the time, so you sit and wait. Skip unless Amazon is often out of stock (check the Keepa chart).FEE_ESTIMATED: Keepa had no fee data, so the script guessed $6. Check in the Revenue Calculator.SLOW: sales rank above your ceiling. A rank of 150,000 sells several units a month in big categories like Home & Kitchen and almost nothing in small ones. AdjustMAX_RANKper category, and prefersold_month(Amazon’s “bought in past month” figure) when Keepa has it.
Step 4: Check every “BUY?” by hand before spending money
The script is a filter, not a buyer. For each candidate, spend two minutes on:
- Same product? Open the Amazon listing. Same size, color, pack count? A UPC that matches a 2-pack listing while you bought singles is the classic beginner loss.
- Can you sell it? In Seller Central, search the ASIN under Add a Product. If it says “approval required,” the brand or category is gated. New accounts are gated on many big brands (Nike, Lego in some cases, most beauty and grocery brands). Don’t buy gated items hoping to get approved later.
- Keepa chart. Look at 90 days of Buy Box price and the offer count. A falling price with a rising number of sellers means others found the same deal. Get out of the way.
- IP complaint history. Brands with aggressive enforcement file complaints against resellers. Search the brand name plus “IP complaint” on seller forums and Reddit (r/FulfillmentByAmazon). If people report complaints, skip that brand, even at 100% ROI.
- Quantity. Buy no more than you can sell in about 30–60 days given
sold_monthdivided by the number of FBA sellers.
Step 5: Stack the discounts
This is the legal “exploit” part, and it’s where marginal deals become good ones:
- Cashback portal (Rakuten, TopCashback, Capital One Shopping): typically 1–10% depending on the retailer and the week.
- Discounted gift cards from resale sites like CardCash or Raise. For popular big-box stores the discount is usually small (a few percent at most), and stock is often sold out.
- Retailer programs and coupons: Target Circle offers, Kohl’s Cash, store-specific promo codes.
- A rewards card on top: 2% flat at minimum.
Stacked, 3–10% off the shelf price is realistic; the top of that range needs a good portal week plus gift cards in stock. On $15,000 a month of purchases, that’s roughly $450–$1,500 a month, which can be the difference between a good month and a mediocre one. Set CASHBACK in the script to what you actually get, not the best case: portals reverse cashback on returns, and several state in their terms that bulk or resale purchases may not qualify.
Step 6: Buy, ship to the prep center, send to Amazon
Ship orders straight to your prep center’s address with your account ID on the label. The prep center checks items in, labels them with FNSKU barcodes, and you create the shipment in Seller Central (Send to Amazon). Most charge per unit plus a small monthly minimum. Your first units are usually live 1–3 weeks after you buy.
Keep every receipt and order confirmation, by ASIN, in one folder. You’ll need them (see Risks).
Capital and realistic earnings
Here’s the part most OA content skips. Your money is locked up for about 4–8 weeks per cycle: shipping to the prep center, inbound to Amazon (sometimes weeks in Q4), time to sell, then the DD+7 hold, the payout cycle, and extra reserves on new accounts.
Working backwards from $5,000/month profit:
| Item | Number |
|---|---|
| Average profit per unit | ~$8 |
| Units sold per month | ~625 (about 21/day) |
| Average cost per unit | ~$15 |
| Inventory bought per month | ~$9,400 |
| Capital needed with a 6-week cycle + buffer | $15,000–$20,000 |
| Time | 25–40 hours/week sourcing, checking and managing |
With $1,000–$2,000 starting capital and these margins, expect roughly $200–$800 profit in the first months, while you learn which categories work and which brands to avoid. Most people who reach $5k/month got there by reinvesting profit for 6–12 months, not by starting with $20k.
Monthly costs at that scale:
| Cost | Monthly |
|---|---|
| Amazon Professional | $39.99 |
| Keepa subscription + API | ~€68 (about $75) |
| SellerAmp or similar | $20–$30 |
| Tactical Arbitrage (optional, at scale) | ~$59–$159 depending on plan |
| Prep center (625 units × ~$1.25) | ~$780 (already in the per-unit math above) |
| Storage + returns allowance | 2–5% of sales |
Software is cheap. Inventory, storage and the units that don’t sell are the real costs.
Risks: suspensions, gating, and retailer bans
This is legal, but you’re working in the gaps between three companies that don’t want you there.
Authenticity complaints are the account killer. A brand can file an “inauthentic” or IP complaint against your listing. Amazon may then ask for invoices from the manufacturer or an authorized distributor. Sellers widely report that retail receipts from Walmart or Target are often not accepted for this. Enough complaints and your account gets suspended with your inventory and balance stuck. How people lower the risk: avoid brands with a complaint history, sell only new, sealed, genuine items, never list as “new” anything that’s been opened, and keep every receipt anyway. Receipts still help in appeals.
One account per person. Amazon’s rules allow one seller account unless you have a legitimate business reason approved by Amazon. Opening a second account to get around a suspension gets every linked account banned. Don’t.
Retailer bans and cancellations. Walmart, Target and others say in their terms they can limit quantities and cancel orders that look like reseller orders. Some OA sellers spread big orders across family members’ accounts, addresses or prep-center suites to get past purchase limits. That’s a grey area: it breaks the retailer’s terms, not the law. The usual outcome is cancelled orders, a closed retailer account and lost cashback, not legal trouble. Big orders shipped to known prep-center addresses get flagged faster.
Cashback clawback. Portals can reverse cashback on bulk orders or returns and close accounts that look like resellers. Treat cashback as a bonus, not your margin.
Price tanking. Ten sellers found the same clearance deal you did. The Buy Box drops below your cost, and you either sell at a loss or pay storage while you wait. This is the most common way people lose money, more common than suspensions.
Taxes. Amazon collects US sales tax from buyers, but it’s still business income, taxed in your country. Indonesian sellers: Amazon payouts are taxable income here too, and Amazon will ask for a tax interview (W-8BEN for non-US sellers) before paying out.
Reality check
Full auto-buying doesn’t work. You could wire the screener to a checkout bot. Don’t. Wrong-variant matches, gated listings and price spikes need a human look, and bot-placed bulk orders are exactly what retailers cancel and ban.
The script is only as good as your deal list. Keepa tells you what something sells for on Amazon. It doesn’t find cheap stock. Finding mispriced stock before everyone else is the job. Everything else is admin.
Common mistakes:
- Buying into a price spike (that’s why the
SPIKEfilter is there) - Ignoring pack-size and variant mismatches between the UPC and the listing
- Going big on one ASIN. Spread capital across 20–50 products at first
- Counting cashback in the margin, then losing it to a reversal
- Buying bulky, slow items in September and paying Q4 storage rates on them
- Buying from brands that have a reputation for filing IP complaints
- Starting with paid lead groups and buying the same deals as 500 other people
Fees and API fields change. If Keepa renames a field or Amazon changes fees, FEE_ESTIMATED will start showing up everywhere. Check Keepa’s API docs and update DEFAULT_FBA and the fee logic.
Final checklist
- Amazon Professional account verified, tax interview done
- Keepa API key working (
api.tokens_leftprints a number withcheck_key=True) -
deals.csvwith real landed costs;CASHBACKset conservatively - Every “BUY?” checked by hand: same variant, not gated, no spike, no IP-complaint brand
- Prep center chosen, preferably in a no-sales-tax state
- Receipts saved per ASIN
- First buy spread across 20+ products, nothing you can’t sell in 60 days
Start today with $500–$1,000. Pull 200 UPCs from one retailer’s clearance section, run the screener, buy the five best clean hits, and track exactly what they make after fees. If they come in at 30%+ ROI, roll the profit into the next round.