Import & Export Format
Before It's Gone can export your inventory to JSON or CSV, and import from either format. This page documents the exact structure expected for each.
JSON
Exporting
The app exports a single JSON object with three top-level keys:
{
"version": 1,
"exportedAt": "2026-05-18T14:30:00.000Z",
"items": [ ... ]
}
| Key | Type | Description |
|---|---|---|
version | number | Schema version. Currently always 1. |
exportedAt | string | ISO 8601 timestamp of when the export was created. |
items | array | Array of inventory item objects (see below). |
Item object
| Field | Type | Notes |
|---|---|---|
id | string | UUID. Preserved on re-import: re-importing the same export will overwrite the existing record rather than duplicate it. |
name | string | Display name of the item. |
quantity | number | Must be a positive integer ≥ 1. |
location | string | One of "fridge", "freezer", "pantry". |
barcode | string | null | Raw barcode string, or null if none was scanned. |
expiresAt | string | ISO 8601 timestamp of the expiry date. |
shelfLifeDays | number | Shelf life in days, stored at creation time. |
createdAt | string | ISO 8601 timestamp. |
updatedAt | string | ISO 8601 timestamp. |
category | string | null | Free-text category label, or null. |
depletionThreshold | number | null | Low-stock alert threshold (quantity), or null if unset. |
tags | string[] | Array of tag strings. Empty array if none. |
recurring | boolean | Whether the item auto-restocks when depleted. Optional; defaults to false. |
restockQuantity | number | undefined | Quantity to restock to when recurring is true. Optional. |
Full example
{
"version": 1,
"exportedAt": "2026-05-18T14:30:00.000Z",
"items": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Whole Milk",
"quantity": 2,
"location": "fridge",
"barcode": "5000112637922",
"expiresAt": "2026-05-25T23:59:59.000Z",
"shelfLifeDays": 10,
"createdAt": "2026-05-15T08:00:00.000Z",
"updatedAt": "2026-05-15T08:00:00.000Z",
"category": "dairy",
"depletionThreshold": 1,
"tags": ["organic", "fridge-door"],
"recurring": true,
"restockQuantity": 2
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Penne",
"quantity": 3,
"location": "pantry",
"barcode": null,
"expiresAt": "2027-12-31T23:59:59.000Z",
"shelfLifeDays": 730,
"createdAt": "2026-05-10T10:00:00.000Z",
"updatedAt": "2026-05-10T10:00:00.000Z",
"category": "pasta",
"depletionThreshold": null,
"tags": [],
"recurring": false
}
]
}
Importing
The importer reads the items array from inside the { version, exportedAt, items } envelope. Items missing an id or name are silently skipped. If you re-import a file that was previously exported, existing items with matching id values are overwritten in place rather than duplicated.
Tip: JSON import is best for full backups and restores. Use CSV import when adding items from a spreadsheet.
CSV
Exporting
The exported CSV uses the following fixed column order:
name,quantity,location,barcode,expiresAt,category,tags,shelf_life_days,depletion_threshold,createdAt
- Values that contain commas are double-quoted.
barcode,category,shelf_life_days, anddepletion_thresholdare empty strings when not set.expiresAtis a date-only string (YYYY-MM-DD);createdAtis a full ISO 8601 timestamp.tagsis a semicolon-separated list (e.g.organic;bulk); empty string if none.
Note: CSV export omitsid,recurring,restockQuantity, and per-item photos. Use JSON export for a complete, lossless backup.
Example export
name,quantity,location,barcode,expiresAt,category,tags,shelf_life_days,depletion_threshold,createdAt
"Whole Milk",2,fridge,5000112637922,2026-05-25,dairy,organic;fridge-door,10,1,2026-05-15T08:00:00.000Z
Penne,3,pantry,,2027-12-31,pasta,,730,,2026-05-10T10:00:00.000Z
"Free-range Eggs, 6pk",6,fridge,5010029005120,2026-06-01,eggs,,21,2,2026-05-18T09:00:00.000Z
Importing
The CSV importer accepts any column order and a wider set of column names. Column headers are case-insensitive.
Column reference
| Column name | Required | Type | Notes |
|---|---|---|---|
name | Yes | string | Item display name. Rows with an empty name are skipped. |
expires_at | Yes | string | Expiry date. See date formats below. Also accepted as expiresat (no underscore). |
location | Yes | string | Must be exactly fridge, freezer, or pantry (case-sensitive). Rows with any other value are skipped. |
quantity | No | number | Defaults to 1 if missing or non-numeric. Always at least 1. |
barcode | No | string | Leave empty if none. |
category | No | string | Free-text category label. |
shelf_life_days | No | number | Shelf life in days. If omitted, calculated from expires_at minus today. Always at least 1. |
tags | No | string | Semicolon-separated list of tags, e.g. organic;bulk;fridge-door. |
depletion_threshold | No | number | Low-stock alert threshold. Leave empty for none. |
Date formats
The importer accepts all of the following formats for expires_at. The time portion is always normalised to end-of-day UTC automatically — you never need to write a timestamp yourself.
| Format | Example |
|---|---|
YYYY-MM-DD | 2026-06-01 |
YYYY/MM/DD or YYYY.MM.DD | 2026/06/01 |
MM/DD/YYYY or M/D/YYYY | 6/1/2026 |
MM-DD-YYYY or M-D-YYYY | 6-1-2026 |
| Full ISO timestamp | 2026-06-01T23:59:59.000Z |
| Month name, day first | June 1, 2026 · Jun 1 2026 |
| Month name, day last | 1 June 2026 · 1 Jun 2026 |
Note: Slash and dash formats with a 4-digit year at the end (e.g.6/1/2026) are treated as MM/DD/YYYY. UseYYYY-MM-DDto avoid ambiguity.
Rows that are skipped
A row is skipped (counted in the "X rows skipped" status message) if:
nameis empty or missingexpires_at/expiresatis empty, missing, or not a parseable datelocationis not one offridge,freezer,pantry- The entire row throws an unexpected parse error
Skipped rows do not cause the import to fail; the rest of the file continues to be processed.
Example import file
name,quantity,location,expires_at,barcode,category,shelf_life_days,tags,depletion_threshold
Whole Milk,2,fridge,2026-05-25,5000112637922,dairy,10,organic;fridge-door,1
Penne,3,pantry,2027-12-31,,pasta,730,,
"Free-range Eggs, 6pk",6,fridge,2026-06-01,5010029005120,eggs,21,,,2
Frozen Peas,4,freezer,2027-03-01,,vegetables,365,bulk,
Tip: When creating a CSV by hand or from a spreadsheet, useYYYY-MM-DDforexpires_atand separate multiple tags with semicolons (not commas).
Format comparison
| JSON export | CSV export | JSON import | CSV import | |
|---|---|---|---|---|
id | ✅ | ❌ | ✅ preserved | ❌ new UUIDs assigned |
name | ✅ | ✅ | ✅ | ✅ |
quantity | ✅ | ✅ | ✅ | ✅ |
location | ✅ | ✅ | ✅ | ✅ |
barcode | ✅ | ✅ | ✅ | ✅ |
expiresAt / expires_at | ✅ full ISO | ✅ date only | ✅ | ✅ |
shelfLifeDays | ✅ | ✅ (shelf_life_days) | ✅ | ✅ (shelf_life_days) |
category | ✅ | ✅ | ✅ | ✅ |
tags | ✅ | ✅ semicolon-separated | ✅ | ✅ semicolon-separated |
depletionThreshold | ✅ | ✅ (depletion_threshold) | ✅ | ✅ (depletion_threshold) |
recurring / restockQuantity | ✅ | ❌ | ✅ | ❌ |
photo | ✅ | ❌ | ✅ | ❌ |
createdAt / updatedAt | ✅ | createdAt only | ✅ preserved | ❌ set to import time |
For a lossless round-trip (export → edit → re-import), always use JSON. CSV export omits id, recurring, restockQuantity, and photos. CSV import creates new item IDs and timestamps.
Barcode import
The Import barcodes (.txt) button in the Data section accepts a plain text file with one barcode per line:
5000112637922
5010029005120
3017620429484
For each barcode the app:
- Queries Open Food Facts for the product name, category, and nutritional info.
- Creates a new inventory item using your default shelf life and default storage location from Settings.
- Saves the nutritional profile (kcal/100g, allergens) to the barcode profile store.
A progress bar tracks enrichment. Barcodes that Open Food Facts does not recognise are still imported, using the raw barcode string as the item name.
Note: Barcode import always creates new items with new UUIDs. It does not deduplicate against existing inventory.