# Ulta Scraper

Ulta web scraper to crawl product information including price and sale price, color, and images.

## Actor Statistics

- Total users: 5
- Monthly users: 1
- Runs succeeded: 100.00%
- Categories: Developer tools, E-commerce, Automation

## Pricing

$30.00/month + usage

## Input Schema

- **startUrls** (type: array): Start URLs of Ulta to start the actor. Category page, product page urls are all supported.
- **maxRequestsPerCrawl** (type: integer): Maximum number of requests that can be made by this crawler, 0 to ignore.
- **proxy** (type: object): Select proxies to be used by your crawler.
- **maxConcurrency** (type: integer): Actor running max concurrency, which helps you to not getting blocked.

## API example

## Ulta Scraper

- **OpenAPI Version:** `3.1.1`
- **API Version:** `0.0`

Ulta web scraper to crawl product information including price and sale price, color, and images.

### Servers

- **URL:** `https://api.apify.com/v2`

### Operations

#### Executes an Actor, waits for its completion, and returns Actor's dataset items in response.

- **Method:** `POST`
- **Path:** `/acts/autofacts~ulta-scraper/run-sync-get-dataset-items`
- **Tags:** Run Actor

##### Request Body

###### Content-Type: application/json

- **`startUrls` (required)**

  `array` — Start URLs of Ulta to start the actor. Category page, product page urls are all supported.

  **Items:**

  - **`url` (required)**

    `string`, format: `uri`

- **`maxConcurrency`**

  `integer`, default: `5` — Actor running max concurrency, which helps you to not getting blocked.

- **`maxRequestsPerCrawl`**

  `integer`, default: `10` — Maximum number of requests that can be made by this crawler, 0 to ignore.

- **`proxy`**

  `object` — Select proxies to be used by your crawler.

**Example:**

```
{
  "startUrls": [
    {
      "url": ""
    }
  ],
  "maxRequestsPerCrawl": 10,
  "proxy": {},
  "maxConcurrency": 5
}
```

##### Responses

###### Status: 200 OK

#### Executes an Actor and returns information about the initiated run in response.

- **Method:** `POST`
- **Path:** `/acts/autofacts~ulta-scraper/runs`
- **Tags:** Run Actor

##### Request Body

###### Content-Type: application/json

- **`startUrls` (required)**

  `array` — Start URLs of Ulta to start the actor. Category page, product page urls are all supported.

  **Items:**

  - **`url` (required)**

    `string`, format: `uri`

- **`maxConcurrency`**

  `integer`, default: `5` — Actor running max concurrency, which helps you to not getting blocked.

- **`maxRequestsPerCrawl`**

  `integer`, default: `10` — Maximum number of requests that can be made by this crawler, 0 to ignore.

- **`proxy`**

  `object` — Select proxies to be used by your crawler.

**Example:**

```
{
  "startUrls": [
    {
      "url": ""
    }
  ],
  "maxRequestsPerCrawl": 10,
  "proxy": {},
  "maxConcurrency": 5
}
```

##### Responses

###### Status: 200 OK

\####### Content-Type: application/json

- **`data`**

  `object`

  - **`actId`**

    `string`

  - **`buildId`**

    `string`

  - **`buildNumber`**

    `string`

  - **`containerUrl`**

    `string`

  - **`defaultDatasetId`**

    `string`

  - **`defaultKeyValueStoreId`**

    `string`

  - **`defaultRequestQueueId`**

    `string`

  - **`finishedAt`**

    `string`, format: `date-time`

  - **`id`**

    `string`

  - **`meta`**

    `object`

    - **`origin`**

      `string`

    - **`userAgent`**

      `string`

  - **`options`**

    `object`

    - **`build`**

      `string`

    - **`diskMbytes`**

      `integer`

    - **`memoryMbytes`**

      `integer`

    - **`timeoutSecs`**

      `integer`

  - **`startedAt`**

    `string`, format: `date-time`

  - **`stats`**

    `object`

    - **`computeUnits`**

      `integer`

    - **`inputBodyLen`**

      `integer`

    - **`rebootCount`**

      `integer`

    - **`restartCount`**

      `integer`

    - **`resurrectCount`**

      `integer`

  - **`status`**

    `string`

  - **`usage`**

    `object`

    - **`ACTOR_COMPUTE_UNITS`**

      `integer`

    - **`DATA_TRANSFER_EXTERNAL_GBYTES`**

      `integer`

    - **`DATA_TRANSFER_INTERNAL_GBYTES`**

      `integer`

    - **`DATASET_READS`**

      `integer`

    - **`DATASET_WRITES`**

      `integer`

    - **`KEY_VALUE_STORE_LISTS`**

      `integer`

    - **`KEY_VALUE_STORE_READS`**

      `integer`

    - **`KEY_VALUE_STORE_WRITES`**

      `integer`

    - **`PROXY_RESIDENTIAL_TRANSFER_GBYTES`**

      `integer`

    - **`PROXY_SERPS`**

      `integer`

    - **`REQUEST_QUEUE_READS`**

      `integer`

    - **`REQUEST_QUEUE_WRITES`**

      `integer`

  - **`usageTotalUsd`**

    `number`

  - **`usageUsd`**

    `object`

    - **`ACTOR_COMPUTE_UNITS`**

      `integer`

    - **`DATA_TRANSFER_EXTERNAL_GBYTES`**

      `integer`

    - **`DATA_TRANSFER_INTERNAL_GBYTES`**

      `integer`

    - **`DATASET_READS`**

      `integer`

    - **`DATASET_WRITES`**

      `integer`

    - **`KEY_VALUE_STORE_LISTS`**

      `integer`

    - **`KEY_VALUE_STORE_READS`**

      `integer`

    - **`KEY_VALUE_STORE_WRITES`**

      `number`

    - **`PROXY_RESIDENTIAL_TRANSFER_GBYTES`**

      `integer`

    - **`PROXY_SERPS`**

      `integer`

    - **`REQUEST_QUEUE_READS`**

      `integer`

    - **`REQUEST_QUEUE_WRITES`**

      `integer`

  - **`userId`**

    `string`

**Example:**

```
{
  "data": {
    "id": "",
    "actId": "",
    "userId": "",
    "startedAt": "2025-01-08T00:00:00.000Z",
    "finishedAt": "2025-01-08T00:00:00.000Z",
    "status": "READY",
    "meta": {
      "origin": "API",
      "userAgent": ""
    },
    "stats": {
      "inputBodyLen": 2000,
      "rebootCount": 0,
      "restartCount": 0,
      "resurrectCount": 0,
      "computeUnits": 0
    },
    "options": {
      "build": "latest",
      "timeoutSecs": 300,
      "memoryMbytes": 1024,
      "diskMbytes": 2048
    },
    "buildId": "",
    "defaultKeyValueStoreId": "",
    "defaultDatasetId": "",
    "defaultRequestQueueId": "",
    "buildNumber": "1.0.0",
    "containerUrl": "",
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0,
      "DATASET_READS": 0,
      "DATASET_WRITES": 0,
      "KEY_VALUE_STORE_READS": 0,
      "KEY_VALUE_STORE_WRITES": 1,
      "KEY_VALUE_STORE_LISTS": 0,
      "REQUEST_QUEUE_READS": 0,
      "REQUEST_QUEUE_WRITES": 0,
      "DATA_TRANSFER_INTERNAL_GBYTES": 0,
      "DATA_TRANSFER_EXTERNAL_GBYTES": 0,
      "PROXY_RESIDENTIAL_TRANSFER_GBYTES": 0,
      "PROXY_SERPS": 0
    },
    "usageTotalUsd": 0.00005,
    "usageUsd": {
      "ACTOR_COMPUTE_UNITS": 0,
      "DATASET_READS": 0,
      "DATASET_WRITES": 0,
      "KEY_VALUE_STORE_READS": 0,
      "KEY_VALUE_STORE_WRITES": 0.00005,
      "KEY_VALUE_STORE_LISTS": 0,
      "REQUEST_QUEUE_READS": 0,
      "REQUEST_QUEUE_WRITES": 0,
      "DATA_TRANSFER_INTERNAL_GBYTES": 0,
      "DATA_TRANSFER_EXTERNAL_GBYTES": 0,
      "PROXY_RESIDENTIAL_TRANSFER_GBYTES": 0,
      "PROXY_SERPS": 0
    }
  }
}
```

#### Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.

- **Method:** `POST`
- **Path:** `/acts/autofacts~ulta-scraper/run-sync`
- **Tags:** Run Actor

##### Request Body

###### Content-Type: application/json

- **`startUrls` (required)**

  `array` — Start URLs of Ulta to start the actor. Category page, product page urls are all supported.

  **Items:**

  - **`url` (required)**

    `string`, format: `uri`

- **`maxConcurrency`**

  `integer`, default: `5` — Actor running max concurrency, which helps you to not getting blocked.

- **`maxRequestsPerCrawl`**

  `integer`, default: `10` — Maximum number of requests that can be made by this crawler, 0 to ignore.

- **`proxy`**

  `object` — Select proxies to be used by your crawler.

**Example:**

```
{
  "startUrls": [
    {
      "url": ""
    }
  ],
  "maxRequestsPerCrawl": 10,
  "proxy": {},
  "maxConcurrency": 5
}
```

##### Responses

###### Status: 200 OK

### Schemas

#### inputSchema

- **Type:**`object`

* **`startUrls` (required)**

  `array` — Start URLs of Ulta to start the actor. Category page, product page urls are all supported.

  **Items:**

  - **`url` (required)**

    `string`, format: `uri`

* **`maxConcurrency`**

  `integer`, default: `5` — Actor running max concurrency, which helps you to not getting blocked.

* **`maxRequestsPerCrawl`**

  `integer`, default: `10` — Maximum number of requests that can be made by this crawler, 0 to ignore.

* **`proxy`**

  `object` — Select proxies to be used by your crawler.

**Example:**

```
{
  "startUrls": [
    {
      "url": ""
    }
  ],
  "maxRequestsPerCrawl": 10,
  "proxy": {},
  "maxConcurrency": 5
}
```

#### runsResponseSchema

- **Type:**`object`

* **`data`**

  `object`

  - **`actId`**

    `string`

  - **`buildId`**

    `string`

  - **`buildNumber`**

    `string`

  - **`containerUrl`**

    `string`

  - **`defaultDatasetId`**

    `string`

  - **`defaultKeyValueStoreId`**

    `string`

  - **`defaultRequestQueueId`**

    `string`

  - **`finishedAt`**

    `string`, format: `date-time`

  - **`id`**

    `string`

  - **`meta`**

    `object`

    - **`origin`**

      `string`

    - **`userAgent`**

      `string`

  - **`options`**

    `object`

    - **`build`**

      `string`

    - **`diskMbytes`**

      `integer`

    - **`memoryMbytes`**

      `integer`

    - **`timeoutSecs`**

      `integer`

  - **`startedAt`**

    `string`, format: `date-time`

  - **`stats`**

    `object`

    - **`computeUnits`**

      `integer`

    - **`inputBodyLen`**

      `integer`

    - **`rebootCount`**

      `integer`

    - **`restartCount`**

      `integer`

    - **`resurrectCount`**

      `integer`

  - **`status`**

    `string`

  - **`usage`**

    `object`

    - **`ACTOR_COMPUTE_UNITS`**

      `integer`

    - **`DATA_TRANSFER_EXTERNAL_GBYTES`**

      `integer`

    - **`DATA_TRANSFER_INTERNAL_GBYTES`**

      `integer`

    - **`DATASET_READS`**

      `integer`

    - **`DATASET_WRITES`**

      `integer`

    - **`KEY_VALUE_STORE_LISTS`**

      `integer`

    - **`KEY_VALUE_STORE_READS`**

      `integer`

    - **`KEY_VALUE_STORE_WRITES`**

      `integer`

    - **`PROXY_RESIDENTIAL_TRANSFER_GBYTES`**

      `integer`

    - **`PROXY_SERPS`**

      `integer`

    - **`REQUEST_QUEUE_READS`**

      `integer`

    - **`REQUEST_QUEUE_WRITES`**

      `integer`

  - **`usageTotalUsd`**

    `number`

  - **`usageUsd`**

    `object`

    - **`ACTOR_COMPUTE_UNITS`**

      `integer`

    - **`DATA_TRANSFER_EXTERNAL_GBYTES`**

      `integer`

    - **`DATA_TRANSFER_INTERNAL_GBYTES`**

      `integer`

    - **`DATASET_READS`**

      `integer`

    - **`DATASET_WRITES`**

      `integer`

    - **`KEY_VALUE_STORE_LISTS`**

      `integer`

    - **`KEY_VALUE_STORE_READS`**

      `integer`

    - **`KEY_VALUE_STORE_WRITES`**

      `number`

    - **`PROXY_RESIDENTIAL_TRANSFER_GBYTES`**

      `integer`

    - **`PROXY_SERPS`**

      `integer`

    - **`REQUEST_QUEUE_READS`**

      `integer`

    - **`REQUEST_QUEUE_WRITES`**

      `integer`

  - **`userId`**

    `string`

**Example:**

```
{
  "data": {
    "id": "",
    "actId": "",
    "userId": "",
    "startedAt": "2025-01-08T00:00:00.000Z",
    "finishedAt": "2025-01-08T00:00:00.000Z",
    "status": "READY",
    "meta": {
      "origin": "API",
      "userAgent": ""
    },
    "stats": {
      "inputBodyLen": 2000,
      "rebootCount": 0,
      "restartCount": 0,
      "resurrectCount": 0,
      "computeUnits": 0
    },
    "options": {
      "build": "latest",
      "timeoutSecs": 300,
      "memoryMbytes": 1024,
      "diskMbytes": 2048
    },
    "buildId": "",
    "defaultKeyValueStoreId": "",
    "defaultDatasetId": "",
    "defaultRequestQueueId": "",
    "buildNumber": "1.0.0",
    "containerUrl": "",
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0,
      "DATASET_READS": 0,
      "DATASET_WRITES": 0,
      "KEY_VALUE_STORE_READS": 0,
      "KEY_VALUE_STORE_WRITES": 1,
      "KEY_VALUE_STORE_LISTS": 0,
      "REQUEST_QUEUE_READS": 0,
      "REQUEST_QUEUE_WRITES": 0,
      "DATA_TRANSFER_INTERNAL_GBYTES": 0,
      "DATA_TRANSFER_EXTERNAL_GBYTES": 0,
      "PROXY_RESIDENTIAL_TRANSFER_GBYTES": 0,
      "PROXY_SERPS": 0
    },
    "usageTotalUsd": 0.00005,
    "usageUsd": {
      "ACTOR_COMPUTE_UNITS": 0,
      "DATASET_READS": 0,
      "DATASET_WRITES": 0,
      "KEY_VALUE_STORE_READS": 0,
      "KEY_VALUE_STORE_WRITES": 0.00005,
      "KEY_VALUE_STORE_LISTS": 0,
      "REQUEST_QUEUE_READS": 0,
      "REQUEST_QUEUE_WRITES": 0,
      "DATA_TRANSFER_INTERNAL_GBYTES": 0,
      "DATA_TRANSFER_EXTERNAL_GBYTES": 0,
      "PROXY_RESIDENTIAL_TRANSFER_GBYTES": 0,
      "PROXY_SERPS": 0
    }
  }
}
```

## 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 = {
    "startUrls": [
        {
            "url": "https://www.ulta.com/shop/new-beauty-products?category=hair"
        }
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    },
    "maxConcurrency": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("autofacts/ulta-scraper").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 = {
    "startUrls": [{ "url": "https://www.ulta.com/shop/new-beauty-products?category=hair" }],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
    "maxConcurrency": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("autofacts/ulta-scraper").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 '{
  "startUrls": [
    {
      "url": "https://www.ulta.com/shop/new-beauty-products?category=hair"
    }
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  },
  "maxConcurrency": 5
}' |
apify call autofacts/ulta-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?actors=autofacts/ulta-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## Ulta Scraper

**Ulta Scraper** provide a way to crawl product details of site Ulta.

### Features

1. Support fetch products of category;
2. Support fetch product detail with prices,descriptions,images and sku info;

> The price values are multiplied by 100 to avoiding floating point calculations.

### Input Parameters

The input of this scraper shoule be JSON formated. Fields are:

| Field               | Type    | Description                                                                                          |
|:--------------------|:--------|:-----------------------------------------------------------------------------------------------------|
| startUrls           | Array   | Start URLs of Ulta site to start the scraper. Category page, product page urls are all supported.    |
| proxy               | Object  | Select proxies to be used by your crawler.                                                           |
| maxConcurrency      | Object  | Actor running max concurrency, which helps you to not getting blocked.                               |
| maxRequestsPerCrawl | Integer | Maximum number of requests that can be made by this crawler, 0 to ignore.                            |

### Supported Pages

Supported of fetch data from below pages:

| Page                | Example                                                                                            |
|:--------------------|:---------------------------------------------------------------------------------------------------|
| Product Detail Page | https://www.ulta.com/p/studio-fix-powder-plus-foundation-makeup-xlsImpprod15921242?sku=2510752     |
| Category Page       | https://www.ulta.com/shop/makeup/face                                                              |
| Brand Page          | https://www.ulta.com/brand/ulta-beauty-collection                                                  |
| Sale Page           | https://www.ulta.com/promotion/sale                                                                |

### Data storage

Ulta scraper stores the product data to default data set in JSON format.

```json
{
	"source": {
		"id": "pimprod2048637",
		"crawlUrl": "https://www.ulta.com/p/radiance-conscious-beauty-kit-pimprod2048637?sku=2630776",
		"canonicalUrl": "https://www.ulta.com/p/radiance-conscious-beauty-kit-pimprod2048637?sku=2630776",
		"retailer": "ulta",
		"currency": "USD"
	},
	"brand": "Beauty Finds by ULTA Beauty",
	"title": "Radiance Conscious Beauty Kit",
	"description": {
		"short_desc": "Unwrap Radiance with Our Beauty Kit! Celebrate the season with the ultimate gift of clean beauty. Our beauty kit features luxurious, eco-friendly products designed to nourish and glow.",
		"full_desc": "##### Includes\n\n- Bubble, Skincare Day Dream Tone + Texture Serum Vitamin C + Niacinamide (0.17 oz)\n- Dermalogica, Special Cleansing Gel (0.5 oz)\n- House Of Lashes, Boudoir Lite Full False Lashes (1 pair)\n- Lolavie, Glossing Detangler (0.85 oz)\n- Nemat, Vanilla Musk Roll-On Fragrance Oil (0.17 oz)\n- ‘Ôrǝbella, NIGHTCAP Parfum (0.05 oz)\n- Peace Out, Acne Dots (4 dot patches)\n- Pür Beauty, Fully Charged Mascara Powered By Magnetic Technology Mini (0.14 oz)\n- St. Tropez, St. Tropez Self Tan Purity Bronzing Water Face Mist (0.47 oz)\n- Sunday Riley, Good Genes All-In-One Lactic Acid Treatment (0.17 oz)\n- Thayers, Thayers PH Cleanser (3.0 oz)\n- Viviscal, Thickening Conditioner (1.7 oz)\n- Viviscal, Thickening Shampoo (1.7 oz)\n\n"
	},
	"categories": [
		"Gifts",
		"By Price",
		"$50 and Under"
	],
	"options": [],
	"variants": [
		{
			"id": "2630776",
			"sku": "2630776",
			"options": [],
			"price": {
				"list": 4000,
				"listFormatted": "$40.00",
				"sale": 2400,
				"stockStatus": "InStock"
			},
			"medias": [
				{
					"id": "2630776",
					"type": "Image",
					"url": "https://media.ulta.com/i/ulta/2630776",
					"alt": "Beauty Finds by ULTA Beauty Radiance Conscious Beauty Kit #1",
					"width": 2000,
					"height": 2000
				},
				{
					"id": "2630776_alt01",
					"type": "Image",
					"url": "https://media.ulta.com/i/ulta/2630776_alt01",
					"alt": "Beauty Finds by ULTA Beauty Radiance Conscious Beauty Kit #2",
					"width": 2000,
					"height": 2000
				}
			]
		}
	],
	"stats": {
		"rating": 4.4,
		"reviewCount": 7
	},
	"price": {
		"sale": 2400,
		"list": 4000,
		"listFormatted": "$40.00",
		"stockStatus": "InStock"
	}
}
```
