A free, browser-side converter for Magic: The Gathering collection CSVs between popular sites. Your file never leaves your machine — everything runs as Blazor WebAssembly in the tab.
➡️ Try it now: stepkie.github.io/MtgCsvHelper · CHANGELOG · Conversion limitations
- Upload input CSV file.
- The input format is auto-detected from the headers; toggle off to pick manually. Pick the output format.
- Click Convert. Larger collections take a moment.
- Download the converted CSV. The results table shows per-row issues and totals.
When a new version ships, the app shows a "Reload to update" banner — click it to swap in the latest build. (The service worker keeps an offline copy of the previous version until you do.)
Both import and export, unless noted.
| Format | Notes |
|---|---|
| Moxfield | |
| Dragon Shield | |
| Manabox | |
| Topdecked | |
| Deckbox | Curated edition names + legacy edition codes are aliased back to Scryfall codes on read/write. |
| MTGGoldfish | |
| TCGplayer | |
| Archidekt | |
| MTGO | 2-letter legacy codes (MI/VI/TE/EX) canonicalized to 3-letter Scryfall codes (MIR/VIS/TMP/EXO); N/M collector numbers stripped. |
| Cardmarket | Import only — resolves rows by idProduct via Scryfall reverse lookup. |
| Card Kingdom | Export only — 4-column buylist shape, no condition/language/foil-etched. |
Open an issue if you want a new site supported.
- Data loss between formats is sometimes unavoidable. See CONVERSION_LIMITATIONS.md for the full per-format matrix — what each platform supports (Etched, conditions, languages, set-name aliases) and where round-trip conversions silently coerce or drop fields. Read it before you trust a cross-format round-trip.
- Sites that export data in non-standard formats for certain sets (i.e., not adhering to Scryfall syntax) may cause issues for certain cards when rewriting to another format
Built originally to bridge card-scanner exports (Manabox, MtG DragonShield Card Manager) into mainstream collection managers (Moxfield, Deckbox) without manual header-shuffling. Also handles the inverse: keeping a collection in sync across multiple sites.
The cumbersome bits the tool takes care of for you:
- Card names — some sites only emit the front face of double-faced cards; we expand to the full Scryfall name.
- Set info — missing set codes or set names, non-standard set names (Deckbox in particular curates its own vocabulary).
- Foil — different sites encode "foil"/"nonfoil"/"etched" with different strings.
- Condition — sites use 6 or 7 levels with varying vocabulary; we map between them (with documented information loss when mapping to a coarser scale).
- Language — short codes vs. full names.
- Price — currency, separator, and symbol position all vary.
All site-specific behavior lives in MtgCsvHelper/appsettings.json so adding a format is configuration, not code.
The web app at https://stepkie.github.io/MtgCsvHelper/ covers the common path. Unlike the console version, the in-browser tool doesn't expose appsettings.json for live tweaking, but it works for any default-shaped CSV from a supported site.
- Prerequisite: .NET 10 SDK / Runtime.
- Download a release zip from the Releases tab, or build from source with
dotnet build MtgCsvHelper.slnx. - Run
dotnet run --project MtgCsvHelper.Console—--helplists all flags.--inis optional; when omitted, the input format is auto-detected from each file's CSV header. Pass--in MOXFIELD(etc.) to skip detection.
MtgCsvHelper/appsettings.jsoncarries the per-format column mappings and is checked into the repo; tweak it if you want to add a new format or override a column.
The web app and console ship with a Scryfall reference bundle (cards.min.json.gz, ~10 MB)
under MtgCsvHelper.BlazorWebAssembly/wwwroot/data/. This avoids hitting the Scryfall
API at runtime for static lookups (set names, double-faced names, tokens).
To regenerate locally — e.g. after a new MtG set release:
dotnet run --project tools/MtgCsvHelper.RefreshReferenceDataThis downloads Scryfall's default_cards bulk file, strips it to the fields the catalog needs, and writes the gzipped bundle to the default location above. The Console and test projects pick it up on the next build via <None CopyToOutputDirectory>. CI regenerates the bundle on every deploy.
Two sibling sub-commands:
-- cardmarket-fixture— regeneratesTests/cardmarket-real-export.csvfrom the Moxfield reference via the Scryfall reverse lookup.-- deckbox-aliases— scrapes deckbox.org/editions and emitsResources/deckbox-set-aliases.json(Deckbox edition names that diverge from Scryfall canonical) andResources/deckbox-code-aliases.json(Deckbox-internal codes likeex_127mapped back to Scryfall codes).
All site-specific column mappings live in MtgCsvHelper/appsettings.json — a single source of truth shared between Console, Tests, and the Blazor app (the latter via a build-time copy into wwwroot/). Adding a new format means dropping in a new entry; PRs welcome.
Example entry — Moxfield:
"MOXFIELD": {
"Quantity": "Count",
"CardName": {
"HeaderName": "Name",
"ShortNames": false
},
"SetCode": "Edition",
"SetName": null,
"SetNumber": "Collector Number",
"Finish": {
"HeaderName": "Foil",
"Normal": "",
"Foil": "foil",
"Etched": "etched"
},
"Condition": {
"HeaderName": "Condition",
"Mint": "Mint",
"NearMint": "Near Mint",
"Excellent": "Near Mint",
"Good": "Good (Lightly Played)",
"LightlyPlayed": "Played",
"Played": "Heavily Played",
"Poor": "Damaged"
},
"Language": {
"HeaderName": "Language",
"Mappings": {
"en": "English",
"es": "Spanish",
"fr": "French",
"de": "German",
"it": "Italian",
"pt": "Portuguese",
"ja": "Japanese",
"ko": "Korean",
"ru": "Russian",
"zhs": "Chinese",
"zht": "Traditional Chinese"
}
},
"PriceBought": {
"HeaderName": "Purchase Price",
"Currency": "EUR",
"CurrencySymbol": "Absent"
}
}- The right-hand side of each mapping is the CSV header used by that site.
Finish,Condition,Language, andPriceBoughtare rich sub-configs because sites encode them differently.- The
Conditionscale varies by site (6 vs 7 values, different vocabulary) — see CONVERSION_LIMITATIONS.md for the per-format collapse table. Language.Mappingsonly needs to be set when the site uses non-ISO codes.CARDKINGDOMis a write-only buylist format (4 columns, no condition/language/foil-etched).CARDMARKETis import-only — it identifies cards byidProductand we Scryfall-reverse-lookup the rest.
Tracked feature and quality work lives in issues.
Most problems are one of two conversion failures — knowing which one you hit tells us where to look, and which issue template to use:
(a) The input won't convert. You feed a CSV in and the tool reports error rows or silently drops them. The problem is on our side — our parser doesn't understand that input. → open an Input won't convert issue and attach the input CSV. (In the web app, the Report to GitHub button after a failed conversion auto-fills this for you.)
(b) The output won't import. The conversion runs clean, but the target site rejects rows when you import the result — or silently lands them on the wrong printing. The problem is in what we emit for that format. → open a Target site rejected the export issue and paste the site's error lines.
Either way, once it's fixed we fold the case into our test corpus so it can't regress. If a cross-format round-trip merely changed or dropped a field, that may be expected — see Limitations / Known Issues first.
For anything else — UI bugs, crashes, or a feature idea — open a new issue. The PWA shows a "Reload to update" banner when a new version is available; click it instead of clearing the browser cache. The current version is at the bottom of the app bar and links to the CHANGELOG.
