Converts the "Total Email List" and "Subscribers" CSV exports from Substack to a CSV compatible with Ghost.
To install the CLI, which is required for the Usage commands below:
npm install --global @tryghost/migrate
To use this package in your own project:
npm install @tryghost/mg-substack-members-csv --save
or
yarn add @tryghost/mg-substack-members-csv
To run a Substack CSV migration, the required command is:
migrate substack-members --pathToFile /path/to/emails.csv
To convert paid subscriptions, add --subs
with a second file path:
migrate substack-members --pathToFile /path/to/emails.csv --subs /path/to/members.csv
It's possible to pass more options, in order to achieve a better migration file for Ghost:
--pathToFile
(required)- Path to the signups CSV file as generated by Substack ("Total Email List").
- string - default:
null
-V
--verbose
- Show verbose output
- bool - default:
false
-s
--subs
- Path to the subscribers CSV file (paid, comp, gift) as generated by Substack ("Subscribers")
- string - default:
null
-l
--limit
- Define the batch limit for import files
- int - default:
5000
--comp
- Provide two values in the format
YY|YYYYMMDD:none|free
. YY is the threshold in years orYYYYMMDD
as the exact date after which Substackcomp
members should receive a complimentary plan depending on the expiry date.none|free
the option how to import members before this threshold, e. g.5:free
- string - default:
0:free
- Choices:
YY:none
,YY:free
,YYYYMMDD:none
,YYYYMMDD:free
- Provide two values in the format
--compLabel
- Provide a label for Substack
comp
subscribers - string - default:
substack-comp
- Provide a label for Substack
--gift
- Provide two values in the format
YY|YYYYMMDD:none|free
. YY is the threshold in years orYYYYMMDD
as the exact date after which Substackgift
members should receive a complimentary plan depending on the expiry date.none|free
the option how to import members before this threshold, e. g.5:free
- string - default:
0:free
- Choices:
YY:none
,YY:free
,YYYYMMDD:none
,YYYYMMDD:free
- Provide two values in the format
--giftLabel
- Provide a label for Substack
gift
subscribers - string - default:
substack-gift
- Provide a label for Substack
--freeLabel
- Provide a label for Substack
free
subscribers - string - default:
substack-free
- Provide a label for Substack
--paidLabel
- Provide a label for Substack
paid
subscribers - string - default:
substack-paid
- Provide a label for Substack
--cache
- Persist local cache after migration is complete (Only if
--zip
istrue
) - bool - default:
true
- Persist local cache after migration is complete (Only if
A more complex migration command that accounts for Stripe data could look like this:
migrate substack-members --pathToFile /path/to/emails.csv --subs /path/to/members.csv --giftLabel Gifted --freeLabel Free --paidLabel Supporter --compLabel 'Try It Out'
This is a mono repository, managed with lerna.
Follow the instructions for the top-level repo.
git clone
this repo &cd
into it as usual- Run
yarn
to install top-level dependencies.
To run a local development copy, cd
into this directory, and use yarn dev
instead of migrate
like so:
yarn dev substack-members --pathToFile /path/to/emails.csv
yarn lint
run just eslintyarn test
run lint and tests
Copyright (c) 2013-2025 Ghost Foundation - Released under the MIT license.