--- title: "Import products" description: "Add or update many products at once by uploading a file in the Tokenz Dashboard, instead of creating them one by one." source: "https://docs.tokenz.one/en/v2/product/import" api_version: "v2" locale: "en" version_status: "current" docs_stage: "prod" --- # Import products Add or update many products at once by uploading a file in the Tokenz Dashboard, instead of creating them one by one. Open **Products**, then select **Import products**. Products are managed in live mode, so switch off test mode first. ## Before you start The **Import products** button appears only when your role has all of these permissions. Ask an admin to add any that are missing. | Group | Permissions | | --- | --- | | Catalog items | View catalog items, Create catalog items, Edit catalog items | | Price plans | View price plans, Create price plans, List price plans | ## Choose a format | Format | Use it when | | --- | --- | | CSV (`.csv`) | Most catalogs: one price per product, one image URL per product. Easy to export from a spreadsheet. | | JSON (`products.json`) | You need prices per region or more than one image per product. Every image is a URL. | | ZIP (`products.json` + images) | Same as JSON, but you want to upload the image files together with the manifest. | All formats use the same rules: products are matched by `sku`, and the import only adds or updates products. It never deletes a product that is missing from the file. ## CSV format ### File rules - Encoding is UTF-8, with or without the byte order mark (BOM) that Excel adds. Japanese Excel's plain "CSV" (Shift_JIS) and Excel's "Unicode Text" (UTF-16) are also read. See [Save a CSV from a spreadsheet](https://docs.tokenz.one/en/v2/product/import#save-a-csv-from-a-spreadsheet). - Values are separated by commas. Semicolon and tab separated files also work: the separator is detected from the header row. - Prices use a dot for decimals, such as `7.50`. A decimal comma (`7,50`), a currency symbol (`¥480`) or a thousands separator (`1,000`) is reported as not a number. - Wrap a value in double quotes if it contains the file's separator (a comma, or a semicolon or tab in those files), a double quote or a line break. Write a double quote inside a value as `""`. For example, in a semicolon-separated file: `gem-pack-500;500 Gems;"500 gems; added instantly.";480;JPY` - The first row must be a header row. Header names are case-insensitive. Empty columns and blank rows that a spreadsheet adds are ignored. - Row numbers in messages count data rows: the first row under the header is row 1. - A CSV or JSON file can be up to 25 MB. There is no limit on the number of rows. ### Columns | Column | Required | Description | | --- | --- | --- | | `sku` | Yes | Your unique product ID. Importing the same `sku` again updates that product. | | `name` | Yes | Product name. | | `description` | Yes | Product description. The column must be present, but the value may be empty. | | `price` | Unless `free` is true | Price as a number in the currency's normal unit, for example `480` for ¥480 or `4.99` for $4.99. | | `currency` | When `price` is set | ISO 4217 currency code, for example `JPY` or `USD`. See [Supported currencies](https://docs.tokenz.one/en/v2/checkout/currency). | | `free` | No | `true` for a free product. Leave `price` and `currency` empty. | | `category` | No | Category name. A category that doesn't exist yet is created. | | `taxCategory` | No | `virtualCurrency`, `digitalGoodsAndServices`, `eBook` or `saas`. Default: `digitalGoodsAndServices`. Use `virtualCurrency` for in-game currency. | | `publish` | No | `true` publishes the product. Default: `false`, which saves it as a draft. | | `image_url` | No | One public `https` image URL. | Boolean columns (`free`, `publish`) accept `true`/`false`, `yes`/`no` or `1`/`0`. ### Example Three game items priced in JPY. The second row shows quoting, and the third is a free item. ```csv sku,name,description,price,currency,free,category,taxCategory,publish,image_url gem-pack-500,500 Gems,"500 gems, added to your account instantly.",480,JPY,false,Gems,virtualCurrency,true,https://cdn.example.com/items/gem-pack-500.webp starter-bundle,Starter Bundle,"1,200 gems and the ""Rookie"" badge.",1980,JPY,false,Bundles,virtualCurrency,false,https://cdn.example.com/items/starter-bundle.webp daily-gift,Daily Free Gift,One free reward per day.,,,true,Free gifts,virtualCurrency,true, ``` ## Save a CSV from a spreadsheet Start from the template: select **Download template** in the import dialog, fill it in, and save it as CSV. ### Microsoft Excel - Use **File** > **Save As** (or **Save a Copy**) and choose **CSV UTF-8 (Comma delimited) (*.csv)**. This is the recommended format in every language. - On a Japanese system, Excel's default **CSV (Comma delimited)** saves the file as Shift_JIS. That works too, and Japanese text imports correctly. - In regions that use a decimal comma, such as much of Europe, Excel saves "CSV" with semicolons and writes prices like `7,50`. The semicolons are detected, but a decimal comma is not read as a price. Before saving, make the decimal separator a dot (in Excel for Windows, **File** > **Options** > **Advanced**, clear **Use system separators** and set **Decimal separator** to `.`), or type the prices as text with a dot. - Excel turns long numeric SKUs such as `123456789012` into `1.23457E+11` and drops leading zeros. Format the `sku` column as **Text** before you enter the SKUs. A SKU saved in scientific notation is refused, because its digits are already lost. ### Google Sheets Use **File** > **Download** > **Comma-separated values (.csv)**. The file is UTF-8 and imports as it is. ### Apple Numbers Use **File** > **Export To** > **CSV**. Keep **Text Encoding** set to **Unicode (UTF-8)**. ## JSON format A JSON import is a single manifest named `products.json`. Upload it on its own when every image is a URL, or inside a ZIP when it refers to image files. ### Manifest | Field | Required | Description | | --- | --- | --- | | `version` | Yes | Always `1`. | | `defaults` | No | Values used by every product that doesn't set its own: `currency`, `taxCategory`, `category` and `publish`. | | `products` | Yes | The list of products. | ### Product fields | Field | Required | Description | | --- | --- | --- | | `sku` | Yes | Your unique product ID, with no leading or trailing spaces. | | `name` | Yes | Product name. | | `description` | Yes | Product description. Use `""` for none. | | `price` | One pricing field | A single price as a number, in the currency's normal unit. | | `free` | One pricing field | `true` for a free product. | | `regionPrices` | One pricing field | Prices per region, keyed by 2-letter country code, for example `{ "JP": 980, "US": 6.99 }`. Each region uses its own currency. The first entry is the price for every region you don't list. | | `currency` | No | Currency of `price`. Defaults to `defaults.currency`, then `JPY`. Not allowed with `regionPrices`. | | `category` | No | Category name. Created if it doesn't exist. | | `taxCategory` | No | `virtualCurrency`, `digitalGoodsAndServices`, `eBook` or `saas`. | | `images` | No | Image paths or URLs. The first image is the main image. | | `publish` | No | `true` to publish, `false` to save as a draft. Must be a real boolean, not a string. | Each product needs exactly one pricing field: `price`, `free: true` or `regionPrices`. ### Example ```json { "version": 1, "defaults": { "currency": "JPY", "taxCategory": "virtualCurrency", "publish": false }, "products": [ { "sku": "starter-bundle", "name": "Starter Bundle", "description": "1,200 gems and the Rookie badge.", "price": 1980, "category": "Bundles", "images": ["images/starter-bundle.webp"] }, { "sku": "daily-gift", "name": "Daily Free Gift", "description": "", "free": true, "images": ["https://cdn.example.com/items/daily-gift.webp"], "publish": true }, { "sku": "gem-pack-1200", "name": "1,200 Gems", "description": "Priced for each region.", "regionPrices": { "JP": 980, "US": 6.99, "GB": 5.99 }, "category": "Gems", "images": [ "images/gem-pack-1200.webp", "https://cdn.example.com/items/gem-pack-1200-alt.webp" ] } ] } ``` ### Package images in a ZIP When any image is a relative path, such as `images/starter-bundle.webp`, upload a ZIP with `products.json` at its root. Relative paths are read from inside the ZIP. ```text products.zip products.json images/ starter-bundle.webp gem-pack-1200.webp ``` ## Prices Write every price in the currency's normal unit (`480` for ¥480, `4.99` for $4.99), as a number greater than 0. This applies to `price` in CSV and JSON and to every amount in `regionPrices`. - **Decimals.** A price can't have more decimal places than its currency uses. JPY and KRW have none, so `480.5` JPY is refused. Most currencies, such as USD and EUR, use 2. A few, such as BHD and KWD, use 3. See [Supported currencies](https://docs.tokenz.one/en/v2/checkout/currency) for each currency's decimals. - **Maximum.** The largest price is 99,999,999.99, scaled to the currency's decimals: | Currency decimals | Example currencies | Maximum price | | --- | --- | --- | | 0 | JPY, KRW | 99,999,999 | | 2 | USD, EUR | 99,999,999.99 | | 3 | BHD, KWD | 99,999,999.999 | The preview checks every price before anything is written, so a price that breaks these rules stops the import with a message naming the row. ## Images - Formats: WebP, PNG, JPEG or GIF. - Each image can be up to 25 MB. Empty files are rejected. - Image URLs must be publicly reachable. URLs that need a login, and private or local addresses such as `localhost`, are rejected. - CSV takes one URL per product in `image_url`. Use JSON or a ZIP for several images or for image files. - When you update a product, leaving out its images keeps the images it already has. ## Re-import and updates Products are matched by `sku`: - **New `sku`**: the product is created. - **Existing `sku`**: the product is updated with the values in the file. This is the default. - **`sku` not in the file**: the product is left as it is. An import never deletes products. To add new products without touching existing ones, select **Create only (skip existing SKUs)** before you import. Rows whose `sku` already exists are skipped and counted as skipped in the result. A few things an import doesn't change on an existing product: - **Price of an existing plan.** If the product already has a plan for the same billing interval (for example, its one-time price) at a different price, the row keeps the current price. The rest of the row is still updated, and the result notes "price not applied". To change the price, open the product in the Dashboard and change it there. Replacing a plan's price is a separate step that can't be undone, so an import never does it. - **Tax category.** It is set when the product is created. ## Import steps 1. Select **Import products** and upload your `.csv`, `products.json` or `.zip` file. 2. Review the preview. Each row shows whether it will be created, updated or skipped, and any errors. 3. Fix any errors and upload again. Nothing is written while the file has errors. 4. Select **Import**. Progress is shown as products are created and updated. 5. Review the result: how many products were created, updated, skipped and failed, with a reason for each failure. ## Common errors The import dialog shows each problem with the row it is on and the SKU, for example "Row 3 (SKU gem-pack-500): add a currency for the price, such as USD or JPY." In a JSON file, products are numbered by position ("Product 2 (SKU …)"). Nothing is written while any problem remains: fix the file and upload it again. ### File problems These stop the file from being read at all. | Message | Fix | | --- | --- | | Add these columns to the header row: sku, description. | Add the missing required columns. `sku`, `name` and `description` are always required, even when `description` is empty. Start from the template if in doubt. | | Unknown columns: … Rename or remove them. | Rename the column to one of the [listed columns](https://docs.tokenz.one/en/v2/product/import#columns), or delete it. | | … can't be imported from a CSV. | The column, such as `regionPrices`, needs JSON. See [Choose a format](https://docs.tokenz.one/en/v2/product/import#choose-a-format). | | These columns appear more than once: … | Keep one column of each name. | | Column 7 has no name but row 2 has a value in it. | A header cell was deleted while the column still has data. Type the header back, or delete the whole column. | | Row 4: a quoted value is never closed. | A value starts with `"` but never ends with one, so the rest of the file reads as one value. Add the closing quote, or remove the stray one. | | Row 4: there is text after a closing quote. | Put the whole value inside the quotes, and write a quote mark inside it as `""`. | | Row 5 has more values than the 10 columns in the header. | A value contains the file's separator (a comma, or a semicolon or tab) but isn't quoted, so later values shifted columns. Wrap that value in double quotes. | | This CSV's text encoding can't be read. | Save the file again as **CSV UTF-8** from Excel, or download it again from Google Sheets or Numbers. | | This file has a header row but no products. | Add one product per row under the header. | | This file is larger than 25MB. | Split the catalog into several files and import them one after another. | | The ZIP has no products.json. | Put `products.json` at the top level of the ZIP, not inside a folder. | ### Row problems | Message | Fix | | --- | --- | | price "7,50" isn't a number. | Use a dot for decimals (`7.50`), with no currency symbol or thousands separator. See [Save a CSV from a spreadsheet](https://docs.tokenz.one/en/v2/product/import#microsoft-excel) if Excel writes decimal commas. | | JPY allows 0 decimal places, so 480.5 can't be charged. Round the price. | Round to the decimals the currency uses. JPY and KRW have none. See [Prices](https://docs.tokenz.one/en/v2/product/import#prices). | | price 150000000 is above the maximum of 99,999,999 JPY. | Use a price within the [maximum](https://docs.tokenz.one/en/v2/product/import#prices) for the currency. | | add a currency for the price, such as USD or JPY. | In CSV, every row with a `price` needs a `currency`. | | currency … isn't supported. | Use a code from [Supported currencies](https://docs.tokenz.one/en/v2/checkout/currency). | | another row uses SKU … too. Give each product its own SKU. | Each `sku` may appear only once per file. Change or remove the duplicate row. | | SKU 1.23457E+11 looks like a number Excel turned into scientific notation. | Format the `sku` column as Text, enter the SKUs again and save. | | add a SKU. / remove the spaces before or after the SKU. | Give every row a `sku`, with no spaces around it. | | add a name. / add a description. | Fill in `name`. Include `description`, even if it is empty. | | add a price and currency, or set free to true. | Set a price and currency, or mark the product free. | | set a price or set free to true, not both. | For a free product, leave `price` and `currency` empty. | | price must be more than 0. | Use a positive price, or leave it empty and set `free` to `true`. | | taxCategory "…" isn't valid. | Use `virtualCurrency`, `digitalGoodsAndServices`, `eBook` or `saas`. | | publish is "…". Set it to true or false. | Use `true`/`false`, `yes`/`no` or `1`/`0` in CSV. In JSON, write `true` or `false` without quotes. | | image_url "…" isn't a full https URL. | Use a complete link that starts with `https://`. | | A product uses an image that isn't in the ZIP. | Add the file to the ZIP at the same path, or fix the path. Paths are case-sensitive. | | A product uses an image that is empty or larger than 25MB. | Replace the file with a smaller image. | | Set version to 1. | Set `"version": 1` in `products.json`. | ### After the import The result lists each row that failed or was changed less than the file asked, with a note in English. | Note | What it means | | --- | --- | | price not applied: this interval already has a plan… | The product kept its current price. See [Re-import and updates](https://docs.tokenz.one/en/v2/product/import#re-import-and-updates) to change it. | | sku already exists | You chose **Create only**, so the existing product was skipped. | | sku belongs to another product | The SKU is in use by a product the import can't update. Use a different SKU. | If text looks garbled after import, the file was saved in an encoding the import guessed wrong. Save it as **CSV UTF-8** and import again. ## Need help? Contact Tokenz support with the file you tried to import and the error shown in the dashboard.