← Developer Portal

Tutorial: Export Your Portfolio to CSV

A small Python script that pulls every card in your inventory along with the current portfolio snapshot, then writes a CSV you can open in any spreadsheet.

What you'll need

  • An IWMM API key — see Getting Started
  • Python 3.10+ (no third-party libraries required)

Note: the site already exposes a one-click CSV export from the Inventory page. This tutorial is for building automation around the API — e.g. a nightly snapshot or a feed into your own portfolio dashboard.

1. The script

Save as export.py:

import csv, os, sys, urllib.request, urllib.parse, json

API = "https://iwantmymtg.net/api/v1"
KEY = os.environ["IWMM_API_KEY"]

def get(path, **params):
    url = f"{API}{path}"
    if params:
        url += "?" + urllib.parse.urlencode(params)
    req = urllib.request.Request(url, headers={"Authorization": f"Bearer {KEY}"})
    with urllib.request.urlopen(req) as r:
        return json.load(r)

# 1. Portfolio snapshot
portfolio = get("/portfolio")["data"]
print(f"Total value: ${portfolio['totalValue']:.2f}", file=sys.stderr)

# 2. Walk inventory pages until exhausted
rows, page = [], 1
while True:
    body = get("/inventory", page=page, limit=100)
    rows.extend(body["data"])
    meta = body["meta"]
    if page * meta["limit"] >= meta["total"]:
        break
    page += 1

# 3. Write CSV
with open("inventory.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["set", "number", "name", "foil", "quantity", "price", "value"])
    for it in rows:
        price = (it.get("priceFoil") if it["isFoil"] else it.get("priceNormal")) or 0
        w.writerow([
            it.get("setCode", ""), it.get("cardNumber", ""), it.get("cardName", ""),
            "yes" if it["isFoil"] else "no",
            it["quantity"], f"{price:.2f}", f"{price * it['quantity']:.2f}",
        ])

print(f"Wrote {len(rows)} rows to inventory.csv", file=sys.stderr)

2. Run it

export IWMM_API_KEY=iwm_live_...
python3 export.py

Open inventory.csv in your spreadsheet of choice.

3. Stay under the rate limit

A 1,000-card collection at limit=100 takes 11 requests (1 portfolio + 10 inventory pages) — well under the free tier's 100/day. Watch X-RateLimit-Remaining if you're scheduling this every hour:

with urllib.request.urlopen(req) as r:
    print("remaining:", r.headers.get("X-RateLimit-Remaining"))

Where to go next

  • Add /portfolio/history?days=30 for a value-over-time series
  • Pipe the CSV into a Google Sheet via the Sheets API
  • On the Developer tier, drop the per-page sleep and pull big collections in seconds