Importing members into Ghost is a five-minute job that people redo about a week later, once they discover what the import did not carry.
The fix is knowing that in advance, which takes about four minutes of reading.
What the import accepts
Ghost takes a CSV with these columns:
email, name, note, subscribed_to_emails, stripe_customer_id, complimentary_plan, labels, created_at.
Only email is required. Everything else is optional, and anything not on that list is ignored silently. Your export from wherever you are leaving almost certainly has columns Ghost will not read, and it will not warn you about them.
The column that is not there
There is no tier column.
complimentary_plan marks somebody as having free access. It does not say to what. On a site with one paid tier that distinction does not exist, and the import feels complete.
On a site with several, it is the whole problem. You import four hundred people as complimentary, and now four hundred people have access without you having said to which level. Sorting that out afterwards, per member, is the week-later job this article exists to prevent.
Two ways to avoid it.
Import in groups. One file per tier, each with a label naming that tier, then apply the tier to each labelled group in Ghost afterwards. More files, and the assignment is a bulk action per group rather than per person.
Import everyone, then split by label. One file, a label per intended tier, then work through the labels. Same idea, one fewer upload.
Either way, the label is what makes the tier assignment possible at all. Which is the next point, and it is the important one.
Labels are the part people skip
labels is the least interesting column and the one worth the most.
A label is the only handle you will have on this exact group of people later. Everyone who came from the old platform. Everyone who was paying when you moved. Everyone imported on that date.
Three weeks in, you will want to email exactly the people who came across and never re-subscribed. With a label that is a filter. Without one, it is matching a CSV against your member list by hand, and by then the list has moved on.
Multiple labels per member are fine. Be generous: imported-2026-08, was-paying, tier-standard. Nobody has ever regretted the extra column, and it costs one field in a file you are already building.
What the import does not do
It does not create a subscription. No billing relationship exists as a result of an import, ever. Nobody is charged, nothing renews, and no payment authorisation appears.
That sounds obvious and it is worth stating, because the interesting case is a paying subscriber from another platform. You import them, they show as complimentary, they have access, everything looks right. Nothing is billing them. If you meant that as a bridge while they re-subscribe, correct. If you thought their payments came across, they did not, and no import anywhere can do that. Why subscriptions cannot be transferred.
It does not send anything. Imported members receive no welcome email and no notification. Telling them is your job, and doing it before the import rather than after avoids the message where people wonder how you got their address.
It does not merge intelligently. The email address is the key. An address already present updates that member; a different address makes a new one. A list with Name@example.com and name@example.com will produce whatever Ghost’s normalisation produces, and it is worth deduplicating your file rather than finding out.
The stripe_customer_id column
This links a member to an existing Stripe customer, and it accepts auto to match by email.
It is narrower than it sounds. It is for moving a Ghost site whose subscribers were already billed through your own Stripe account, where the customers still exist and you want the new install to recognise them.
It cannot pull subscribers out of another platform’s Stripe account. Those customers belong to that platform, not to you, and the ID means nothing in your account.
If you are arriving from Substack, Patreon or anywhere similar, this column is not for you. Leave it out.
The order that works
Get the export and read it. Actually open it. Find out which of your columns Ghost accepts and what the paid status is called in your source, which is never complimentary_plan.
Build the file with labels planned. Decide the labels before the upload rather than during it.
Import a test file of five. Including one that already exists in Ghost, so you see the update behaviour rather than assuming it.
Check those five by hand. Email right, name right, label applied, complimentary flag where you expected.
Import the rest.
Assign tiers by label.
Then tell people. After the import, so the message and the reality agree.
Afterwards
Ghost’s member count is now much larger, and none of the growth is revenue.
That matters for two things. Your newsletter is going to a bigger list, so check your sending arrangement can take it before the next send rather than during it. And your own numbers now mix people who pay with people who were given access, so any counting you do needs the label to tell them apart.
Which is the third time labels have come up, and the reason is that they are the only piece of this you cannot add cheaply later.
Related: moving from Substack, and what happens to active subscriptions when you switch.