Data Dictionary · snapshot 2026.10

Relational Schema Specification

Seven tables in INCIDB Complete, plus three in INCIDB Korea (§8), exported as pipe-delimited UTF-8 CSV and Apache Parquet with identical columns. Every enrichment column is either populated from a named public source with a documented join, or NULL. There are no default values and no placeholder rows.

TableRows
products20,435
brands6,542
ingredients46,013
product_ingredients382,797
ingredient_name_map79,047
fragrance_allergens277
regulatory_status641

1. brands

6,542 brand identities as recorded in Open Beauty Facts.

FieldTypeDescriptionExample
brand_idINTEGERPrimary key1
nameSTRINGBrand name as recorded upstreamLaneige

2. products

20,435 products, one row each, every one carrying a usable on-pack ingredient declaration.

FieldTypeDescriptionExample
product_idINTEGERPrimary key68597
brand_idINTEGERFK → brands.brand_id. NULL when the source label records no brand55
barcode_eanSTRINGGTIN / EAN barcode4006381333931
nameSTRINGProduct name as recorded upstreamGood Genes Lactic Acid Treatment
obf_categories_tagsSTRINGOpen Beauty Facts' own tag list, joined with ;, verbatim — present on 13,647 of 20,435 products (66.8%), NULL where the source has noneen:hygiene;en:soaps
raw_ingredient_textSTRINGThe unparsed declaration, kept verbatim so the parse can be re-derivedWater, Glycerin, ...
created_atSTRINGIngestion 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.

FieldTypeSourceDescription
ingredient_idINTEGER—Primary key
inci_nameSTRINGcanonicalisationThe canonical name every raw label token was resolved to
cosing_matchedINTEGERCosIng1 when the canonical name matched the inventory. Gates every CosIng column below
cosing_ref_noSTRINGCosIngCosIng reference number
cas_numberSTRINGCosIngCAS registry number — from CosIng only, never inferred
ec_numberSTRINGCosIngEC number
functionsSTRINGCosIngCosIng functional categories, multi-valued, ;-separated
chemical_descriptionSTRINGCosIngCosIng chemical / IUPAC description text
cosing_restrictionSTRINGCosIngRestriction reference, e.g. an Annex V entry
cosing_update_dateSTRINGCosIngAlways NULL in this snapshot — the export carries no update date
annex_ii … annex_viFLOATCosIngMembership 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_allergenINTEGEREU Annex IIIEU fragrance-allergen flag — see the method note below
allergen_sourceSTRINGEU Annex IIIThe list a flag came from
comedogenic_ratingFLOATauthored0–5 rating for the ingredients the cited paper covers; NULL otherwise
is_fungal_acne_triggerFLOATruleRule-derived flag — a heuristic, not a measured property
rating_sourceSTRINGauthored / ruleThe per-row citation or rule text behind the two columns above

Measured coverage of the CosIng columns

ColumnShare of the 46,013 distinct namesShare of the 382,797 label occurrences
CosIng match (any)12.2%85.4%
functions12.0%84.3%
cas_number9.0%78.8%
chemical_description9.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.

FieldTypeDescriptionExample
product_idINTEGERFK → products.product_id68597
ingredient_idINTEGERFK → ingredients.ingredient_id1214
position_indexINTEGER1-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 proportion1
concentration_percentageSTRINGAlways 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.

FieldTypeDescriptionExample
raw_nameSTRINGThe token as it appeared in products.raw_ingredient_textAQUA (WATER)
canonical_nameSTRINGThe canonical INCI name it resolved toAQUA
methodSTRINGHow it resolved — see belowparen_stripped
confidenceFLOATConfidence attached to the method0.9
ingredient_idINTEGERFK → ingredients.ingredient_id162
part_indexINTEGEROrder of this part within raw_name; 1 when the token was not cut2
split_ruleSTRINGThe re-split rules applied to raw_name, +-joined; NULL when not re-splitbracket+period
methodMeaning
exactThe cleaned token is already a CosIng inventory name
cleanedResolved after entity decoding, character stripping and whitespace collapse
typo_mapResolved through a small, explicit, cited typo table
percent_strippedA declared percentage was removed
paren_strippedA parenthetical was removed
synonym_mapResolved through an explicit synonym table
slash_variantA slash-joined multilingual variant
splitA run-together token split into two names, both of which matched the inventory
slash_same_casTwo slash-joined CosIng names that share a CAS number (CI 77891 / TITANIUM DIOXIDE): one ingredient, mapped to the first-listed name
unresolved_residualUnmatched text left between names recovered by the re-split, kept in label order, never guessed
unresolvedNo 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.

FieldTypeDescription
annex_iii_refSTRINGAnnex III entry reference as printed
legal_nameSTRINGThe name as printed in the Regulation, parenthetical alias kept; on LABEL_NAME rows, the collective label name
label_asSTRINGThe collective label name the Regulation prescribes (e.g. Rose Ketones); NULL when it prescribes none
cas_listed, ec_listedSTRINGCAS and EC numbers as printed for the entry
leave_on_threshold_pctDECIMALLabelling threshold in leave-on products, in percent, parsed per entry
rinse_off_threshold_pctDECIMALLabelling threshold in rinse-off products, in percent, parsed per entry
placing_on_market_untilDATEEnd 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_untilDATEEnd of the making-available transition, likewise
transition_conditionSTRINGThe Regulation's proviso for replaced entries, verbatim; NULL elsewhere
instrumentSTRINGThe legal instrument for the entry
ingredient_id, inci_nameINTEGER / STRINGThe matched INCIDB ingredient; NULL when the legal name matched nothing
match_methodSTRINGNAME, LABEL_NAME, CAS (chemically defined substances only) or BOTANICAL_CAS_REVIEW; NULL when unmatched
flaggedBOOLEAN1 for a flag that sets is_common_allergen; 0 on botanical CAS review rows and unmatched rows
sourceSTRINGEURLEX (the consolidated legal text) or COSING_ANNEX_III (a name CosIng lists that the legal text does not print)
source_url, retrieved_atSTRING / DATEWhere 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.

FieldTypeDescription
ingredient_id, inci_nameINTEGER / STRINGThe INCIDB ingredient
casSTRINGINCIDB's own CAS value (not used for matching)
jurisdictionSTRINGEU; CA, ASEAN and CN reserved
list_refSTRINGThe list entry, e.g. an Annex III entry
statusSTRINGPROHIBITED (Annex II), RESTRICTED (Annex III), ALLOWED_WITH_CONDITIONS (Annexes IV–VI); LISTED_EXISTING reserved
instrumentSTRINGCosIng's Regulation column as printed
product_typeSTRINGProduct type / body parts, verbatim
max_concentrationSTRINGMaximum concentration in the ready-for-use preparation, verbatim; never collapsed to one number
condition_textSTRINGAnnex II: the Chemical name / INN text verbatim (where conditional bans live). Annexes III–VI: the Other and Wording of conditions columns, joined
effective_dateDATEOnly where the source states one; NULL on EU rows
match_methodSTRINGNAME (CosIng glossary name) or IDENTIFIED_INGREDIENT (CosIng Identified INGREDIENTS column). No CAS route for EU rows
source_url, retrieved_atSTRING / DATEThe CosIng export the row came from and the date it was fetched
source_update_dateSTRINGCosIng'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).

TableKey columnsDescription
kr_mfds_entriesentry_id, annex, section, name_ko, limit_ko, max_pct, condition_kind, effective_fromOne row per printed entry, with the parsed limit and scope fields and the page it is printed on
kr_mfds_substancesentry_id, cas, cas_valid, chemical_name_ko, cosing_inci_namesOne row per CAS sub-row, with the CosIng names that share the CAS number (a cross-reference, not a translation)
kr_mfds_linksingredient_id, inci_name, cas, entry_id, review, scope_noteINCIDB ingredients linked to entries by exact CAS; review links and links not yet hand-checked ship but are left out of the published counts