Ulta Beauty API endpoint
Browses 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.
/ulta/categorycategory is required. It accepts a category path (shop/makeup/eyes/mascara) or a full category URL copied from Ulta's own site navigation -- only the path is used. Use GET /ulta/categories to discover valid category paths without already knowing one. filter is optional and narrows results using Ulta's own guided-navigation facet-code shape (e.g. BENEFIT--WATERPROOF, or a comma-joined combination such as BENEFIT--WATERPROOF,BRAND--MAYBELLINE) -- discover valid codes for a category from that same category's own response facets field, whose value is ready to use directly as this parameter; a ?gn=... query string on a pasted category URL is honored as filter's default when filter is not set separately. page is optional, 1-based, defaults to 1.
Notable response behavior:
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.category returns 404.list_price/sale_price are Ulta's own display-formatted price strings, the same as /ulta/search.| Field | Filled in, of 71 checked | Values in the sample |
|---|---|---|
| product_id | 71 | |
| sku_id | 71 | |
| brand_name | 71 | “The Ordinary” and 29 other values |
| product_name | 71 | “Glycolic Acid 7% Exfoliating and Brightening Daily Toner” and 70 other values |
| list_price | 71 | “$9.00 - $13.50” and 60 other values |
| rating | 71 | 3.6 to 4.9, median 4.6 |
| review_count | 71 | 8 to 44,647, median 1,198 |
| variant_label | 53 | 2 sizes (22), 3 sizes (20), 4 sizes (7) and 2 more |
| image_url | 71 | media.ultainc.com |
| url | 71 | ulta.com |
| sponsored | 13 | true in 13 of 13 |
| promo_text | 25 | Free Gift with purchase (16), Bundle & Save! (9) |
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| category | string | Yes | Ulta category path or URL | shop/makeup/eyes/mascara | |
| filter | string | No | Guided-navigation facet code(s), comma-joined for multiple | BENEFIT--WATERPROOF | |
| page | integer | No | 1 | Result page, 1-based, defaults to 1 | 1 |
| x-api-key (header) | string | Yes | API key required |
curl -X GET "https://api.crawlora.net/api/v1/ulta/category?category=shop%2Fmakeup%2Feyes%2Fmascara&page=1" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"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"
}
}Request schema
No body schema
Response schema
#/definitions/ulta.categoryResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| code | integer | No | 200 | |||
| data | ulta.CategoryResponse | No | ||||
| data.category | string | No | ||||
| data.count | integer | No | ||||
| data.facets | array | No | Facets is the category's own guided-navigation refinement options, read from the same response this package already fetches -- no separate upstream call needed. Confirmed live 2026-08-11 present and non-empty (46 options on an unfiltered category, 17 on a filtered one) on every case tried (unfiltered, filtered, and page 2), so this field is populated whenever the upstream returns it, nil otherwise. Ulta's own guidedNavigation does not carry a live per-option result count (unlike this repo's Nike/Zappos facet shapes), so CategoryFacet has no result-count field. | |||
| data.facets[].label | string | No | ||||
| data.facets[].selected | boolean | No | ||||
| data.facets[].value | string | No | ||||
| data.fetched_at | string | No | ||||
| data.filter | string | No | ||||
| data.has_more | boolean | No | ||||
| data.page | integer | No | ||||
| data.page_size | integer | No | ||||
| data.products | array | No | ProductSummary record, 15 fields: brand_name, discount, image_url, is_limited_stock, list_price, product_id, product_name, promo_text, rating, review_count, sale_price, sku_id and 3 more. Described under Search Ulta Beauty products API. | |||
| data.result_count | integer | No | ||||
| data.source_url | string | No | ||||
| msg | string | No | OK |
| Status | Description | Schema |
|---|---|---|
| 400 | Bad Request | #/definitions/app.Response |
| 404 | Not Found | #/definitions/app.Response |
| 429 | Too Many Requests | #/definitions/app.Response |
| 503 | Service Unavailable | #/definitions/app.Response |
Use environment variables for secrets and keep Crawlora API keys server-side.
curl -X GET "https://api.crawlora.net/api/v1/ulta/category?category=shop%2Fmakeup%2Feyes%2Fmascara&page=1" \
-H "x-api-key: $CRAWLORA_API_KEY"