Products API
Live, filterable access to the BeautyFeeds product catalog, plus self-service credit-metered access for expanding what your organization can pull. See Plans & Features for what's included per plan.
Scope: this API covers product data only. Reviews are not available via API — they're purchased and downloaded manually from the Global Data page, not through any endpoint below.
The Playground sends real requests with your real API key and shows the real response — no code required. Sign in to open the Playground →
Authentication
Every request needs your organization's API key in a header:
Find your key on your dashboard.
Requests without a valid key, or from an inactive subscription, return 401 Unauthorized.
GET/api/v1/beauty_products
Returns products your organization currently has access to. Always live at query time — every request reads current data straight from the database, never a stale HTTP-level cache. The underlying catalog itself is refreshed on a monthly cycle by default (weekly or daily on request) — this endpoint just guarantees you always see the latest refresh, not an older cached response.
Query parameters
| Param | Type | Description |
|---|---|---|
brand_name | string | Filter by brand (partial match). Comma-separated for an OR match, e.g. Fenty,Sephora |
category | string | Filter by category (partial match). Comma-separated for OR |
site_name | string | Filter by retailer, e.g. sephora.com. Comma-separated for OR |
country | string | Filter by country code, e.g. us. Comma-separated for OR |
uniq_ids | string | Comma-separated exact product IDs — selects specific products instead of a whole brand/category |
updated_within | string | last_7_days, last_30_days, or last_1_year — only products re-crawled within that window. Plan-gated; not included on every plan. |
page | integer | Default 1 |
per_page | integer | Capped at your plan's per-page limit (default 50) |
Example response
A real response, from an org with a large assigned catalog and no active filter (so matched_in_catalog/expansion_notice are null — they only populate when a filter is given):
next_page_url / previous_page_url are null at either end of the result set, so you can branch on presence instead of computing bounds yourself. matched_in_catalog is the full-catalog count for the given filter (not just what you're entitled to) — null when no filter is given. expansion_notice is also null unless the catalog has more matches than you're currently assigned. If either shows a gap, use assign to unlock the rest.
On ingredient/INCI data: it's automatically parsed and normalized, not manually verified — ingredients_data_notice repeats this on every response as a standing reminder, not just here. Treat it as a strong starting point for formulation research, not a certified source.
Which fields you get back
data rows only include the fields your plan grants — see Plans & Features
for the exact list per plan. A Starter-tier response, for example, won't include ingredients or
description even if you don't filter for them; an Enterprise plan (or any plan with no field
restriction configured) gets every field. This applies to the row shape only — meta is always complete
regardless of plan.
List-shaped fields (ingredients, ingredients_formatted, variants,
additional_images, breadcrumbs, gtin_list, ean_list,
highlights, other_information) come back as real JSON arrays/objects, not
JSON-encoded strings — no need to JSON.parse them again on your end.
GET/api/v1/beauty_products/stats
Free preview — see what a filter would grant and what it would cost, without spending anything or changing your access.
Requires at least one filter (brand_name, category, site_name, country,
or precise uniq_ids — a comma-separated list to select specific products instead of a whole brand/category).
POST/api/v1/beauty_products/assign
Grants access to newly-matched products and charges credits for exactly what's new — products you already have access to are never re-charged. If your balance can't cover everything matched, you're granted as many as you can afford rather than the call failing outright.
Once assigned, those products are immediately included in GET /api/v1/beauty_products — no separate step needed.
notice is null on a clean assign, and explains itself otherwise — two independent reasons
it can be non-null: your credit balance couldn't cover everything matched (assigned as many as affordable), or a
small number of matched products couldn't be resolved due to a data sync issue and weren't charged for. Either
way, credits_used/newly_assigned always reflect what actually happened, not what was requested.
POST/api/v1/beauty_products/reset
Releases access to products matching the given filter (or specific uniq_ids). Credits are not refunded — see how credits work.
How credits & renewal work
- Credits are spent once per product, the moment it's newly granted — downloading it or querying it via the API afterward is free, no matter how many times, for the rest of the current billing cycle.
resetstops future access to a product but never refunds the credit already spent this cycle.- Access isn't permanent across billing cycles: the product catalog itself is re-crawled every cycle, so continued access to your currently-assigned products is re-affirmed and re-charged from your new cycle's credit pool at renewal. You'll see this cost ahead of time on your dashboard and by email before it happens.
- Credits are separate from your plan's request-rate limit — credits govern how many distinct products you can access; the request limit governs how many API calls you can make per cycle.
GET/api/v1/reviews/stats
Note the different host — this endpoint is served by app.beautyfeeds.io (this app), not api.beautyfeeds.io.
Reviews live in a separate datastore from products, so they're not part of the Products API above.
Analytics and pricing only — review count and estimated cost for products you already have Products API access to.
Does not return the review records themselves; those are still purchased and downloaded from
Global Data.
Same X-API-KEY auth and filters (brand_name, category, site_name,
country, uniq_ids) as the Products API — at least one is required.
Errors
| Status | Meaning |
|---|---|
401 | Missing/invalid X-API-KEY, or an inactive subscription |
403 | Your plan doesn't include API access, or doesn't include a filter you passed (e.g. updated_within on a plan that doesn't have it) |
422 | stats/assign/reset called with no filter |
429 | Plan request limit or rate limit exceeded |
404 | Unmatched path or method |
500 | Something went wrong on our end — every error response is a clean, minimal JSON object; internals are never exposed in the response body |
Rate limits
Two independent limits apply: your plan's request quota (per billing cycle, resets on renewal), and a general abuse backstop of 300 requests/minute per IP. Normal usage — including paging through a large result set quickly — stays well under the backstop; it exists to catch scripted abuse, not to slow down real usage.