Import file format
Import lives in Settings → Data → Import data. It takes two kinds of input — a JSON file, or a plain list — and works out which one you gave it on its own. You never pick a format.
Nothing is written to your account until you have seen the preview and confirmed it.
Three situations cover almost everything:
From a list you already have
The quickest route, and it needs no file at all: paste your list into the box on the import page, or drop it in as a .txt or .md file.
## Birthday
- [Sony WH-1000XM5](https://www.amazon.de/dp/B09XS7JWHH)
- Wool blanket — https://shop.com/wool-blanket
- [x] Kettle
- A long walk with a friend
## Kitchen
- https://shop.com/p/9912
One item per line. A ## heading starts a new list; anything above the first heading goes into the list you chose on the import page. Bullets, numbers and checkboxes are understood, and lines that are only a link still get a name. The exact rules are under How a pasted list is read, further down.
From another app
Most wishlist apps can export your data, but no two of them agree on a format. Rather than convert it by hand, hand both halves to a chat assistant — ChatGPT, Claude, Gemini — and let it do the reformatting.
- Export your wishlist from the other app, however that app does it.
- Copy the prompt below into the assistant, followed by your export. A screenshot, a page of notes or a pile of links works just as well as a real export file.
- Save the reply as a file ending in
.json. - Drop that file on the import page.
Turn my wishlist into a Things To Have import file. Follow the format documented at https://thingstohave.app/help/import-format exactly: one JSON object with "schemaVersion": 1, a "lists" array and a "things" array, where each list references its items by their ids in "thingIds". Reply with the JSON only.
Agents get things wrong, so read the preview before you confirm — it lists every item that will be created, and nothing is written to your account until you say so.
From a Things To Have export
Settings → Data → Export my data produces a file in exactly the format described below, so an export always imports back cleanly — the same lists, the same items, their original dates. Anything that was in no list lands in the list you chose on the import page. Use it to move things to another account, or to put back something you deleted.
Two things do not survive the round trip: images are never imported, and neither are followers or settings.
The JSON format
The file is one JSON object with three parts:
schemaVersion— must be1. A file without it, or with any other version, is rejected.lists— the lists to create. Each list references the things in it by their ids, throughthingIds.things— the items themselves. A thing that no list mentions goes into the list you chose on the import page.
Fields
| Field | Where | Required | Notes |
|---|---|---|---|
id | both | yes | Any number, unique within the file. Used to link the two. |
name | both | yes | An item or list without a name is skipped. |
description | lists | no | Free text. |
isPublic | lists | no | true makes the list publicly visible. Defaults to false. |
defaultViewMode | lists | no | "list" or "grid". |
thingIds | lists | no | The ids of the things in this list. |
url | things | no | Must be an http(s) link. Stored exactly as written. |
note | things | no | Free text. |
price + currency | things | no | A number plus a currency, or one string like "120 USD". |
identifier | things | no | Your own reference, e.g. a model number. |
isOwned | things | no | true marks the item as already owned. |
createdAt | both | no | ISO date. Kept as written, so history survives a round trip. |
A price always needs a currency. "price": 120 on its own is skipped; "price": 120, "currency": "USD" is fine.
A complete example
{
"schemaVersion": 1,
"lists": [
{
"id": 1,
"name": "Birthday",
"description": "Things I would love",
"isPublic": true,
"defaultViewMode": "grid",
"thingIds": [1, 2]
},
{
"id": 2,
"name": "Kitchen",
"isPublic": false,
"thingIds": [3]
}
],
"things": [
{
"id": 1,
"name": "Sony WH-1000XM5",
"url": "https://www.amazon.de/dp/B09XS7JWHH",
"note": "Black, not silver",
"price": 379,
"currency": "EUR"
},
{
"id": 2,
"name": "Wool blanket",
"isOwned": true
},
{
"id": 3,
"name": "Kettle",
"url": "https://shop.com/p/9912",
"price": "49.90 USD",
"identifier": "KT-9912"
}
]
}
How a pasted list is read
Anything that is not JSON is read line by line. Blank lines are ignored, and -, * and 1. prefixes are stripped.
- A
##heading starts a new list named after the heading. Items above the first heading go into the list you chose on the import page. [Name](link)uses the name and the link.- Text before a link becomes the name; trailing dashes, colons and pipes are trimmed off.
- A bare link is named after the readable part of its address —
https://shop.com/wool-blanketbecomes "Wool blanket", andhttps://shop.com/p/9912falls back to "shop.com". - A line with no link becomes an item with just a name.
- [x]imports the item as already owned.
Limits
- Files must be
.txt,.jsonor.md, up to 2 MB and 1,000 items per import. - Import never deduplicates. A link you already have is imported again, and the same link twice in one file becomes two items.
- Rows that cannot be imported — no name, a price that cannot be read, an invalid link, a field that is too long — are listed on the result screen and can be downloaded, corrected and imported again.