Ulta Beauty API
Ulta Beauty API returns live Ulta Beauty data as clean JSON for ulta beauty The primary endpoint, search, returns product results including product id, sku id, name, brand and image.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
Developers reach for it when they need to validate, enrich, classify and normalize operational data without maintaining one-off scraping code or separate API contracts. If you were about to build or fix a Ulta Beauty scraper, this API is the maintained alternative — it returns the same data as clean JSON, with the proxies, rotation and anti-bot handling already solved. This page covers the live example, request shape, response shape and available actions: search, price, product_detail. Every request uses the same ReefAPI envelope, one API key and one shared credit pool, so it fits alongside the rest of your data stack.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/ulta/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"category": "/shop/makeup/face/foundation",
"max_results": 20
}
}{
"ok": true,
"meta": {
"api": "ulta",
"endpoint": "search",
"mode": "live",
"latency_ms": 1653.1,
"record_count": 20,
"bytes": 905763,
"cache_hit": false,
"completeness_pct": 100,
"stop_reason": "limit_reached",
"method": "ulta_graphql_page",
"search_mode": "category",
"source": {
"route": "/dxl/graphql Page → ProductListingResults",
"upstream_calls": 1,
"upstream_pages": 1,
"page_size": 64
},
"pagination": {
"page": 1,
"per_page": 64,
"total_results": 225,
"returned": 20,
"has_more": true,
"next_page": 2
},
"filters": {
"applied": [],
"ignored": [],
"notes": null
},
"notes": null
},
"data": {
"products": [
{
"product_id": "pimprod2057355",
"sku_id": "2651970",
"url": "https://www.ulta.com/p/double-wear-stay-in-place-longwear-matte-foundation-pimprod2057355?sku=2651970",
"name": "[redacted-name]",
"brand": "Estée Lauder",
"image": "https://media.ultainc.com/i/ulta/2651970",
"image_alt": null,
"list_price": 52,
"list_price_max": 52,
"sale_price": null,
"sale_price_max": null,
"price": 52,
"currency": "USD",
"on_sale": false,
"discount_amount": null,
"rating": 4.3,
"review_count": 9926,
"variant_label": "57 colors",
"badge": null,
"badge_tags": [],
"promo_text": "Bundle & Save!",
"additional_offers_text": null,
"sponsored": false,
"limited_stock": false
},
{
"product_id": "pimprod2051406",
"sku_id": "2635749",
"url": "https://www.ulta.com/p/studio-fix-powder-plus-foundation-pimprod2051406?sku=2635749",
"name": "[redacted-name]",
"brand": "MAC",
"image": "https://media.ultainc.com/i/ulta/2635749",
"image_alt": null,
"list_price": 39,
"list_price_max": 39,
"sale_price": 31.2,
"sale_price_max": 31.2,
"price": 31.2,
"currency": "USD",
"on_sale": true,
"discount_amount": 7.8,
"rating": 3.8,
"review_count": 3453,
"variant_label": "71 colors",
"badge": null,
"badge_tags": [
"Sale"
],
"promo_text": null,
"additional_offers_text": null,
"sponsored": false,
"limited_stock": false
},
{
"product_id": "xlsImpprod5770257",
"sku_id": "2264064",
"url": "https://www.ulta.com/p/cc-cream-with-spf-50-xlsImpprod5770257?sku=2264064",
"name": "[redacted-name]",
"brand": "IT Cosmetics",
"image": "https://media.ultainc.com/i/ulta/2264064",
"image_alt": null,
"list_price": 39,
"list_price_max": 39,
"sale_price": null,
"sale_price_max": null,
"price": 39,
"currency": "USD",
"on_sale": false,
"discount_amount": null,
"rating": 4.3,
"review_count": 22005,
"variant_label": "30 colors",
"badge": null,
"badge_tags": [],
"promo_text": null,
"additional_offers_text": null,
"sponsored": false,
"limited_stock": false
}
],
"category": {
"path": "/shop/makeup/face/foundation",
"url": "https://www.ulta.com/shop/makeup/face/foundation",
"title": "Foundation",
"result_count": 225,
"page": 1,
"page_size": 64
},
"suggestions": null,
"facets": {
"sort": [
{
"value": "best_sellers",
"label": "Best Sellers"
},
{
"value": "price_asc",
"label": "Price (low to high)"
},
{
"value": "price_desc",
"label": "Price (high to low)"
}
],
"filters": [
{
"group_id": "FORM",
"title": "Form",
"select": "MULTI",
"param": "form",
"values": [
"[trimmed-depth]",
"[trimmed-depth]",
"[trimmed-depth]"
]
},
{
"group_id": "FINISH",
"title": "Finish",
"select": "MULTI",
"param": "finish",
"values": [
"[trimmed-depth]",
"[trimmed-depth]",
"[trimmed-depth]"
]
},
{
"group_id": "COVERAGE",
"title": "Coverage",
"select": "MULTI",
"param": "coverage",
"values": [
"[trimmed-depth]",
"[trimmed-depth]",
"[trimmed-depth]"
]
}
]
}
}
}What the Ulta Beauty API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Find products on ulta.com (US). Browse a `category` — `/shop/makeup/face/foundation`, `/shop/hair/shampoo-conditioner/shampoo`, `/shop/skin-care/moisturizers` — for the precise, fully pageable result set with Ulta's own total, or give a free-text `query` ('mascara', 'vitamin c serum', 'dyson airwrap') to let Ulta's own type-ahead decide which category answers it. Every product comes back with Ulta's own product id and sku, the name, brand, image, list and sale price (a range when the shades differ), the star rating and review count, the shade/size count and Ulta's badges and promo text. Narrow with `brand`, `min_rating`, `sort` and any facet Ulta publishes for that category; the live facet vocabulary comes back WITH counts in every category response, so a filter value is never a guess. | Ops teams call search to find products on ulta.com (US). | query, category, page, max_results, sort, ... |
| price | Re-check ONE variant's price and stock, cheaply. Give it the product_id and the sku_id you already hold and it returns the current price, the regular price, whether that variant is in stock, and nothing else — no description, images, ingredients, variants or reviews. Built for refreshing a catalogue you already have rather than discovering products: it reads the same small module Ulta's own shade swatch fires, so it costs a fraction of a full product_detail. Note that sku_id is REQUIRED, because Ulta stocks and prices every shade and size separately — a product can be on sale and buyable in one size while another is sold out, so there is no single product-level answer. | Developer tools call price to get re-check ONE variant's price and stock, cheaply. | product_id, sku_id, url |
| product_detail | Get one Ulta Beauty product in full, by Ulta's product id or by its ulta.com URL — brand, name, the marketing copy in both plain text and its original Markdown, the full ingredient list, how-to-use, size, category path, list and sale price, availability, star rating and question count, the complete image AND video gallery with dimensions, Ulta's merchandising highlights and active promotions, one row per shade/size variant carrying that variant's OWN sku, price, sale price, stock flag, swatch and product images, and the customer reviews inline with the rating histogram, the recommended ratio, the most helpful positive and negative review, and the reviewer-reported attribute rollups. One call, everything the product page shows. | Validation workflows call product_detail to get one Ulta Beauty product in full, by Ulta's product id or by its ulta.com URL. | product_id, sku_id, url, reviews, variant_prices |
Call search from your stack
curl -X POST https://api.reefapi.com/ulta/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"category":"/shop/makeup/face/foundation","max_results":20}'import requests
r = requests.post(
"https://api.reefapi.com/ulta/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"category": "/shop/makeup/face/foundation",
"max_results": 20
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/ulta/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"category": "/shop/makeup/face/foundation",
"max_results": 20
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.ulta.search with {"category":"/shop/makeup/face/foundation","max_results":20}.Who uses this API and why
- Ops teams use Ulta Beauty to find products on ulta.com (US).
- Developer tools use Ulta Beauty to get re-check ONE variant's price and stock, cheaply.
- Validation workflows use Ulta Beauty to get one Ulta Beauty product in full, by Ulta's product id or by its ulta.com URL.
Questions developers ask before integrating
What is the Ulta Beauty API?
Ulta Beauty API is a ReefAPI endpoint group for ulta beauty It returns live JSON through POST requests under /ulta/v1.
Is the Ulta Beauty API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Ulta Beauty calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Ulta Beauty login or account?
No login to Ulta Beauty is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Ulta Beauty data?
The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Ulta Beauty API use?
Ulta Beauty actions currently cost 2 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Ulta Beauty from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call ulta actions with the same key, credit pool and JSON envelope used by normal REST requests.
Is the Ulta Beauty API a Ulta Beauty scraper?
It is the managed alternative to a DIY Ulta Beauty scraper. Instead of building and maintaining your own scraper — proxies, headless browsers, captcha and constant breakage — you call one ReefAPI endpoint and get the same ulta beauty back as clean JSON.
Why does my Ulta Beauty scraper keep getting blocked?
Most Ulta Beauty scrapers break on anti-bot defenses, rate limits and IP bans that need rotating residential proxies and browser fingerprinting to clear. ReefAPI handles all of that for you — no proxies, no captchas, no maintenance — and returns live JSON. Blocked or failed calls are free.