The whole catalogue, one record at a time.
A metered HTTP API over the same database we sell as files. You pay per record returned, at the level of detail you ask for. No subscription and no seats: top up a balance, fetch what your application needs, cache it as long as you like.
Data and images live on our infrastructure
Every record and every image you receive is served from our own storage and CDN, not linked from a third-party site. Nothing in an answer depends on another website staying online.
Nothing ever disappears
Ids are ours and permanent. A fragrance withdrawn at its source stays in the API with its last known content and a status flag; we never delete or re-use an id, so your cache and your links keep working.
The whole record, three times a month
Pyramids, votes, translations, related items and photos come together in one call, refreshed with every release — about three a month; the free index tells you exactly which records changed.
Top up, get a key
Sign up → Dashboard → Data API. A free test key is there from the start; for real data top up from $100 in BTC, ETH, TRX, XMR or USDT and create a key. Units land on the balance when the payment confirms — minutes for most coins. Up to 5 paid keys per account; each is shown once.
# every call carries your key curl -H "Authorization: Bearer fk_…" \ https://fragdb.net/api/v1/account
Download the index
Gzipped JSONL, one line per record, each with its kind: every fragrance (id, name, brand, year) and the id of every brand, perfumer and note, with the change status against the previous release. The lists of related fragrances are refreshed with every release and do not count as a change. Free, 5 downloads a day; an unchanged index answers 304 and costs nothing. Search on your side, then ask us only for what moved.
curl -H "Authorization: Bearer fk_…" \ -H 'If-None-Match: "…"' \ https://fragdb.net/api/v1/index # 304 Not Modified — free
Fetch records
One id, or a batch of up to 100. The answer keeps your order and your length: unknown ids come back as null in place, listed under problems, and cost nothing. A batch you cannot pay for in full is refused whole with 402. An id repeated inside one batch is returned and charged each time — de-duplicate before you send.
curl -X POST -H "Authorization: Bearer fk_…" \
https://fragdb.net/api/v1/fragrances/batch \
-d '{"ids":["…","…"],"level":"full"}'
# X-Balance-Units · X-Daily-RemainingEvery account gets one free test key. It costs nothing, charges nothing and serves the 5 sample records below at every level — enough to write your parser against the real shape before you pay for anything.
curl -H "Authorization: Bearer <your test key>" \ "https://fragdb.net/api/v1/fragrances/f_9kwkhn9yqx?level=full"
| Response header | What it tells you |
|---|---|
| X-Data-Release | the release this record comes from |
| X-Balance-Units | units left after the call — paid keys only |
| X-Daily-Remaining | records left in today’s cap |
| X-Key-Kind | test — present only on a test key, which is never charged |
| X-Request-Id | quote it if you write to us about one call |
| Retry-After | seconds to wait after a 429 (daily cap: until 00:00 UTC) — nothing was charged |
| ETag / If-None-Match | on the index and on single records: a 304 (or a HEAD) costs nothing and is not counted |
{
"id": "f_9kwkhn9yqx",
"name": "Club de Nuit Intense Man",
"year": 2015,
"brand": {
"id": "fb_vsz4efb3je",
"name": "Armaf"
},
"notes": {
"top": [
{
"id": "fn_x2evj9v47c",
"name": "Lemon",
"images": {
"icon": "https://img.fragdb.net/n/2c9fb818077db6a1.webp"
},
"weight": 5,
"opacity": 1,
"name_i18n": {
"ar": "الليمون",
"cs": "Citrón",
"de": "Zitrone",
"…": "20 more languages"
}
},
{
"id": "fn_3qcvzywpkn",
"name": "Pineapple",
"images": {
"icon": "https://img.fragdb.net/n/565cf16d623edda7.webp"
},
"weight": 3.65,
"opacity": 0.95,
"name_i18n": {
"ar": "الأناناس",
"cs": "Ananas",
"de": "Ananas",
"…": "20 more languages"
}
},
"… 3 more"
],
"base": [
{
"id": "fn_4g85khaq94",
"name": "Musk",
"images": {
"icon": "https://img.fragdb.net/n/0225cc91ba01764a.webp"
},
"weight": 3.2,
"opacity": 0.88,
"name_i18n": {
"ar": "المسك",
"cs": "Pižmo",
"de": "Moschus",
"…": "20 more languages"
}
},
{
"id": "fn_kh4k462kwh",
"name": "Ambergris",
"images": {
"icon": "https://img.fragdb.net/n/fe118dc33561c12f.webp"
},
"weight": 3,
"opacity": 0.85,
"name_i18n": {
"ar": "الآمبرغريس",
"cs": "Ambra",
"de": "Ambergris",
"…": "20 more languages"
}
},
"… 2 more"
],
"notes": null,
"layout": [
"top",
"middle",
"… 1 more"
],
"middle": [
{
"id": "fn_gxeqvhejjf",
"name": "Birch",
"images": {
"icon": "https://img.fragdb.net/n/60593bce37738961.webp"
},
"weight": 4.08,
"opacity": 1,
"name_i18n": {
"ar": "أخشاب البتولا",
"cs": "Bříza",
"de": "Birke",
"…": "20 more languages"
}
},
{
"id": "fn_aeed6f0fwy",
"name": "Jasmine",
"images": {
"icon": "https://img.fragdb.net/n/c75c090b6e07693c.webp"
},
"weight": 2.6,
"opacity": 0.76,
"name_i18n": {
"ar": "الياسمين",
"cs": "Jasmín",
"de": "Jasmin",
"…": "20 more languages"
}
},
"… 1 more"
]
},
"votes": 28991,
"gender": "male",
"images": {
"photo": "https://img.fragdb.net/b/af72860f6afae7f1.webp",
"cutout": "https://img.fragdb.net/b/ff213593e65979db.webp",
"thumbnail": "https://img.fragdb.net/t/f2130761fd1e063d.webp"
},
"rating": 4.07,
"season": [
{
"key": "season_winter",
"pct": 58.44,
"votes": 9400,
"label_i18n": {
"ar": "الشتاء",
"cs": "zima",
"de": "Winter",
"…": "20 more languages"
}
},
{
"key": "season_spring",
"pct": 100,
"votes": 16200,
"label_i18n": {
"ar": "الربيع",
"cs": "jaro",
"de": "Frühling",
"…": "20 more languages"
}
},
"… 2 more"
],
"accords": [
{
"id": "fa_b7ecvxh7fp",
"name": "citrus",
"strength": 100,
"name_i18n": {
"ar": "الحمضيات",
"cs": "citrus",
"de": "zitrisch",
"…": "20 more languages"
}
},
{
"id": "fa_6av56j7vp4",
"name": "fruity",
"strength": 71,
"name_i18n": {
"ar": "فاكهي",
"cs": "ovocný",
"de": "fruchtig",
"…": "20 more languages"
}
},
"… 7 more"
],
"related": {
"also_like": [
"f_jncs8jkvbp",
"f_w31jepfj76",
"… 18 more"
],
"reminds_of": [
{
"id": "f_y7bhdp1wbc",
"no": 936,
"yes": 6100
},
{
"id": "f_chtz8h9nv9",
"no": 531,
"yes": 2500
},
"… 18 more"
],
"by_designer": [
"f_6p4nh7c8gh",
"f_bjwmne0vps",
"… 18 more"
],
"in_collection": [
"f_dhb98akc4b",
"f_zcv8xxfkkk",
"… 25 more"
]
},
"sillage": [
{
"key": "sillage_intimate",
"pct": 4,
"votes": 987,
"label_i18n": {
"ar": "ناعم",
"cs": "intimní",
"de": "sanft",
"…": "20 more languages"
}
},
{
"key": "sillage_moderate",
"pct": 32,
"votes": 7300,
"label_i18n": {
"ar": "متوسط",
"cs": "mírná",
"de": "moderat",
"…": "20 more languages"
}
},
"… 2 more"
],
"longevity": [
{
"key": "longevity_very_weak",
"pct": 2,
"votes": 351,
"label_i18n": {
"ar": "ضعيف جداً",
"cs": "velmi slabá",
"de": "enttäuschende Haltbarkeit",
"…": "20 more languages"
}
},
{
"key": "longevity_weak",
"pct": 3,
"votes": 755,
"label_i18n": {
"ar": "ضعيف",
"cs": "slabá",
"de": "schwach",
"…": "20 more languages"
}
},
"… 3 more"
],
"perfumers": [],
"pros_cons": {
"cons": [
{
"text": "Harsh and synthetic start in the opening",
"likes": 2800,
"dislikes": 373
},
{
"text": "Some people dislike the harshness or synthetic nature of the fragrance",
"likes": 2000,
"dislikes": 304
},
"… 6 more"
],
"pros": [
{
"text": "Affordable price",
"likes": 3100,
"dislikes": 59
},
{
"text": "Good performance and projection",
"likes": 2500,
"dislikes": 117
},
"… 6 more"
]
},
"collection": "Club de Nuit",
"gender_raw": "gender_for_men",
"description": "Club de Nuit Intense Man by Armaf is a Woody Spicy fragrance for men. Club de Nuit Intense…",
"price_value": [
{
"key": "price_way_overpriced",
"pct": 2,
"votes": 305,
"label_i18n": {
"ar": "مبالغ في سعره جداً",
"cs": "velmi předražené",
"de": "sehr überteuert",
"…": "20 more languages"
}
},
{
"key": "price_overpriced",
"pct": 2,
"votes": 359,
"label_i18n": {
"ar": "مبالغ في سعره",
"cs": "předražené",
"de": "etwas überteuert",
"…": "20 more languages"
}
},
"… 3 more"
],
"time_of_day": [
{
"key": "season_day",
"pct": 99,
"votes": 16000,
"label_i18n": {
"ar": "نهاري",
"cs": "den",
"de": "Tag",
"…": "20 more languages"
}
},
{
"key": "season_night",
"pct": 86.99,
"votes": 14100,
"label_i18n": {
"ar": "ليلي",
"cs": "noc",
"de": "Abend",
"…": "20 more languages"
}
}
],
"appreciation": [
{
"key": "like_love",
"pct": 45.69,
"votes": 13200,
"label_i18n": {
"ar": "أعشق",
"cs": "miluji",
"de": "liebe ich",
"…": "20 more languages"
}
},
{
"key": "like_like",
"pct": 31.8,
"votes": 9200,
"label_i18n": {
"ar": "احب",
"cs": "mám rád/a",
"de": "mag ich",
"…": "20 more languages"
}
},
"… 3 more"
],
"gender_votes": [
{
"key": "gvotes_female",
"pct": 1,
"votes": 116,
"label_i18n": {
"ar": "للنساء",
"cs": "ženský",
"de": "weiblich",
"…": "20 more languages"
}
},
{
"key": "gvotes_more_female",
"pct": 0,
"votes": 27,
"label_i18n": {
"ar": "يميل للعطور النسائية",
"cs": "více ženský",
"de": "weiblich unisex",
"…": "20 more languages"
}
},
"… 3 more"
],
"rating_scale": 5,
"reviews_count": 4400,
"gender_i18n": {
"ar": "للرجال",
"cs": "pro muže",
"de": "für Männer",
"…": "20 more languages"
},
"source_status": "present",
"last_seen": "2026-09-10",
"merged_into": null,
"change_status": "new",
"updated_in": "2026-09-10"
}$0.0025 / record
Identity and the numbers a list needs: id, brand, name, year, gender, rating with vote count, and a thumbnail.
- ✓id · brand {id, name} · name · year
- ✓gender · gender_raw
- ✓rating · rating_scale · votes
- ✓images.thumbnail
- ✗accords, notes, perfumers
$0.005 / record
Everything in list plus the photo, the collection, review count, main accords with their strength and the credited perfumers.
- ✓all of list
- ✓images.photo
- ✓accords [{id, name, strength}]
- ✓perfumers [{id, name}]
- ✓collection · reviews_count
$0.01 / record
The note pyramid as published, seven vote blocks, description, pros and cons, related fragrances, and every label translated inline.
- ✓all of basic
- ✓notes {layout, top, middle, base} + name_i18n
- ✓appreciation · longevity · sillage · season · time_of_day · gender_votes · price_value
- ✓description · pros_cons
- ✓related {by_designer, in_collection, also_like, reminds_of}
- ✓name_i18n · label_i18n · gender_i18n — every label translated
Top-ups are multiples of $50, from $100. Paid units do not expire while the account is open.
Minimum spend is $100 (40,000 units) per 180 days — $16.67 a month — counted from the first top-up. Whatever is left of that minimum is written off at the end of the period; spend more and nothing is. Granted units are never taken for it.
Bought a database file? You get 500 units for 30 days to try the API against the same release.
Paying. BTC, ETH, TRX, XMR or USDT; the invoice holds its rate for the time it shows and units appear when the payment confirms.
Re-requests are charged again. Cache what you fetch — the index tells you which records changed, so you only refetch those.
You pay for what a record holds. If a record has nothing beyond its basic level — the source published no notes, votes or related items for it — a full request costs the basic 2 units, not 4. The X-Charged-Units header on every response says what was taken. When the source fills such a record in, it appears in the index as changed and costs the full price from then on.
Minimum spend $100 per 180 days is covered.
The whole catalogue once at full level: 139,501 × 4 units = $1,395; at list level $349.
Inside a fragrance record a brand, a perfumer or a note arrives as {id, name} — enough to display, and stable enough to key your own tables on. Ask for the id itself when you want the rest of it.
| Resource | basic | full |
|---|---|---|
| GET /v1/brands/{id} | name, country, year founded, logo, counts | the same with the description translated |
| GET /v1/perfumers/{id} | name, country, role, counts, photo | the same with status and biography translated |
| GET /v1/notes/{id} | name, group, icon, counts | the same with the name and description translated |
Priced like fragrances: basic 2 units, full 4. A full request on a reference that has nothing localised is charged as basic — you asked for more and got what exists. Batches of up to 100 work the same way.
{ "error": { "code": "payment_required",
"message": "Not enough units for this batch.",
"required_units": 400,
"available_units": 120 } }| 400 | bad_request | malformed body, unknown level, more than 100 ids |
| 401 | unauthorized | no key, unknown key, revoked key, closed account — one answer for all |
| 402 | payment_required | not enough units — required_units · available_units; a test key outside its samples — reason "sample_ids_only"; the index on a test key — reason "paid_keys_only" |
| 403 | wrong_site | a key for the other storefront’s dataset |
| 404 | not_found | no record with this id was ever issued — costs nothing |
| 413 | payload_too_large | the request body is over 256 KB |
| 429 | rate_limited · daily_cap · index_cap | over 10/s, 300/min, 20/s per account or the daily cap — Retry-After says when, nothing is charged |
| 451 | unavailable_for_legal_reasons | the record is withheld; its id stays reserved |
| 503 | api_disabled | the API is switched off — balances untouched |
Every error has this one shape. Nothing is charged for a refusal — not for 400, not for 402, not for 429.
Requests above a limit are refused with 429 and a Retry-After header, and nothing is charged. A batch that would exceed your balance is refused whole with 402: you are never charged for a partial answer.
Images are served from our CDN for use inside your application. Hot-linking from pages that do not use our data, mirroring the image store, or re-serving images to third parties is outside fair use.
Call the API from your server, never from a browser: keys must not be embedded in client-side code, and cross-origin requests are not answered.
Records never disappear. An item withdrawn at the source stays in the API with its last known content, marked absent; ids are permanent and never re-used.
| Status in the index | Meaning | Share of records |
|---|---|---|
| new | The record appears for the first time in this release. | 100 % |
This is the first release on the API, so every record is new; from the next one the table shows the real split. Shares are from the release on the API right now. Releases come about three times a month: one full crawl, and deltas in between that add new records and update existing ones.
User reviews, comments and news texts — those stay in the downloadable files. User-uploaded photos and avatars. Source page URLs and source identifiers: every id and every image URL you see is ours.
Text fields are served as plain text; the note pyramid, vote blocks and relations are structured JSON with stable keys, documented in the OpenAPI spec.
Every response carries X-Data-Release. Fields are only ever added within v1; a breaking change ships as v2 with the previous version kept for at least 180 days after notice.
Do I need a subscription?+
No. You prepay units and spend them per record. The only recurring rule is the minimum spend — $100 per 180 days, $16.67 a month — which is written off from the paid balance if unused.
Can I search by name?+
Search runs on your side against the free index: id, name, brand, year and change status. Fetch whole records only for the hits you need.
What if a fragrance is removed at the source?+
It stays in the API with its last content and source_status "absent". We never delete an id and never re-use one.
Is there a sandbox?+
/account, /release and the index cost nothing, and an id that was never issued returns null without a charge. Buyers of a database file get 500 trial units.