Beauty retail pricing and availability monitoring
Use Ulta Beauty endpoints to turn beauty retail pricing and availability monitoring into repeatable API requests with documented inputs and JSON responses.
Turn Ulta Beauty's own storefront data into structured beauty-retail data — keyword product search with real pagination, and per-product detail with pricing, rating, review count, images, and every purchasable color/shade variant, all as normalized JSON. Credential-free.
Search Ulta Beauty's product catalog and get full product detail — pricing, ratings, reviews, and every color/shade variant — as structured JSON.
Endpoint families
6
Documented params
25
Examples
8
Live catalog snapshot
Active endpoints
8
Methods
GET
Required params
14
Schema refs
8
{
"platform": "Ulta Beauty",
"endpoint": "ulta-search",
"method": "GET",
"path": "/ulta/search",
"auth": "apiKey"
}Use cases
Search Ulta Beauty's product catalog and get full product detail — pricing, ratings, reviews, and every color/shade variant — as structured JSON.
Use Ulta Beauty endpoints to turn beauty retail pricing and availability monitoring into repeatable API requests with documented inputs and JSON responses.
Use Ulta Beauty endpoints to turn product catalog research into repeatable API requests with documented inputs and JSON responses.
Use Ulta Beauty endpoints to turn shade/color-variant tracking into repeatable API requests with documented inputs and JSON responses.
Managed execution
Crawlora's Ulta Beauty API is designed around Ulta Beauty's own data surface, not a generic fetch endpoint — combining endpoint-specific request logic, managed infrastructure, parsing, normalization, billing, and Playground-tested examples.
Request behavior for Ulta Beauty endpoints is tuned to the platform's own response shapes and edge cases.
Supported Ulta Beauty endpoints can use managed proxy routing to improve reliability and reduce infrastructure work.
Dynamic Ulta Beauty pages can be routed through managed browser instances where JavaScript rendering is required.
Challenge pages and unusable Ulta Beauty responses are detected and surfaced clearly, not silently returned as empty data.
Ulta Beauty results are returned as documented JSON instead of raw HTML.
Test the same Ulta Beauty route from Docs and Playground before production integration.
Coverage map
These cards are generated from the active endpoint catalog, so the landing page reflects the same API surface used by Docs and Playground.
/ulta/product/{productId}
/ulta/categories
/ulta/category
/ulta/search
/ulta/stores
/ulta/suggest
Endpoint catalog
/ulta/searchSearches Ulta Beauty's product catalog by keyword, with real page-based pagination. Returns normalized products with brand, pricing, rating, and review count. An unrecognized/nonsense keyword returns a genuine empty result rather than a fallback set. Requesting a page beyond the available results returns a normal, empty result rather than an error.
Response notes
- Pagination is real and page-based, 64 results per page. `has_more` is the upstream's own genuine "more pages available" marker: `true` on every page with more results after it, `false` on the real last page and on any page beyond it. - Requesting a `page` beyond the available results returns a normal `200` with an empty `products` array and `has_more: false`, not an error. - An unrecognized/nonsense `query` returns a genuine empty result (`count: 0`, `result_count: 0`) rather than a fallback set. - `list_price`/`sale_price` are Ulta's own display-formatted price strings (e.g. `"$12.99 - $13.99"`), since a search card's list price is often a range across a product's own color/shade variants -- use `/ulta/product/{productId}` for one variant's single resolved price. Example response: ```json {"code":200,"msg":"OK","data":{"query":"mascara","page":1,"page_size":64,"count":3,"result_count":382,"has_more":true,"products":[{"product_id":"pimprod2020260","sku_id":"2574523","brand_name":"Maybelline","product_name":"Lash Sensational Sky High Mascara","list_price":"$12.99 - $13.99","rating":4.5,"review_count":11861,"variant_label":"9 colors","image_url":"https://media.ulta.com/i/ulta/2574523","url":"https://www.ulta.com/p/lash-sensational-sky-high-mascara-pimprod2020260?sku=2574523"}],"source_url":"https://www.ulta.com/dxl/graphql?...","fetched_at":"2026-08-10T10:00:00Z"}} ```
/ulta/product/{productId}Returns one Ulta Beauty product's full detail: name, brand, description, category, pricing, rating, review count, images, and every purchasable color/shade variant. productId is taken from a search result's product_id field or a product page's URL (e.g. pimprod2020260). sku is optional and selects a specific color/shade variant; an omitted or invalid sku still resolves the base product using its own default variant. An unrecognized productId returns 404.
Response notes
- `category` lists the product's category breadcrumb (e.g. `["Makeup", "Eyes", "Mascara"]`). - `variants` lists every purchasable color/shade of the base product, each with its own `product_id`/`sku_id`/`name`. - `images` is the featured variant's image gallery as absolute URLs. - `sale_price` is only present when the product is currently on sale. - An unrecognized `productId` returns `404`. A malformed `productId` returns `400` before any upstream request is made. Example response: ```json {"code":200,"msg":"OK","data":{"product_id":"pimprod2020260","sku_id":"2574523","brand_name":"Maybelline","product_name":"Lash Sensational Sky High Mascara","description":"#### Benefits\n\n- Sky high lash impact from every angle\n- Soft, lightweight lashes with no flaking or smudging","category":["Makeup","Eyes","Mascara"],"currency_code":"USD","list_price":"$13.99","unavailable":false,"rating":4.5,"review_count":11861,"images":["https://media.ulta.com/i/ulta/2574523","https://media.ulta.com/i/ulta/2574523_sm"],"variants":[{"product_id":"pimprod2020260","sku_id":"2574523","name":"Blackest Black"},{"product_id":"pimprod2020260","sku_id":"2617940","name":"Burgundy Haze"}],"url":"https://www.ulta.com/p/product-pimprod2020260?sku=2574523","source_url":"https://www.ulta.com/dxl/graphql?...","fetched_at":"2026-08-10T10:00:00Z"}} ```
/ulta/categoriesLists Ulta Beauty's own storefront category navigation: department, group, name, and a URL usable directly as GET /ulta/category's own category parameter. Closes the discovery gap of not already knowing a category path. department, if set, filters to just that department's entries. group is empty for a department's own top-level link or a group's own heading link, and set to that group's name for the leaf categories nested under it. The exact same real category can legitimately appear more than once under a different department/group when the site's own navigation cross-lists it.
Response notes
- `group` is empty for two kinds of entries: a department's own top-level link (e.g. "All Makeup & Nails"), and a group's own heading link (e.g. "Face"), which is itself a real, browsable category one level above its own leaf items. Every leaf item nested under a group carries that group's name as `group` (e.g. "Foundation" carries group `"Face"`). - `department`/`group` reflect the site's own navigation grouping, not a slug derived from the returned URL's path. Some categories are cross-listed by Ulta's own navigation under more than one department -- for example "Tinted Moisturizer" and "Makeup Remover" appear under the Makeup & Nails department's "Face" group, but their URLs resolve under `shop/skin-care/...`, and the same two categories also appear separately under the Skin Care department's own "Moisturizers"/"Cleansers" groups. This is expected: the same real category can legitimately appear more than once with a different `department`/`group` pairing. - A small number of groups have a heading link but no nested leaf items at all (for example Men's Care's "Cologne") and surface as a single group-empty entry, the same shape as a department's own top-level link. Example response: ```json {"code":200,"msg":"OK","data":{"source_url":"https://www.ulta.com/","fetched_at":"2026-08-11T12:00:00Z","categories":[{"department":"makeup-nails","name":"All Makeup & Nails","url":"https://www.ulta.com/shop/makeup"},{"department":"makeup-nails","name":"Eyes","url":"https://www.ulta.com/shop/makeup/eyes"},{"department":"makeup-nails","group":"Eyes","name":"Mascara","url":"https://www.ulta.com/shop/makeup/eyes/mascara"},{"department":"makeup-nails","group":"Face","name":"Tinted Moisturizer","url":"https://www.ulta.com/shop/skin-care/moisturizers/tinted-moisturizer"}]}} ```
/ulta/categoryBrowses an Ulta Beauty category page's product grid, with real page-based pagination and the category's own guided-navigation refinement options. category accepts a category path or full URL copied from Ulta's own site navigation (e.g. shop/makeup/eyes/mascara). filter narrows results using Ulta's own guided-navigation facet-code shape (e.g. BENEFIT--WATERPROOF, or a comma-joined combination of codes) -- discover valid codes for a category from that category's own response facets field, whose value is ready to use directly as this parameter. An unrecognized category returns 404.
Response notes
- Pagination is real and page-based, 64 results per page. `has_more` is the upstream's own genuine "more pages available" marker, the same as `/ulta/search`. - `facets` lists the category's own guided-navigation refinement options (label, a ready-to-use `value`, and whether it is already applied) -- present whenever the upstream returns them. An already-selected facet carries an empty `value`, since the caller already knows they set that filter themselves. - An unrecognized `category` returns `404`. - `list_price`/`sale_price` are Ulta's own display-formatted price strings, the same as `/ulta/search`. Example response: ```json {"code":200,"msg":"OK","data":{"category":"shop/skin-care/cleansers","page":1,"page_size":64,"count":2,"result_count":1161,"has_more":true,"products":[{"product_id":"xlsImpprod5140065","sku_id":"2254420","brand_name":"CeraVe","product_name":"Foaming Facial Cleanser","list_price":"$5.99 - $18.99","rating":4.6,"review_count":19250,"variant_label":"4 sizes","image_url":"https://media.ultainc.com/i/ulta/2254420","url":"https://www.ulta.com/p/foaming-facial-cleanser-xlsImpprod5140065?sku=2254420"}],"facets":[{"label":"Combination Skin Type","value":"SKIN_TYPE--COMBINATION"},{"label":"On Sale","value":"SPECIAL_OFFER--SALE"}],"source_url":"https://www.ulta.com/dxl/graphql?...","fetched_at":"2026-08-11T10:00:00Z"}} ```
/ulta/product/questionsReturns one page of an Ulta Beauty product's normalized customer questions, each with every answer it received. product_id is taken from a search result's product_id field or a product page's URL. A product with zero questions, or a well-formed but unrecognized product_id, returns a normal, empty result rather than an error.
Response notes
- Each question's `answers` lists every answer it received, in the order the source returns them. `is_expert` marks an answer from a brand representative rather than a fellow customer. - A product with zero questions, or a well-formed but unrecognized `product_id`, returns a normal, empty result (`count: 0`, `total_questions: 0`) rather than an error -- the review source does not distinguish an unknown product id from a real, question-free product. - A malformed `product_id` returns `400` before any upstream request is made. Example response: ```json {"code":200,"msg":"OK","data":{"product_id":"pimprod2020260","page":1,"page_size":10,"total_pages":3,"total_questions":62,"count":10,"source_url":"https://display.powerreviews.com/...","fetched_at":"2026-08-11T10:00:00Z","questions":[{"id":"11248885","text":"Is this a tubing mascara?","author":"Ace","location":"fl","created_at":"2026-01-15T18:32:15Z","answer_count":1,"answers":[{"id":"571075350","text":"Hi Ulta Customer. Thank you for reaching out...","author":"Maybelline","is_expert":true,"helpful_votes":16,"created_at":"2026-02-02T08:56:52Z"}]}]}} ```
/ulta/product/reviewsReturns one page of an Ulta Beauty product's normalized customer reviews, plus the retailer's own site-wide rating summary (rating count, average rating, recommended ratio, rating histogram) for the product. product_id is taken from a search result's product_id field or a product page's URL. A product with zero reviews, or a well-formed but unrecognized product_id, returns a normal, empty result rather than an error.
Response notes
- `rating_count`, `average_rating`, `recommended_ratio`, and `rating_histogram` are the product's site-wide aggregate rating summary, not just this page. `rating_histogram` is the count of ratings per star, ascending: index 0 is 1-star, index 4 is 5-star. - A product with zero reviews, or a well-formed but unrecognized `product_id`, returns a normal, empty result (`count: 0`, `total_reviews: 0`) rather than an error -- the review source does not distinguish an unknown product id from a real, reviewless product. - A malformed `product_id` returns `400` before any upstream request is made. Example response: ```json {"code":200,"msg":"OK","data":{"product_id":"pimprod2020260","page":1,"page_size":10,"total_pages":203,"total_reviews":2028,"rating_count":11861,"average_rating":4.45,"recommended_ratio":0.66,"rating_histogram":[428,380,825,2021,8207],"count":10,"source_url":"https://display.powerreviews.com/...","fetched_at":"2026-08-11T10:00:00Z","reviews":[{"id":"585043647","rating":5,"headline":"Best mascara","body":"I have fairly long, thick eyelashes...","author":"ollie","location":"NYC","verified_purchase":false,"created_at":"2026-08-01T12:30:29Z"}]}} ```
/ulta/storesReturns Ulta Beauty physical retail store locations near a point: name, phone, full address, hours, services, and coordinates. Either search, or both lat and lng, is required. search is a free-text zip code, city, or address that is first resolved to coordinates; if it does not resolve to any location, a well-formed empty result is returned rather than an error. lat and lng, when given directly, skip that resolution step. radius_meters is optional (1000 to 50000, defaults to 25000). A location with no stores within the radius returns a well-formed empty result rather than an error.
Response notes
- Each store includes its name, phone number, full street address, hours of operation, in-store services, and coordinates. - `lat`/`lng` in the response always reflect the coordinates actually searched -- the caller's own values when given directly, or the coordinates resolved from `search` otherwise. - A `search` value that does not resolve to any location returns a well-formed empty result (`count: 0`, `stores: []`), not an error. - A location with no stores within the radius returns a well-formed empty result, not an error. - `hours` maps a three-letter day abbreviation (`Mon`..`Sun`) to that day's opening range, e.g. `"09:00-21:00"`. Example response: ```json { "code": 200, "msg": "OK", "data": { "search": "60601", "lat": 41.885769, "lng": -87.624599, "radius_meters": 25000, "count": 1, "stores": [ { "id": 2806899, "slug": "chicago-il-1357", "name": "Ulta Beauty", "phone": "+1 312-527-9045", "address": "430 North Michigan Avenue", "city": "Chicago", "state": "IL", "postal_code": "60611", "country": "US", "latitude": 41.8903696, "longitude": -87.6247778, "is_permanently_closed": false, "services": ["Hair Services", "Brow Services", "Makeup Services"], "hours": { "Mon": "09:00-21:00", "Sun": "10:00-20:00" }, "url": "https://www.ulta.com/stores/chicago-il-1357" } ] } } ```
/ulta/suggestReturns Ulta Beauty's own search-suggestion (typeahead) result for a partial search term: suggested search terms, each with its own top product matches, plus a featured top result matching what a real user sees at the top of the dropdown. A partial term with no matches returns a normal, empty result rather than an error.
Response notes
- `suggestions` lists each suggested search term with its own `label`, `url` (a category/search page), and `top_results` (up to a handful of matching products). - `first_top_result` mirrors what a real user sees featured at the top of the dropdown -- it is one of `suggestions`' own top results, not a separate search. - A partial term with no matches returns a normal, empty result (`count: 0`, empty `suggestions`) rather than an error. Example response: ```json {"code":200,"msg":"OK","data":{"query":"masc","count":10,"source_url":"https://www.ulta.com/dxl/graphql?...","fetched_at":"2026-08-11T10:00:00Z","suggestions":[{"label":"Mascara","url":"https://www.ulta.com/shop/makeup/eyes/mascara","top_results":[{"brand_name":"Tarte","product_name":"Tartelette Tubing Mascara","image_url":"https://media.ultainc.com/i/ulta/2596644","url":"https://www.ulta.com/p/tartelette-tubing-mascara-pimprod2033171?sku=2596644"}]}],"first_top_result":[{"brand_name":"Tarte","product_name":"Tartelette Tubing Mascara","image_url":"https://media.ultainc.com/i/ulta/2596644","url":"https://www.ulta.com/p/tartelette-tubing-mascara-pimprod2033171?sku=2596644"}]}} ```
Related APIs
Marketplaces & Retail
Collect marketplace product signals from Amazon without building brittle storefront scrapers.
Marketplaces & Retail
Build resale, pricing, and marketplace workflows from structured eBay data.
Marketplaces & Retail
Turn public Shop.app product and merchant pages into structured JSON for e-commerce product intelligence, price research, shop monitoring, and marketplace discovery workflows.
How to scrape Ulta Beauty
Crawlora's Ulta Beauty endpoints return search and product detail as normalized JSON with one API key — no Ulta account required.
Send a free-text keyword to /ulta/search for paginated, normalized products with brand, pricing, rating, and review count.
Pass a product_id (from a search result, or a product page URL like pimprod2020260) to /ulta/product/{productId} for full detail — pricing, rating, review count, images, and every color/shade variant. Add an optional sku to select a specific variant; an omitted or invalid sku still resolves the base product using its own default variant.
FAQ
Yes — /ulta/search returns a genuine empty result for an unrecognized or nonsense keyword, and a normal empty result (not an error) when you request a page beyond the available results.
Yes — pass an optional sku to /ulta/product/{productId} to select a specific color/shade variant. An omitted or invalid sku still resolves the base product using its own default variant. An unrecognized productId returns 404.