# INCI Beauty Safety Score & Ingredients Scraper (`extractify-labs/incibeauty-inci-ingredients`) Actor

Scrape INCI Beauty by barcode: the 0-20 safety score, full risk-rated INCI composition and ingredient reference dictionary. Score INCI, pay per result.

- **URL**: https://apify.com/extractify-labs/incibeauty-inci-ingredients.md
- **Developed by:** [Extractify Labs](https://apify.com/extractify-labs) (community)
- **Categories:** E-commerce, Developer tools
- **Stats:** 5 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## 🧴 INCI Beauty Safety Score & Ingredients Scraper

Scan a barcode, get the score French shoppers actually check. This actor turns **incibeauty.com** into a clean, structured feed: every cosmetic product keyed by its **EAN/GTIN barcode**, carrying INCI Beauty's published **0-20 "Note INCI Beauty" safety score**, the **full ordered ingredient (INCI) composition** with each ingredient's **risk class**, product images and retail price offers. Alongside the products you get a **16,879-record INCI ingredient reference dictionary** (INCI name, CAS, EINECS, functions, risk class, origin, multilingual names). Two entities, one dataset, joinable on the ingredient identifier.

The key thing to understand up front: **we reproduce INCI Beauty's own published data, faithfully.** The 0-20 score and the green-to-red ingredient ratings are theirs, captured verbatim. This actor invents no methodology and computes no score of its own. If you have ever been burned by a scraper that bolts on its own opinionated "safety rating," that is exactly the trap this avoids.

> 🇫🇷 *INCI Beauty est un service du marché français. Cet acteur extrait les produits cosmétiques (score 0-20 « Note INCI Beauty », composition INCI complète, classe de risque par ingrédient) et le dictionnaire de référence des ingrédients INCI d'incibeauty.com.*

---

### 👤 Who it's for

- 💄 **Cosmetic brands & formulators** (R&D, regulatory, competitive marketing). See exactly how your SKUs and your competitors' score on a French-market gatekeeper, ingredient by ingredient, and benchmark composition against your own catalog.
- 📱 **Clean-beauty, "dupe" and comparison apps.** Barcode in, score plus composition out, so you can power "is this safe?" and "show me a better-rated alternative" without building a scoring system.
- 🛒 **Retailers & e-commerce teams** enriching product pages with a recognized safety score and surfacing better-rated SKUs.
- 🔬 **Researchers & NGOs** running ingredient-prevalence and cosmetic-safety studies across the catalog plus the full ingredient dictionary.
- ✍️ **Affiliate & SEO content sites** building score-plus-price comparison content that earns engagement.

---

### ✅ What you get

- 🎯 **The real 0-20 "Note INCI Beauty" score**, plus the quality tier behind the colored-flower badge, captured exactly as INCI Beauty publishes it.
- 🌿 **Per-ingredient risk, green to red.** Every ingredient in the composition carries its hazard level (0 = no concern, higher = more concern) and its penalty families, aligned to the declared order.
- 🧪 **The full ordered INCI composition** for each product, not a summary. This is the headline field.
- 🔍 **A transparent score breakdown** (valid ingredient count, composition size, clean and bio flags) so you can see *why* a product scored the way it did and reconcile it against your own data.
- 🆔 **EAN/GTIN as the primary key**, so products drop straight into retail catalogs, open product databases and your own SKU lists.
- 📚 **A 16,879-record INCI ingredient dictionary**: official INCI name, CAS, EINECS, food-additive E-number, cosmetic functions, risk/penalty class, origin, free-text notes, localized names (22 locales) and a count of how many products contain each ingredient.
- ⏱️ **Freshness built in.** Risk classes and scores are a moving target on INCI Beauty as their knowledge base updates. Every row carries a `scraped_at` capture timestamp, and products carry the source's own composition date, so a periodic re-crawl keeps you current instead of stuck with a one-time dump.
- ⚙️ **You choose what to pull**: products only, the ingredient dictionary only, or both in one run. Narrow by brand, barcode, ingredient or specific URLs. Toggle composition and price.

---

### 🗂️ Scope & coverage (what's in, what's not)

**Covered**

- 🚀 **Products:** the cosmetic catalog keyed by EAN/GTIN, walked brand by brand across the full A-Z brand index (hundreds of thousands of products, on the order of ~300,000), de-duplicated by barcode. No result-window cap, so a full crawl reaches the whole brand-listed catalog.
- 🧬 **Product composition & score:** the complete per-product INCI composition (ordered list + per-ingredient risk + score breakdown), captured for each product by default.
- 💶 **Price offers:** the merchant price offers shown on each product page (EUR by default).
- 📖 **Ingredients:** the full browsable INCI reference dictionary, **16,879 verified distinct ingredients**, de-duplicated by ingredient id.

**Not covered (by design)**

- 💬 **Product reviews / Q&A** are **not collected.** They appear only on a Cloudflare-protected product page with no API path, so this actor does not fetch them. The review fields remain in the schema (always empty) for transparency.
- 🏷️ **Brandless products** (any product not listed under a brand) fall outside the brand-walk, the one structural item edge.
- 🔤 **Ingredient synonym tail** beyond the 16,879 browsable records (a much larger backend synonym keyspace) is intentionally out of default scope.
- 🔒 **Account-gated B2B / login-walled feeds** are out of scope. This actor reads only what INCI Beauty shows publicly to anonymous visitors; it never logs in.

These edges could be covered later by companion actors if you need them. They are boundaries, not defects.

---

### 📊 Output fields

The actor writes **one dataset** with two row types. Every row has an `entity` field set to either `product` or `ingredient`. Product rows fill the product fields; ingredient rows fill the ingredient fields; a small set of fields is shared by both. Use the built-in **Products** and **Ingredient reference** dataset views to see each as a clean table, or filter on `entity` yourself.

#### Shared fields (both entities)

| Field | Type | Description |
|---|---|---|
| `entity` | string | Row type: `product` or `ingredient`. Use it to filter, or pick the matching dataset view. |
| `sourceUrl` | string | The incibeauty.com page this row was collected from. |
| `website` | string | Source website host (`incibeauty.com`). |
| `scrapeDate` | string | UTC date (YYYY-MM-DD) the row was collected. Scores and risk classes change over time, so this matters. |
| `scraped_at` | string \| null | UTC capture timestamp (ISO-8601). The freshness key for the mutable score and risk data. |
| `country` | string \| null | ISO country of the service (FR). |
| `currency` | string \| null | Price currency for product offers (EUR). |
| `product_locale` | string \| null | Locale of the captured row (default `fr`). |
| `locale_variants` | array \| null | Other-language versions of the page available (hreflang variants). |
| `canonical_url` | string \| null | Canonical page URL. |

#### Product fields (`entity = "product"`)

| Field | Type | Description |
|---|---|---|
| `ean_gtin` | string \| null | The product barcode (EAN / GTIN). Primary key for products. Several barcode forms can occur (13-digit, zero-padded, hyphenated). |
| `product_name` | string \| null | Product title. |
| `brand` | string \| null | Brand name. |
| `brand_slug` | string \| null | Brand slug (the key to the brand's page). |
| `marque_parent` | string \| null | Parent company or group behind the brand, when published. |
| `inci_score` | number \| null | The 0-20 "Note INCI Beauty" overall safety score. |
| `score_quality_tier` | string \| null | Quality tier behind the colored-flower badge. |
| `score_breakdown` | object \| null | The components behind the 0-20 score: score value, count of valid ingredients, composition size, and the clean / bio flags. |
| `is_unrated` | boolean \| null | True when the product is not yet scored. |
| `is_approximative_note` | boolean \| null | True when the score is approximate. |
| `product_category` | string \| null | Cosmetic category path. |
| `full_inci_composition` | array \| null | **The headline field.** The full ordered INCI ingredient list. Each entry carries the ingredient name, official name, risk level and function families. |
| `composition_ingredient_name` | array \| null | The official INCI names of the composition, in declared order. |
| `composition_ingredient_position` | array \| null | The 1-based declared position of each ingredient. |
| `per_ingredient_risk_class` | array \| null | Per-ingredient hazard level (0 = no concern, higher = more concern), aligned to the composition order. |
| `per_ingredient_penalty_reasons` | array \| null | Risk/penalty families per ingredient, aligned to the composition order. |
| `composition_status` | string \| null | Whether the composition is fully expanded (the normal case) or, in a rare fallback, returned in raw form. In the published actor this is the expanded form. |
| `composition_ciphertext` | string \| null | Empty in normal runs; populated only in the rare raw fallback noted above. |
| `composition_ciphertext_b64_len` | integer \| null | Diagnostic length value, present only alongside the rare fallback. |
| `product_image_large` | string \| null | Large product image URL. |
| `product_image_small` | string \| null | Small product image URL. |
| `product_image_thumb` | string \| null | Thumbnail image URL (from the brand listing card). |
| `user_photos` | array \| string \| null | User-submitted photos when available. |
| `where_to_buy_table` | array \| null | Merchant price offers. Each offer carries retailer, price value, currency, availability, a display string and a shop link. |
| `acheter_currency_options` | array \| null | Currencies offered by the price widget. |
| `acheter_response_status` | integer \| null | Status of the price/offers lookup (200, or 404 when a product has no offers). |
| `date_compo` | string \| null | When INCI Beauty last computed the composition/score (ISO-8601). A second freshness signal. |
| `similar` | object \| array \| null | Related/recommended products. |
| `alternative_products` | array \| null | "Produits alternatifs" links. **Not collected (bot-protected page; always null)** — for related products see `similar`. |
| `comment_count_new` | integer \| null | Count behind the "new comments" badge. |
| `comment_count_total` | integer \| null | Total review/comment count. **Not collected (always null).** |
| `reviews_comments` | array \| null | User reviews/comments. **Not collected** — the reviews page is bot-protected (always empty). |
| `review_author` | string \| null | Public review author pseudonym. **Not collected (always null).** |
| `review_body` | string \| null | Review free text. **Not collected (always null).** |
| `review_date` | string \| null | Review date. **Not collected (always null).** |
| `review_id` | integer \| null | Review identifier. **Not collected (always null).** |
| `product_mini` | object \| string \| null | A lighter product card surface, when available. |
| `product_subscription_state` | string \| boolean \| null | The anonymous "searchable" flag for the product. |
| `brand_logo` | string \| null | Brand logo image URL. |
| `product_weight` | string \| null | Net weight / volume, from the product information block. |
| `skin_type` | string \| array \| null | Target skin type(s), from the product information block. |
| `certifications` | array \| null | Certifications / labels (e.g. bio, clean). Sparse; present mainly on certified products. |
| `product_information` | array \| object \| null | The product information block: weight/volume, skin type, period-after-opening, usage advice. |
| `engagement_stats` | object \| null | Public engagement counters (scans, likes). |
| `product_natural_extracts` | array \| object \| null | Natural-extract details for the product. |

#### Ingredient fields (`entity = "ingredient"`)

| Field | Type | Description |
|---|---|---|
| `ingredient_id` | integer \| null | Integer ingredient id. Primary key for ingredients, and the join key from a product's composition. |
| `ingredient_slug` | string \| null | URL slug. |
| `inci_name` | string \| null | Official INCI name. |
| `ingredient_french_name` | string \| null | French common name. |
| `cas_number` | string \| null | CAS registry number. |
| `einecs_number` | string \| null | EINECS / EC number. |
| `additif_enumber` | string \| null | Food-additive E-number, when applicable. |
| `ingredient_description` | string \| null | Free-text "À savoir" note, when present. |
| `ingredient_functions` | array \| null | Cosmetic functions, each with a name and description. |
| `ingredient_risk_penalty_status` | string \| null | The colored-flower risk/penalty rating label. |
| `ingredient_risk_penalty_tier` | string \| null | The risk tier behind the flower icon. |
| `ingredient_origin` | array \| null | Origin classification (e.g. vegetal, synthetic). |
| `ingredient_classification` | array \| null | Classification labels. |
| `ingredient_names_other_languages` | array \| null | Names in other languages, from the detail page. |
| `ingredient_name_localized` | object \| null | A `{locale: name}` map across 22 locales. |
| `ingredient_products_containing` | array \| null | A sample of products that contain this ingredient, each with barcode, name, brand and score. |
| `ingredient_nb_produits` | integer \| null | How many products contain this ingredient (a scale signal). |
| `ingredient_natural_extract` | object \| string \| null | Natural-extract detail for the ingredient, when available. |
| `inci_name_md5` | string \| null | Stable hash of the INCI name. |
| `ingredient_spellcheck_name` | string \| null | Normalized name. |
| `ingredient_id_synonyme` | integer \| null | Synonym id (0 = canonical record). |
| `ingredient_link` | string \| null | Canonical ingredient link. |

> 🔗 **About the product ↔ ingredient join.** A product's composition carries an ingredient id, and the ingredient dictionary is keyed by that same id, so the two streams are designed to be joined on the ingredient identifier. Run with `entity = both` to get both in one feed.

---

### 🔧 Input

Everything is optional. Leave it all on the defaults for a quick, non-empty sample run.

| Input | What it does |
|---|---|
| **What to collect** (`entity`) | `products`, `ingredients`, or `both`. Products are EAN-keyed and scored with full composition; ingredients are the reference dictionary. |
| **Start URLs** (`startUrls`) | Specific INCI Beauty page URLs to collect: a product page (`/produit/<barcode>`), an ingredient page (`/ingredients/<id>-<name>`), or a brand page (`/brand/<brand-slug>`). Leave empty to crawl by the filters below. |
| **Brands** (`brands`) | Brand slugs to crawl (e.g. `loreal-paris`, `nivea`). Only products from these brands are returned. Empty = walk the full A-Z brand catalog. |
| **Product barcodes** (`eans`) | A list of EAN / GTIN barcodes to look up directly, skipping the brand walk. Ideal for enriching an existing barcode list. |
| **Ingredient identifiers** (`ingredientIds`) | A list of ingredient id-name segments (e.g. `12345-aqua`) to look up directly from the dictionary. |
| **Maximum results** (`maxItems`) | Stop after this many records per stream (products and ingredients are capped independently). Lower it for a quick test. The free daily-test default is 10. |
| **Include price offers** (`includePrice`) | ON by default; adds negligible cost. Collects the retail price offers shown on each product page. Products only. |
| **Enrich ingredient records** (`enrichIngredients`) | OFF by default. Adds extra naming, classification and CAS detail to each ingredient at the cost of one extra request per ingredient. Ingredients only. |
| **Proxy** (`proxyType`) | **No proxy** (default, fastest and cheapest) works for the whole actor. **Apify Proxy** routes through your own Apify account (you pay Apify for that traffic) if you want to rotate IPs. |

#### ⚡ Quick sample run (the default, finishes fast)

This is the prefilled default. It pulls a small slice of the ingredient dictionary and finishes non-empty in well under five minutes, with no proxy.

```json
{
  "entity": "ingredients",
  "maxItems": 10,
  "enrichIngredients": false,
  "proxyType": "none"
}
````

#### 💄 One brand, products with composition and score

Pull a single brand's products, each with the full risk-rated composition, the 0-20 score and price offers.

```json
{
  "entity": "products",
  "brands": ["respire"],
  "maxItems": 100,
  "includePrice": true,
  "proxyType": "none"
}
```

#### 🔢 A handful of barcodes (enrich your own SKU list)

Resolve specific barcodes straight to their scored product records.

```json
{
  "entity": "products",
  "eans": ["3760368740730", "4005808851904"],
  "includePrice": true,
  "proxyType": "none"
}
```

***

### 📦 Sample output

**A product row** (values are illustrative placeholders):

```json
{
  "entity": "product",
  "ean_gtin": "3760368740730",
  "product_name": "Deodorant Stick Fleur de Coton",
  "brand": "Respire",
  "brand_slug": "respire",
  "inci_score": 19.5,
  "score_quality_tier": "fleurfont1",
  "score_breakdown": { "score_sur20": 19.5, "valid_incis": 3, "composition_count": 3, "is_ib_clean": true, "is_bio": false },
  "product_category": "Deodorant",
  "composition_ingredient_name": ["HELIANTHUS ANNUUS SEED OIL", "PARFUM", "TOCOPHEROL"],
  "composition_ingredient_position": [1, 2, 3],
  "per_ingredient_risk_class": [0, 2, 0],
  "where_to_buy_table": [{ "retailer": "Example Shop", "price_value": 9.9, "price_currency": "EUR", "price_availability": "in_stock" }],
  "product_image_large": "https://incibeauty.com/photos/products/large/.../image.jpg",
  "date_compo": "2025-08-20T04:52:45Z",
  "sourceUrl": "https://incibeauty.com/produit/3760368740730",
  "scrapeDate": "2026-06-22",
  "scraped_at": "2026-06-22T18:00:00Z"
}
```

**An ingredient row** (values are illustrative placeholders):

```json
{
  "entity": "ingredient",
  "ingredient_id": 1447,
  "inci_name": "HELIANTHUS ANNUUS SEED OIL",
  "ingredient_french_name": "Huile de graines de tournesol",
  "cas_number": "8001-21-6",
  "einecs_number": "232-273-9",
  "ingredient_functions": [{ "function_name": "Emollient", "function_desc": "Softens the skin." }],
  "ingredient_risk_penalty_status": "No concern",
  "ingredient_risk_penalty_tier": "1",
  "ingredient_origin": ["Vegetal"],
  "ingredient_name_localized": { "en": "Sunflower seed oil" },
  "ingredient_nb_produits": 227635,
  "sourceUrl": "https://incibeauty.com/ingredients/1447-helianthus-annuus-seed-oil",
  "scrapeDate": "2026-06-22",
  "scraped_at": "2026-06-22T18:00:00Z"
}
```

***

### ❓ FAQ

**How fast is it, and how many results will I get?**
The quick sample default (10 ingredient records, no proxy) finishes non-empty in well under five minutes. A full crawl scales with how much you ask for: the products catalog runs into hundreds of thousands of records (on the order of ~300,000), and the ingredient dictionary is 16,879 verified records. Use `maxItems` to cap either stream while you test.

**How fresh is the data?**
You get whatever INCI Beauty publishes at the moment you run the actor. Scores and per-ingredient risk classes are mutable, they change as INCI Beauty updates its knowledge base, so every row carries a `scraped_at` timestamp and products also carry the source's own `date_compo`. Re-run on a schedule to keep current; this is a refreshable feed, not a one-time dump.

**Where does the score come from? Is it your rating?**
No. The 0-20 score, the quality tier and the per-ingredient risk classes are **INCI Beauty's own published data**, captured faithfully. This actor computes no score and invents no methodology.

**Do I need a proxy?**
No. The default **No proxy** option works for the whole actor and is the fastest and cheapest. **Apify Proxy** is optional and routes through your own Apify account (you pay Apify for that traffic) if you want to rotate IPs.

**How does billing work?**
This actor uses **Pay Per Result**: you are billed per item written to the dataset. There is no separate start fee. If you are on a free plan, the run stops gracefully when it reaches your usage limit, it does not crash, so you always keep the results collected up to that point. Use `maxItems` to control exactly how many results (and how much spend) you want.

**Can I get just the products, or just the ingredients?**
Yes. Set **What to collect** to `products`, `ingredients`, or `both`. Both streams land in the same dataset, tagged by the `entity` field, and the dataset ships with separate Products and Ingredient reference views.

**Can I look up specific products by barcode?**
Yes. Put your EAN / GTIN barcodes in the **Product barcodes** input and each one is resolved straight to its scored product record, skipping the brand walk. Perfect for enriching an existing SKU list.

**Are reviews included?**
No. Reviews appear only on a bot-protected product page (no API path), so this actor does not collect them. It focuses on the 0-20 score, the full risk-rated composition, price, and the 16,879-ingredient dictionary.

**Is this legal?**
This actor collects data that INCI Beauty displays publicly to anonymous visitors. You are responsible for using the output in line with applicable law and INCI Beauty's terms. See the disclaimers below.

***

### ⚠️ Disclaimers

**Unofficial and not affiliated.** This actor is unofficial and is **not affiliated with, authorized by, or endorsed by INCI Beauty or Touslesprix.** It is not "the INCI Beauty API." No third-party trademarks or logos are used. All product, brand and service names belong to their respective owners.

**Public data.** This actor collects data that INCI Beauty displays publicly to anonymous visitors.

**Faithful reproduction.** The 0-20 INCI Beauty score, the per-ingredient risk classes and the compositions are **INCI Beauty's published data, reproduced faithfully.** This actor does not invent a scoring methodology and does not compute its own scores or ratings.

**Personal data.** By default this actor collects **no personal data** — it returns public cosmetic product and ingredient information. Product reviews (which would contain author pseudonyms and free text) are **not collected**, because their source page is bot-protected. Use any output in line with applicable law and INCI Beauty's terms.

**Responsible use.** Use this data in compliance with applicable law and INCI Beauty's terms. You are responsible for your use of the output, including any reuse, redistribution, or processing of personal data.

***

🔎 *Keywords: INCI Beauty scraper, cosmetic safety score, Note INCI Beauty, INCI ingredients, composition cosmétique, score de sécurité cosmétique, EAN GTIN cosmetics, ingredient risk class, base de données ingrédients INCI, CAS EINECS, cosmetic ingredient database.*

# Actor input Schema

## `entity` (type: `string`):

Choose which records to return. "Products" returns EAN-keyed cosmetic products with their 0-20 safety score and full ingredient composition. "Ingredients" returns the ingredient reference dictionary (INCI name, CAS, functions, risk class, origin). "Both" returns the two streams in one feed, joinable on the ingredient identifier; maxItems applies per stream. Tip: for "Both" with a small maxItems, also pass specific ingredientIds (or run "Ingredients" separately) to guarantee ingredient records, since an open catalog crawl returns products first.

## `startUrls` (type: `array`):

Optional list of specific INCI Beauty page URLs to collect, instead of crawling whole brands. Three URL shapes are supported: a product page (https://incibeauty.com/produit/<barcode>), an ingredient page (https://incibeauty.com/ingredients/<id>-<name>), or a brand page (https://incibeauty.com/brand/<brand-slug>). Each URL is resolved to the matching product, ingredient or brand. Leave empty to crawl by brand, barcode or ingredient filters below.

## `brands` (type: `array`):

Optional list of brand slugs to crawl (the slug is the last path segment of a brand page URL, for example nivea from https://incibeauty.com/brand/nivea). Only products from these brands are returned. Leave empty to walk the full A-Z brand catalog.

## `eans` (type: `array`):

Optional list of product barcodes (EAN / GTIN) to look up directly. Each barcode is resolved straight to its scored product record, skipping the brand walk. Ideal for enriching an existing barcode list. You can also paste a full product URL here (e.g. https://incibeauty.com/en/produit/3662361003174) and it is parsed to the barcode automatically. Leave empty to discover products by brand instead.

## `ingredientIds` (type: `array`):

Optional list of ingredient identifiers (the id-name segment of an ingredient page URL, for example 16498-azurite) to look up directly from the ingredient dictionary, skipping the A-Z ingredient walk. A full ingredient page URL is also accepted here and parsed to the id automatically. Leave empty to crawl ingredients by the selected scope.

## `maxItems` (type: `integer`):

Stop after collecting this many records per stream (products and ingredients are capped independently). Lower it for a quick test run, raise it for a full catalog crawl. The free daily-test default is 10.

## `includePrice` (type: `boolean`):

Collect the current retail price offers shown on each product page. ON by default and adds negligible cost. Turn it OFF if you do not need pricing. Applies to products only.

## `enrichIngredients` (type: `boolean`):

Augment each ingredient with its additional reference details (extra naming, classification and CAS data) from INCI Beauty's lookup service. OFF by default; turn it ON for the richest ingredient dictionary at the cost of one extra request per ingredient. Applies to ingredients only.

## `proxyType` (type: `string`):

No proxy is the fastest and cheapest option and works for the whole actor (the data runs over INCI Beauty's public pages without an IP wall). Apify Proxy routes through your own Apify account (you pay Apify for proxy traffic); use it only if you want to rotate IPs.

## `proxyConfiguration` (type: `object`):

Apify Proxy settings. Only used when Proxy is set to "Apify Proxy". Charged to your own Apify account.

## Actor input object example

```json
{
  "entity": "ingredients",
  "startUrls": [
    "https://incibeauty.com/produit/3600523307159",
    "https://incibeauty.com/ingredients/16498-azurite",
    "https://incibeauty.com/brand/nivea"
  ],
  "brands": [
    "nivea",
    "la-roche-posay"
  ],
  "eans": [
    "3662361003174",
    "3337875940412"
  ],
  "ingredientIds": [
    "16498-azurite",
    "3963-buxus-sempervirens-leaf-extract"
  ],
  "maxItems": 10,
  "includePrice": true,
  "enrichIngredients": false,
  "proxyType": "none",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped product + ingredient rows in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "entity": "ingredients",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("extractify-labs/incibeauty-inci-ingredients").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "entity": "ingredients",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("extractify-labs/incibeauty-inci-ingredients").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "entity": "ingredients",
  "maxItems": 10
}' |
apify call extractify-labs/incibeauty-inci-ingredients --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=extractify-labs/incibeauty-inci-ingredients",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "INCI Beauty Safety Score & Ingredients Scraper",
        "description": "Scrape INCI Beauty by barcode: the 0-20 safety score, full risk-rated INCI composition and ingredient reference dictionary. Score INCI, pay per result.",
        "version": "0.1",
        "x-build-id": "UJjlsdvMaUfRLb9mR"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/extractify-labs~incibeauty-inci-ingredients/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-extractify-labs-incibeauty-inci-ingredients",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/extractify-labs~incibeauty-inci-ingredients/runs": {
            "post": {
                "operationId": "runs-sync-extractify-labs-incibeauty-inci-ingredients",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/extractify-labs~incibeauty-inci-ingredients/run-sync": {
            "post": {
                "operationId": "run-sync-extractify-labs-incibeauty-inci-ingredients",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "entity": {
                        "title": "What to collect",
                        "enum": [
                            "products",
                            "ingredients",
                            "both"
                        ],
                        "type": "string",
                        "description": "Choose which records to return. \"Products\" returns EAN-keyed cosmetic products with their 0-20 safety score and full ingredient composition. \"Ingredients\" returns the ingredient reference dictionary (INCI name, CAS, functions, risk class, origin). \"Both\" returns the two streams in one feed, joinable on the ingredient identifier; maxItems applies per stream. Tip: for \"Both\" with a small maxItems, also pass specific ingredientIds (or run \"Ingredients\" separately) to guarantee ingredient records, since an open catalog crawl returns products first.",
                        "default": "products"
                    },
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "Optional list of specific INCI Beauty page URLs to collect, instead of crawling whole brands. Three URL shapes are supported: a product page (https://incibeauty.com/produit/<barcode>), an ingredient page (https://incibeauty.com/ingredients/<id>-<name>), or a brand page (https://incibeauty.com/brand/<brand-slug>). Each URL is resolved to the matching product, ingredient or brand. Leave empty to crawl by brand, barcode or ingredient filters below.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "brands": {
                        "title": "Brands",
                        "type": "array",
                        "description": "Optional list of brand slugs to crawl (the slug is the last path segment of a brand page URL, for example nivea from https://incibeauty.com/brand/nivea). Only products from these brands are returned. Leave empty to walk the full A-Z brand catalog.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "eans": {
                        "title": "Product barcodes (EAN / GTIN)",
                        "type": "array",
                        "description": "Optional list of product barcodes (EAN / GTIN) to look up directly. Each barcode is resolved straight to its scored product record, skipping the brand walk. Ideal for enriching an existing barcode list. You can also paste a full product URL here (e.g. https://incibeauty.com/en/produit/3662361003174) and it is parsed to the barcode automatically. Leave empty to discover products by brand instead.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "ingredientIds": {
                        "title": "Ingredient identifiers",
                        "type": "array",
                        "description": "Optional list of ingredient identifiers (the id-name segment of an ingredient page URL, for example 16498-azurite) to look up directly from the ingredient dictionary, skipping the A-Z ingredient walk. A full ingredient page URL is also accepted here and parsed to the id automatically. Leave empty to crawl ingredients by the selected scope.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxItems": {
                        "title": "Maximum results",
                        "minimum": 1,
                        "maximum": 1000000,
                        "type": "integer",
                        "description": "Stop after collecting this many records per stream (products and ingredients are capped independently). Lower it for a quick test run, raise it for a full catalog crawl. The free daily-test default is 10.",
                        "default": 1000
                    },
                    "includePrice": {
                        "title": "Include price offers",
                        "type": "boolean",
                        "description": "Collect the current retail price offers shown on each product page. ON by default and adds negligible cost. Turn it OFF if you do not need pricing. Applies to products only.",
                        "default": true
                    },
                    "enrichIngredients": {
                        "title": "Enrich ingredient records",
                        "type": "boolean",
                        "description": "Augment each ingredient with its additional reference details (extra naming, classification and CAS data) from INCI Beauty's lookup service. OFF by default; turn it ON for the richest ingredient dictionary at the cost of one extra request per ingredient. Applies to ingredients only.",
                        "default": false
                    },
                    "proxyType": {
                        "title": "Proxy",
                        "enum": [
                            "none",
                            "apify"
                        ],
                        "type": "string",
                        "description": "No proxy is the fastest and cheapest option and works for the whole actor (the data runs over INCI Beauty's public pages without an IP wall). Apify Proxy routes through your own Apify account (you pay Apify for proxy traffic); use it only if you want to rotate IPs.",
                        "default": "none"
                    },
                    "proxyConfiguration": {
                        "title": "Apify Proxy configuration",
                        "type": "object",
                        "description": "Apify Proxy settings. Only used when Proxy is set to \"Apify Proxy\". Charged to your own Apify account.",
                        "default": {
                            "useApifyProxy": true
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
