Jump to content

Food Logging Data Standard

From Open Food Facts wiki

Objective

The reason for developing a standard for food logging data exchange is to ensure that people are not locked in to a single application once they start logging food data. For example, they might want to switch to a different application for logging, use another application to analyse their food logging history or supply data to a third party, such as researchers or medical practitioners.

The next section describes the proposed standard with following sections providing background context and documentation of the design process.

Standard

Glossary

Entity Description Examples Also known as
Consumer The individual that this food log relates to. Note could possibly be an animal in the case of a pet food diary Me, John Smith User, Person
Food Something that can be ingested in the form that it was purchase by the Consumer Apple, Lasagne, Soda Product, fruit, vegetable
Source Where data about a Food or Food Facet came from Producer, Label, CIQUAL Provenance
Facet Any kind of information about food that can be aggregated to give insights into nutrition, health, the environment, etc. 234 kJ per 100g, 68g CO2 per 100ml Nutrients, Environmental Footprint
Facet Type Description of the facet and its unit of measure per 100 g / ml of the food kJ, Carbohydrates, CO2, Land usage Attribute
Recipe A collection of Foods that are combined to for the component of a meal (for a cake) 100g flour, 50g sugar, 50g butter Course, Plate, Dish, Ingredients
Preparation The way that the Food is processed before eating Baked, Fried, Re-heated Processing
Meal An occasion when Food was consumed This morning's breakfast Snack
Meal Type Classification of the nature and timing of the meal Breakfast, Lunch, Snack

Export Packaging

Exports will be packaged in a zip file with an arbitrary name (determined by the user). Each zip file will contain the following:

  • meals.csv
  • meals_metadata.json

Meals

This is a CSV file to make it as simple as possible for moderately technical Consumers or delegated third-parties to analyse their own data in a spreadsheet or database. All columns names should be human readable in the user's chosen language. A Metadata file will cross-reference the column names against pre-defined properties, like meal time, Meal Type, Food and nutrients.

Foods will include the human readable name, e.g. "Heinz Baked Beans" that was presented to the user when they selected the Food.

Date / times will be recorded in the user's local format and timezone, which would be identified in the metadata. Items consumed as part of the same meal should have exactly the same date / time to allow grouping of meals if needed.

Facets, like Nutrients, would have a single column name with a consistent unit of measure (identified in the metadata). The exporter could choose to include the unit in the column name if they feel this would be helpful. All Facets that were retrieved from the source of the Food information that were either presented to the user or used in calculations should be included in the export.

It is suggested that more system oriented fields, such as the Source, Global Trade Item Number (GTIN) and Image URLs are included at the "end" of the export line (this might be the leftmost columns in a right to left language).

An example might look like this (formatted as a table):

Time Meal Recipe Food Amount Measure Quantity Unit Protein Carbohydrate Fat Source Code Image
1-May-2025 6:00am Breakfast Kelloggs Corn Flakes 1 serving 30 g 0.3 24 1 GTIN 5059319030487 https://images.openfoodfacts.org/images/products/505/931/903/0487/front_en.3.400.jpg
1-May-2025 6:00am Breakfast Full Fat Milk 0.5 pint 284 ml 3.1 10.2 4.3 GTIN 5060066960071 https://images.openfoodfacts.org/images/products/506/006/696/0071/front_en.3.400.jpg
1-May-2025 9:00am Snack White Coffee Instant Coffee Powder 1 teaspoon 4 g 0 0.1 0 GTIN 4056489440628 https://images.openfoodfacts.net/images/products/405/648/944/0628/front_en.26.400.jpg
1-May-2025 9:00am Snack White Coffee Full Fat Milk 20 ml 20 ml 1.3 1.2 1.2 GTIN 5060066960071 https://images.openfoodfacts.org/images/products/506/006/696/0071/front_en.3.400.jpg

Metadata

The Metadata in an export provides a translation between the human readable column names and enumerations used in the Meals file and standardised representations of these. Applications with limited user-customization could potentially hard-code this Metadata file (per user language supported) if their exports always include the same standard column names.

The main component of the Metadata file is the "columns" property which is a single object with a key for each column name mentioned in the Meals CSV file. The value associated with each column is an object whose "type" column will determine the standardised property the column represents. Additional attributes, specific to the type, will identify other data relevant to that property, e.g. the standard nutrient code and unit for a "facet" column.

The full list of properties on the root Metadata object are as follows:

Property Description Example
columns Object with a key for each column name used in the Meals CSV file
"columns": {"type": "food"}
locale Combination of the user's ISO 639 2-letter language code and ISO 3166 2-letter country code separated with a hyphen
"locale": "en-US"
timezone A standard IANA TZ identifier
"timezone": "Europe/London"
<additional properties> Applications may add their own global properties. It is suggested that each application just adds one root property using a URI owned by that application as the key name. This URI would ideally point to a JSON Schema document describing the structure of the custom properties, but this is not essential.
"https://openfoodfacts.org/metadata.json":
 {
   ...
 }

All dates / times in the Meals CSV file will be formatted according to the default format associated with their locale as would be generated by the JavaScript Intl.DateTimeFormat object or equivalent.

However, numbers in both the Meals CSV and Metadata files should follow the JSON number standard to avoid the need for unnecessary CSV escaping in regions that normally use a comma as the decimal separator.

The following table lists the different column types and additional attributes to be specified:

Type Additional Attributes Description Example
time [None] The time that the meal was consumed
"Time": {"type": "time"}
meal values The different meal type names used, keyed with the value used in the CSV file. The value will be one of the following (derived from here):
  • breakfast
  • second-breakfast
  • brunch
  • elevenses
  • lunch
  • tea
  • dinner
  • supper
  • high-tea
  • siu-yeh
  • snack
  • daily

Note the "daily" item refers to entries where the user has just recorded overall consumption throughout the day, e.g. 6 cups of coffee

"Meal": {
    "type": "meal",
    "values": {
        "Breakfast": "breakfast",
        "Mail Meal": "dinner",
        "Daily Allowance": "daily"
    }
}
recipe [None] The name of a Recipe used that links multiple related foods in the meal
"Recipe": {"type": "recipe"}
food [None] The name of the Food as it was presented to the user when they selected it from the source
"Food": {"type": "food"}
entered_quantity [None] The amount of the food that the user recorded in the specified "entered_unit". Formatted according to the user's locale
"Amount": {"type": "entered_quantity"}
entered_unit [None] The unit used when recording the quantity of food. This is a free format string in the end-user's language
"Measure": {"type": "entered_unit"}
quantity [None] The amount of the food in normalized units (g for weight, ml for volume). Formatted according to the user's locale
"Quantity": {"type": "quantity"}
unit [None] The normalized unit type. "g" for weight or "ml" for volume. Not localized. This should match the unit used in the source, see the note on Units below.
"Unit": {"type": "unit"}
facet code The facet type as defined in this standard (TBA). e.g. "protein", "carbohydrates-total", "vitamin-b12", "energy-kj"
"Protein": {
    "type": "facet",
    "code": "protein"
},
"Iron (g)": {
    "type": "facet",
    "code": "iron"
},
"Vitamin B12 (µg)": {
    "type": "facet",
    "code": "vitamin-b12",
    "factor": 1000000
}
factor Value that the quantity of the facet in the Meals CSV file must be divided by in order to convert it to the unit defined for the facet type. Note that most nutrients will have a units of "g" but energy will be in "kJ" or "kcal" e.g. 1000 if the values in the Meals CSV are expressed in mg. Defaults to 1 if omitted.
source values The type of Food identifier used, keyed by the value used in the Meals CSV.

The value has a source, which can be "gtin" or "plu" and a location code, e.g. for GTIN this would be the Global Location Number

"Source": {
    "type": "source",
    "values": {
        "Bar code": {
            "source": "gtin",
            "location": "3014517900101"
        },
        "Food code": {
            "source": "plu"
        }
    }
}
code [None] The identifier for the Food in the specified source
"Code": {"type": "code"}
image [None] URL to an image that was presented to the user when they selected the food from the source
"Image": {"type": "image"}
<additional properties> [Any] Application properties that are specific to the individual meal. It is suggested that each application adds type for each custom column using a URI owned by that application as the key name. This URI would ideally point to a JSON Schema document describing the structure of the custom type, but this is not essential.
"Green Score": {
"type":"https://openfoodfacts.org/green.json",
...
}

A full example of a Meal Metadata file follows:

{
  "columns": {
    "Heure": {
      "type": "time"
    },
    "Repas": {
      "type": "meal",
      "values": {
        "Petit-déjeuner": "breakfast",
        "Dîner": "dinner"
      }
    },
    "Recette": {
      "type": "recipe"
    },
    "Nourriture": {
      "type": "food"
    },
    "Nombre": {
      "type": "entered_quantity"
    },
    "Mesure": {
      "type": "entered_unit"
    },
    "Quantité": {
      "type": "quantity"
    },
    "Unité": {
      "type": "unit"
    },
    "Protéines": {
      "type": "facet",
      "code": "protein"
    },
    "Fer (g)": {
      "type": "facet",
      "code": "iron"
    },
    "Vitamine B12 (µg)": {
      "type": "facet",
      "code": "vitamin-b12",
      "factor": 1000000
    }
  },
  "locale": "fr-FR",
  "timezone": "Europe/Paris"
}

API Packaging

Storing and retrieving data via an API would be performed by an application rather than a user, so there is no requirement to support localization of fields or units. However, it is still important to store the data that was presented to the user, such as the Food name, as it may not be possible to re-create this from the source.

Authentication

APIs should be secured using OAuth using Beater tokens supplied in the HTTP Authorization Request Header. The method of authentication and obtaining and renewing access tokens is beyond the scope of this specification.

Metadata

The metadata format is already in a suitable form for use in a REST API and API backends should support PUT and GET methods for this (automatically qualified by the consumer based on the authorization header).

Meals

Meals should be structured as an array of JSON objects with the following schema:

property Description Example
id A UUID string formatted according to RFC 9562. Optional on new entries (will be generated by the receiving service)
"id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
time The time that the meal was consumed in UTC following the ISO 8061 format
"time": "2025-07-30T15:56:32Z"
meal The meal type. The value will be one of the following (derived from here):
  • breakfast
  • second-breakfast
  • brunch
  • elevenses
  • lunch
  • tea
  • dinner
  • supper
  • high-tea
  • siu-yeh
  • snack
  • daily

Note the "daily" item refers to entries where the user has just recorded overall consumption throughout the day, e.g. 6 cups of coffee

"meal": "breakfast"
recipe [optional] The name of a Recipe used that links multiple related foods in the meal
"recipe": "White coffee"
food The name of the Food as it was presented to the user when they selected it from the source
"food": "Instant Coffee Powder"
entered_quantity The amount of the food that the user recorded in the specified "entered_unit". Formatted according to the JSON number standard
"entered_quantity": 1
entered_unit The unit used when recording the quantity of food. This is a free format string in the end-user's language
"entered_unit": "tsp"
quantity The amount of the food in normalized units (g for weight, ml for volume). Formatted according to the JSON number standard
"quantity": 15
unit The normalized unit type. "g" for weight or "ml" for volume. This should match the unit used in the source, see the note on Units below.
"unit": "ml"
<facet> The property name will be a facet type as defined in this standard (TBA). e.g. "protein", "carbohydrates-total", "vitamin-b12", "energy-kj". The value will in "g" in most cases but energy will be in "kJ" or "kcal"
"protein": 0,
"iron": 0.02,
"vitamin-b12": 1.2e-6
source The type of Food identifier used, which can be "gtin" or "plu"
"source": "gtin"
location [optional] The location code for the source, e.g. for GTIN this would be the Global Location Number
"location": "3014517900101"
code The identifier for the Food in the specified source
"code": "4056489440628"
image URL to an image that was presented to the user when they selected the food from the source
"image": "https://images.openfoodfacts.net/images/products/405/648/944/0628/front_en.26.400.jpg"
<additional properties> Application properties that are specific to the individual meal. This should match the type specified in the metadata
"https://openfoodfacts.org/green.json": 34

The following example shows the same data in JSON format as the previous CSV export example, with the addition of a custom property:

[
    {
        "time": "2025-05-01T05:00:00Z",
        "meal": "breakfast",
        "food": "Kelloggs Corn Flakes",
        "entered_quantity": 1,
        "entered_unit": "serving",
        "quantity": 30,
        "unit": "g",
        "protein": 0.3,
        "carbohydrate": 24,
        "fat": 1,
        "source": "gtin",
        "location": "3014517900101",
        "code": "5059319030487",
        "image": "https://images.openfoodfacts.org/images/products/505/931/903/0487/front_en.3.400.jpg",
        "https://openfoodfacts.org/green.json": 53
    },
    {
        "time": "2025-05-01T05:00:00Z",
        "meal": "breakfast",
        "food": "Full Fat Milk",
        "entered_quantity": 0.5,
        "entered_unit": "pint",
        "quantity": 284,
        "unit": "ml",
        "protein": 3.1,
        "carbohydrate": 10.2,
        "fat": 4.3,
        "source": "gtin",
        "location": "3014517900101",
        "code": "5060066960071",
        "image": "https://images.openfoodfacts.org/images/products/506/006/696/0071/front_en.3.400.jpg",
        "https://openfoodfacts.org/green.json": 31
    },
    {
        "time": "2025-05-01T08:00:00Z",
        "meal": "snack",
        "recipe": "White Coffee",
        "food": "Instant Coffee Powder",
        "entered_quantity": 1,
        "entered_unit": "teaspoon",
        "quantity": 4,
        "unit": "g",
        "protein": 0,
        "carbohydrate": 0.1,
        "fat": 0,
        "source": "gtin",
        "location": "3014517900101",
        "code": "4056489440628",
        "image": "https://images.openfoodfacts.net/images/products/405/648/944/0628/front_en.26.400.jpg",
        "https://openfoodfacts.org/green.json": 45
    },
    {
        "time": "2025-05-01T08:00:00Z",
        "meal": "snack",
        "recipe": "White Coffee",
        "food": "Full Fat Milk",
        "entered_quantity": 20,
        "entered_unit": "ml",
        "quantity": 20,
        "unit": "ml",
        "protein": 1.3,
        "carbohydrate": 1.2,
        "fat": 1.2,
        "source": "gtin",
        "location": "3014517900101",
        "code": "5060066960071",
        "image": "https://images.openfoodfacts.org/images/products/506/006/696/0071/front_en.3.400.jpg",
        "https://openfoodfacts.org/green.json": 31
    }
]

A meals API should support the following methods (only accessing data for the authenticated consumer):

  • POST: Creates meals from the supplied array in the HTTP request body using the application/json content type
  • GET: Returns an array of meals in the response body that were logged between mandatory "from_time" and "to_time" query parameters , specified in ISO 8061 format
  • DELETE: Deletes a meal item. The meal "id" is supplied in the path

Drivers

The following factors have influenced the design of the data standard:

  1. It should be easy for users to store data themselves without requiring a back-end service, e.g. as a zip download or in a cloud file system
  2. It should also be possible to develop an API that can provide real-time read/write capabilities to any application that the user has authorised
  3. The format should be extensible in a controlled way so that new food facets can be introduced, e.g. additional micro-nutrients, environmental scores, etc.
  4. It should be possible for "short cuts" to be persisted between export and import, e.g. "I ate the same thing as I had last Wednesday"
  5. The data should include what was presented to the user when they identified the product / ingredient that they consumed to allow subsequent correction of historic data if something was mis-identified
  6. The data should be formatted in a way that allows users with moderate technical skills to analyse their own data using simple tools, like a spreadsheet
  7. Users will want to see data of interest to them presented in their own language with measurement units they are familiar with

Sources

The following existing applications and formats were considered in developing this specification:

Food Diary Applications and Data Formats

OpenNutriTracker

Support for import / export was recently added.

Waistline

Has an import / export facility but CSV does not show specific foods / products.

Tidepool

This is tool, primarily aimed at people with diabetes, which includes an import capability which was developed to support the Dexcom API but is not widely used.

General Food-related Standards

GS1 / GDSN

International body for product bar coding and trade information exchange. The GDSN incorporates a number of mechanisms for describing food items, some examples listed below, but no specific capability for food logging.

NAct: The Nutrition and Activity Ontology

This is an OWL Ontology describing relationships between certain foods, vitamins, allergies, intolerances, diets, etc. It does not seem to accommodate incorporation of regular bar-coded products or food logging.

Conceptual Data Model

In abstract terms a food diary consists of a number of food intake events. An event might be regarded as a meal or a snack and could consist of one or more "courses" or "plates", such as starter, main course or desert. Each course will have a recipe, consisting of one or more ingredients prepared in a certain way. The ingredient could simply be a pre-prepared product (ready-meal), a raw fruit or vegetable or a combination of other ingredients that have been prepared separately (such as a pizza dough).

The term Food is used as a catch-all to cover courses, ingredients, raw foods and products. Hence a meal or snack will consist of one or more Foods (courses), each of which has a preparation method (baked, raw, fried, re-heated, etc.) and potentially itself consists of a number of other Foods (ingredients).

The following diagram depicts the model which is described in more detail in the glossary above:

[1]

Note that the following entities are not implemented:

  • Preparation: This is potentially quite a complex area as individual components of a meal might have different and / or multiple preparation methods applied for different times, e.g fried for 5 minutes and then boiled for 15 minutes.
  • Ingredients: This relates more to managing user recipes, which is outside the scope of this standard. Ingredients for core foods could be obtained from the source of the food if required.

Use Cases

The following use cases were considered from human and machine perspectives:

Human Use-cases

  • Analyse nutrition over certain time periods. e.g. "how has my protein intake varied over the last month?"
  • Search for when specific foods were consumed, e.g. to determine some kind of reaction pattern.
  • View recipes that were previously used so that they can be re-logged.

Machine Use-cases

  • Be able to recognise Facets (such as nutrients) and their units of measure to support proprietary computations, e.g. calculation of a personalised nutritional score
  • Identify Foods used and their associated composition to be able to correct for historic errors or retro-fit additional Facets
  • To be able to store custom Facets
  • Store and retrieve data using an API

Design Considerations

Data Format

For the human use cases that involve summing or searching for data then a spreadsheet compatible format, like CSV, is most appropriate. Structures designed to be mainly read by machines are likely to be more hierarchical, so JSON or YAML are more appropriate. JSON is suggested for machine oriented data as it is has a more limited syntax than YAML and it's appearance is more obviously not intended to be read by humans.

It is highly likely that users will log the same Food over many different Meals so it might seem logical to normalise the data and store distinct Foods in a separate structure to Meals. However, a Food's nutritional composition could change over time and keeping the data separate could introduce the possibility of inconsistencies, e.g. a Meal that references a Food that is not included in the export. It is therefore proposed that Foods are exported with each Meal in a denormalized form.

When considering Recipes it is not within the scope of this standard to offer a structured way of defining all aspects of a Recipe, such as how individual ingredients are prepared and processed (cooked) and in what order and combination. For this reason it is proposed that no specific recipe structure is included, but the details of a meal will include an optional Recipe column to allow Foods that came from the same recipe to be grouped together, making it easier for users to repeatedly log the same Recipe from meal to meal. To facilitate this, Foods from a Recipe that were consumed in the a single meal should have identical logged Date / Times.

Nutrient Types

It was initially proposed to use the GDSN NutrientTypeCode as a universal way of representing nutrients. However, there are potentially copyright issues with this data set and its comprehensiveness and complexity could give rise to a false sense of accuracy.

Also, in practice, the nutrient values will be what is presented on the packaging and could have been derived in a number of ways, e.g. through chemical analysis or simply adding up data from the constituent ingredients. It should be possible to draw the distinction between Total Carbohydrates (including fibre, as used in the US and Canada) and Available Carbohydrates (excluding fibre, as used in the EU). However, for other ingredients there may be subtle differences in calculation which the Consumer will not be aware of (e.g. different Nitrogen multipliers used to determine Protein).

It is therefore proposed that the list of nutrient types is defined as part of this specification, which will initially be based on the Open Food Facts nutrients taxonomy.

Units

Users might record the amount consumed in a number of different ways, e.g. bunch, pinch, serving. It is the responsibility of the food logging app to determine how to convert this into a normalized unit (g or ml) in order to determine nutrient values or other facets. Given that units like "serving", "bunch", etc. may have different sizes depending on the type of Food it was decided to not model units in the metadata but instead record what was entered on each individual meal entry.

Note this should match the unit used in the source that the recorded facets, such as nutrient information, were quoted against. e.g. if the source quoted nutrients per 100ml then the unit should be ml. If the user entered the quantity in unit that is fundamentally different from the unit on the source then it is the responsibility of the food logging app to convert to the normalized unit, e.g. if the user recorded 0.5 cups of flour the app would need to convert this to grammes.

TODO

  • Additional sources, such as CIQUAL and USDA
  • JSON schema for metadata file
  • Add more strict wording around SHALL, SHOULD and MAY