1. brands
6,542 brand identities as recorded in Open Beauty Facts.
| Field | Type | Description | Example |
brand_id | INTEGER | Primary key | 1 |
name | STRING | Brand name as recorded upstream | Laneige |
2. products
20,435 products, one row each, every one carrying a usable on-pack ingredient declaration.
| Field | Type | Description | Example |
product_id | INTEGER | Primary key | 68597 |
brand_id | INTEGER | FK → brands.brand_id. NULL when the source label records no brand | 55 |
barcode_ean | STRING | GTIN / EAN barcode | 4006381333931 |
name | STRING | Product name as recorded upstream | Good Genes Lactic Acid Treatment |
obf_categories_tags | STRING | Open Beauty Facts' own tag list, joined with ;, verbatim — present on 13,647 of 20,435 products (66.8%), NULL where the source has none | en:hygiene;en:soaps |
raw_ingredient_text | STRING | The unparsed declaration, kept verbatim so the parse can be re-derived | Water, Glycerin, ... |
created_at | STRING | Ingestion timestamp (ISO 8601) | 2026-07-02T14:01:48Z |
3. ingredients
One row per distinct canonical INCI name — 46,013 of them. Every CosIng-derived column is populated only where the canonical name matched the European Commission CosIng inventory on an exact name match, and is NULL otherwise.
| Field | Type | Source | Description |
ingredient_id | INTEGER | — | Primary key |
inci_name | STRING | canonicalisation | The canonical name every raw label token was resolved to |
cosing_matched | INTEGER | CosIng | 1 when the canonical name matched the inventory. Gates every CosIng column below |
cosing_ref_no | STRING | CosIng | CosIng reference number |
cas_number | STRING | CosIng | CAS registry number — from CosIng only, never inferred |
ec_number | STRING | CosIng | EC number |
functions | STRING | CosIng | CosIng functional categories, multi-valued, ;-separated |
chemical_description | STRING | CosIng | CosIng chemical / IUPAC description text |
cosing_restriction | STRING | CosIng | Restriction reference, e.g. an Annex V entry |
cosing_update_date | STRING | CosIng | Always NULL in this snapshot — the export carries no update date |
annex_ii … annex_vi | FLOAT | CosIng | Membership of Annexes II (prohibited), III (restricted), IV (colorants), V (preservatives), VI (UV filters). Legacy exact-name method; regulatory_status is the precise source and may differ |
is_common_allergen | INTEGER | EU Annex III | EU fragrance-allergen flag — see the method note below |
allergen_source | STRING | EU Annex III | The list a flag came from |
comedogenic_rating | FLOAT | authored | 0–5 rating for the ingredients the cited paper covers; NULL otherwise |
is_fungal_acne_trigger | FLOAT | rule | Rule-derived flag — a heuristic, not a measured property |
rating_source | STRING | authored / rule | The per-row citation or rule text behind the two columns above |
Measured coverage of the CosIng columns
| Column | Share of the 46,013 distinct names | Share of the 382,797 label occurrences |
| CosIng match (any) | 12.2% | 85.4% |
functions | 12.0% | 84.3% |
cas_number | 9.0% | 78.8% |
chemical_description | 9.5% · 4,322 distinct values | — |
The left column counts distinct names; the right counts ingredient occurrences across product labels. They differ by an order of magnitude because a label corpus contains far more distinct strings — botanical variants, multilingual spellings, marketing tokens — than any regulatory inventory lists, while the slots on a real label are filled overwhelmingly by ingredients CosIng does cover. Neither number alone describes the data. ec_number, cosing_restriction and the Annex flags are populated on the CosIng-matched subset only, at their own rates — see the build_report.json shipped with the archive.
Method — allergen flags. One list only: the fragrance allergens of Annex III to the EU Cosmetics Regulation (Regulation (EC) 1223/2009 as amended by Regulation (EU) 2023/1545), matched by exact INCI name, then by the collective label name the Regulation prescribes, then by CAS number for chemically defined substances only. Botanical CAS hits are shipped as review rows, never flags. 120 names are flagged; the per-entry evidence is in fragrance_allergens (table 6). The US FDA has not yet published its MoCRA fragrance-allergen list, so this dataset carries no US flag.
Method — comedogenic ratings. 142 ingredients carry a rating on the 0–5 scale, transcribed from Table I (pp. 324–326) of Fulton JE Jr., Comedogenicity and irritancy of commonly used ingredients in skin care products, J Soc Cosmet Chem 1989;40:321–333. Where the paper prints a range rather than a single grade, the rating is left NULL. Absence of a rating means "not covered by the cited source", never "safe".
Method — fungal-acne flag. A rule-derived heuristic, not a measured property: no per-ingredient Malassezia assay exists to transcribe. The rule flags C11–C24 fatty acids and their esters, and polysorbates 20/40/60/80, applied only to CosIng-matched ingredients, giving 277 flags. Basis: Saunte et al., Front Cell Infect Microbiol 2020 (DOI 10.3389/fcimb.2020.00112), with the chain-length window from Liebregts et al., FEMS Yeast Res 2025 (DOI 10.1093/femsyr/foaf043). Specific oils, butters and waxes are not flagged — the literature does not support ingredient-level calls on them.
4. product_ingredients
382,797 ordered composition links, preserving the order in which ingredients are declared on the label.
| Field | Type | Description | Example |
product_id | INTEGER | FK → products.product_id | 68597 |
ingredient_id | INTEGER | FK → ingredients.ingredient_id | 1214 |
position_index | INTEGER | 1-indexed label position, contiguous 1..n per product (a repeated ingredient keeps its first position); lower means declared earlier, i.e. present in a higher proportion | 1 |
concentration_percentage | STRING | Always NULL in this snapshot — labels almost never declare percentages | |
5. ingredient_name_map
79,047 rows: the canonicalisation evidence, shipped with the data so every decision the pipeline made can be audited or overridden.
| Field | Type | Description | Example |
raw_name | STRING | The token as it appeared in products.raw_ingredient_text | AQUA (WATER) |
canonical_name | STRING | The canonical INCI name it resolved to | AQUA |
method | STRING | How it resolved — see below | paren_stripped |
confidence | FLOAT | Confidence attached to the method | 0.9 |
ingredient_id | INTEGER | FK → ingredients.ingredient_id | 162 |
part_index | INTEGER | Order of this part within raw_name; 1 when the token was not cut | 2 |
split_rule | STRING | The re-split rules applied to raw_name, +-joined; NULL when not re-split | bracket+period |
| method | Meaning |
exact | The cleaned token is already a CosIng inventory name |
cleaned | Resolved after entity decoding, character stripping and whitespace collapse |
typo_map | Resolved through a small, explicit, cited typo table |
percent_stripped | A declared percentage was removed |
paren_stripped | A parenthetical was removed |
synonym_map | Resolved through an explicit synonym table |
slash_variant | A slash-joined multilingual variant |
split | A run-together token split into two names, both of which matched the inventory |
slash_same_cas | Two slash-joined CosIng names that share a CAS number (CI 77891 / TITANIUM DIOXIDE): one ingredient, mapped to the first-listed name |
unresolved_residual | Unmatched text left between names recovered by the re-split, kept in label order, never guessed |
unresolved | No canonical match — kept verbatim and flagged, never guessed |
Method — re-split. Tokens that no method resolves are re-split by explicit rules (unclosed brackets, (and)/&, bullets, colons, . separators with an abbreviation guard, the last ingredient/"contains" marker, spacing around /, newlines and spaced dashes only where re-joining does not resolve, and a strict full-coverage split in which every word must belong to a matched name). Rules run only on unresolved tokens; nothing is fuzzy, and every recovered name must match the CosIng inventory or the cited typo and synonym maps exactly. slash_same_cas carries 0.75. A part cut out of a token carries its method's usual confidence minus 0.1, and unresolved_residual rows carry 0.0. retokenise in split_rule means the token resolved after re-tokenising its original text (line breaks intact) and re-joining line- or dash-wrapped names; no separator rule split it. The full rule list is in DATA_DICTIONARY.md.
unresolved is the largest bucket by distinct token and a small one by label occurrence — the same long-tail effect that produces the two coverage columns above.
6. fragrance_allergens
The EU fragrance-allergen labelling entries of Annex III (Regulation (EC) 1223/2009 as amended by Regulation (EU) 2023/1545): one row per legal name and INCIDB match, with unmatched legal names and botanical CAS review rows included.
| Field | Type | Description |
annex_iii_ref | STRING | Annex III entry reference as printed |
legal_name | STRING | The name as printed in the Regulation, parenthetical alias kept; on LABEL_NAME rows, the collective label name |
label_as | STRING | The collective label name the Regulation prescribes (e.g. Rose Ketones); NULL when it prescribes none |
cas_listed, ec_listed | STRING | CAS and EC numbers as printed for the entry |
leave_on_threshold_pct | DECIMAL | Labelling threshold in leave-on products, in percent, parsed per entry |
rinse_off_threshold_pct | DECIMAL | Labelling threshold in rinse-off products, in percent, parsed per entry |
placing_on_market_until | DATE | End of the placing-on-market transition for entries whose consolidated text carries the transition footnote ((37), (38) or (40)); NULL on the others |
making_available_until | DATE | End of the making-available transition, likewise |
transition_condition | STRING | The Regulation's proviso for replaced entries, verbatim; NULL elsewhere |
instrument | STRING | The legal instrument for the entry |
ingredient_id, inci_name | INTEGER / STRING | The matched INCIDB ingredient; NULL when the legal name matched nothing |
match_method | STRING | NAME, LABEL_NAME, CAS (chemically defined substances only) or BOTANICAL_CAS_REVIEW; NULL when unmatched |
flagged | BOOLEAN | 1 for a flag that sets is_common_allergen; 0 on botanical CAS review rows and unmatched rows |
source | STRING | EURLEX (the consolidated legal text) or COSING_ANNEX_III (a name CosIng lists that the legal text does not print) |
source_url, retrieved_at | STRING / DATE | Where the row came from and the date it was fetched |
7. regulatory_status
EU Annex II–VI status from the European Commission CosIng exports: one row per ingredient, jurisdiction and list entry. Rows exist only where a list says something; absence of a row is not a status. Not legal advice.
| Field | Type | Description |
ingredient_id, inci_name | INTEGER / STRING | The INCIDB ingredient |
cas | STRING | INCIDB's own CAS value (not used for matching) |
jurisdiction | STRING | EU; CA, ASEAN and CN reserved |
list_ref | STRING | The list entry, e.g. an Annex III entry |
status | STRING | PROHIBITED (Annex II), RESTRICTED (Annex III), ALLOWED_WITH_CONDITIONS (Annexes IV–VI); LISTED_EXISTING reserved |
instrument | STRING | CosIng's Regulation column as printed |
product_type | STRING | Product type / body parts, verbatim |
max_concentration | STRING | Maximum concentration in the ready-for-use preparation, verbatim; never collapsed to one number |
condition_text | STRING | Annex II: the Chemical name / INN text verbatim (where conditional bans live). Annexes III–VI: the Other and Wording of conditions columns, joined |
effective_date | DATE | Only where the source states one; NULL on EU rows |
match_method | STRING | NAME (CosIng glossary name) or IDENTIFIED_INGREDIENT (CosIng Identified INGREDIENTS column). No CAS route for EU rows |
source_url, retrieved_at | STRING / DATE | The CosIng export the row came from and the date it was fetched |
source_update_date | STRING | CosIng's Update Date for the entry, as printed |
status is the entry's annex category, not a verdict on the ingredient. A row, especially one with match_method = IDENTIFIED_INGREDIENT, means CosIng links the INCI name to that list entry. An Annex II ban covers only the substance, form or use the entry describes in condition_text (for example hair-dye use only, the nano form only, or a component such as furocoumarins), so the same ingredient can also carry an Annex III–VI row. Read condition_text before concluding anything.
8. INCIDB Korea tables
Sold separately as INCIDB Korea (INCIDB Complete plus the South Korea MFDS overlay); not part of INCIDB Complete. Every entry of MFDS Notice 2026-19, Annex 1 and Annex 2, with the Korean text as printed and INCIDB links by exact CAS. Many Annex 1 entries apply only as limited by a condition or exception written in the entry (condition_kind, hair_dye_exemption, scope_note). An ingredient without a link has no status in this data. Not legal advice. Every column is described in DATA_DICTIONARY.md (section 8).
| Table | Key columns | Description |
kr_mfds_entries | entry_id, annex, section, name_ko, limit_ko, max_pct, condition_kind, effective_from | One row per printed entry, with the parsed limit and scope fields and the page it is printed on |
kr_mfds_substances | entry_id, cas, cas_valid, chemical_name_ko, cosing_inci_names | One row per CAS sub-row, with the CosIng names that share the CAS number (a cross-reference, not a translation) |
kr_mfds_links | ingredient_id, inci_name, cas, entry_id, review, scope_note | INCIDB ingredients linked to entries by exact CAS; review links and links not yet hand-checked ship but are left out of the published counts |