Importing Members Into Ghost Without Losing the Paid Ones

MIGRATIONImporting Members Into GhostWithout Losing the Paid Ones
On this page

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.

Frequently asked

Which columns does Ghost's member import accept?

email, name, note, subscribed_to_emails, stripe_customer_id, complimentary_plan, labels and created_at. Only email is required. Everything else is optional and everything not on that list is ignored.

Can the import assign a tier?

No. There is no tier column. The import can mark somebody complimentary, but which tier they hold is set afterwards, which is why sites with more than one tier need to plan the order of operations.

Does importing a paying member create a subscription?

No. The import creates a member record and access, never a billing relationship. Nobody is charged as a result of an import, and no payment authorisation exists until that person subscribes themselves.

What does stripe_customer_id do?

It links the member to an existing Stripe customer, and it accepts auto to match by email address. That is only useful when you are moving a Ghost site whose subscribers were already billed by your own Stripe account.

What is the one thing not to skip?

Labels. They are the only way to find this exact group again later. Adding them at import costs one column; adding them afterwards means matching lists by hand.