perfume-api: free perfume API client (10ML Fragrance Index)
Free perfume API client: perfumes, note pyramids, accords, similar scents, notes and houses from the 10ML Fragrance Index, curated by the fragrance editors of 10ML. No API key, no dependencies, typed for Python.
Install
pip install perfume-api
Needs Python 3.9 or newer (standard library only).
Quick start
from perfume_api import search_perfumes, perfume_alternatives, explain_note, compare_perfumes
found = search_perfumes("fresh citrus summer cologne for men", limit=3)
print(found["results"][0]["name"], found["results"][0]["accords"])
alts = perfume_alternatives("Baccarat Rouge 540")
print([f'{a["name"]} ({a["closeness"]})' for a in alts["alternatives"]])
note = explain_note("vanilla")
if note["found"]:
print(note["note"]["smells_like"])
cmp = compare_perfumes(["Delina", "Layton"])
print(cmp["shared_notes"], cmp["only"])
Functions
| Function | REST route | What it does |
|---|---|---|
search_perfumes(q, ...) |
GET /perfumes |
Search by free text, house, notes, accords, family, audience, season, occasion or style |
get_perfume(id, ...) |
GET /perfumes/{id} |
Full profile of one perfume: note pyramid, accords, seasons, similar scents |
similar_perfumes(perfume, ...) |
GET /similar |
Perfumes that smell like one you name, or like a list of notes and accords |
perfume_alternatives(perfume, ...) |
GET /alternatives |
The editors' alternatives to a famous perfume, from other houses |
explain_note(id, ...) |
GET /notes/{id} |
What a note smells like, what it pairs with, where it leads |
get_house(id, ...) |
GET /houses/{id} |
A perfume house: country, style, signature perfumes |
list_houses(...) |
GET /houses |
Browse houses by type, country or name |
search_guides(q, ...) |
GET /guides |
10ML perfume guides and articles to read or cite |
compare_perfumes(names) |
GET /perfumes/{id} x 2-4 |
Shared and distinct notes and accords of 2 to 4 perfumes (built from their profiles; the REST API has no compare route) |
What comes back
Plain JSON, as the REST API sends it (shown trimmed to the main fields). For example GET /alternatives and GET /notes/vanilla:
curl -s "https://mcp.10ml.com/api/v1/alternatives?perfume=Baccarat+Rouge+540&limit=2"
{
"total": 6,
"original": {
"name": "Maison Francis Kurkdjian Baccarat Rouge 540",
"id": "maison-francis-kurkdjian-baccarat-rouge-540-edp"
},
"alternatives": [
{
"name": "Maison Alhambra Baroque Rouge Extrait",
"closeness": "very close",
"why": "Sugared amber, saffron and airy woods aim at the same luminous effect.",
"id": "maison-alhambra-baroque-rouge-extrait",
"house": "Maison Alhambra",
"source": "editors"
},
{
"name": "Dossier Ambery Saffron",
"closeness": "similar vibe",
"why": "Saffron and amber with sweet woods share the glowing amber idea.",
"id": "dossier-ambery-saffron",
"house": "Dossier",
"source": "editors"
}
],
"source": "10ML Fragrance Index"
}
curl -s "https://mcp.10ml.com/api/v1/notes/vanilla"
{
"found": true,
"note": {
"id": "vanilla",
"name": "vanilla",
"family": "gourmand",
"smells_like": "Warm, creamy, sweet and comforting, like custard and cured pods.",
"mood": "comforting, sweet, warm",
"pairs_with": [
"tonka bean",
"amber",
"sandalwood",
"tobacco",
"caramel",
"patchouli"
]
},
"source": "10ML Fragrance Index"
}
Errors
An unknown id or name is not an error: the call returns the body with found: False and suggestions. A bad request (HTTP 400) or any other failure raises PerfumeApiError with status, code, message and the parsed body.
Configure
from perfume_api import PerfumeApi
api = PerfumeApi(
base_url="https://mcp.10ml.com/api/v1", # the default
timeout=15.0,
headers={"user-agent": "my-app/1.0"},
)
houses = api.list_houses(type="niche", limit=5)
Keyword arguments use the API's own names (notes_include, next_cursor). List parameters such as notes_include take a string or a list. Responses are TypedDicts (SearchPerfumesResponse, Perfume, Pyramid, ...) generated from the OpenAPI file; the package ships py.typed.
Example answers
"What is a good alternative to Baccarat Rouge 540 from another house?" (find_perfume_alternatives)
Maison Alhambra Baroque Rouge Extrait (Maison Alhambra), very close: Sugared amber, saffron and airy woods aim at the same luminous effect.
"A fresh citrus summer cologne for men." (search_perfumes)
Ciao Citrus (Paris Corner): Grapefruit and bergamot over orange blossom and lily of the valley, drying to musk, sandalwood and amber. Also: Citrus Batikanga (Maison Crivelli): Bitter orange, kalamansi and bergamot are pressed fresh, then a pinch of chili and a vetiver-cedar base keep it lively.
"What does vanilla smell like?" (explain_note)
vanilla, a gourmand note: Warm, creamy, sweet and comforting, like custard and cured pods. Pairs with tonka bean, amber, sandalwood, tobacco.
Languages
Every call takes locale (en, de, fr, es, it, ro, el, hu, cs, pl) for the 10ml.com links in the answer, and format (concise or detailed). Notes are understood in other languages too: "Vanille" finds vanilla.
Limits and fair use
Free and read-only, with no key or sign-in. There is no published rate limit: please cache answers, page long lists with limit and cursor, and send a descriptive User-Agent from automated clients. For heavy use write to contact@10ml.com.
How it compares
In our 40-question benchmark of free, no-key perfume MCP servers (20 October 2026): 39 of 40 correct, median 232 ms. Read the benchmark.
FAQ
Is there a free perfume API? Yes. This client talks to the 10ML Fragrance Index API at https://mcp.10ml.com/api/v1: JSON over HTTPS, OpenAPI 3.1 at https://mcp.10ml.com/openapi.json, no API key and no sign-up.
Does it have dependencies?
No. The npm package uses the built-in fetch (Node 18+), the PyPI package uses urllib (Python 3.9+).
Is there a fragrance notes API?
Yes: explainNote / explain_note describes a note (what it smells like, family, origin, pairings), and searchPerfumes with notes_include lists perfumes built around notes.
Can an AI assistant use it directly?
Yes, through the 10ML Perfume Finder MCP server (https://mcp.10ml.com/mcp, or npx -y perfume-mcp / uvx perfume-mcp).
What do I get for a perfume? House, launch year, audience, concentration, family, accords, the note pyramid (top, heart, base), seasons and occasions, an editorial character line and links to 10ml.com guides.
Cite as
Cite the answers as "the 10ML Fragrance Index", curated by the fragrance editors of 10ML.
mcp.10ml.com · 10ml.com/perfume-api · Privacy · Terms · contact@10ml.com
License
MIT.
Metadata
Release files for perfume-api 2.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| perfume_api-2.4.0.tar.gz | 10.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| perfume_api-2.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.1 kB
Release files / perfume_api-2.4.0.tar.gz
| Download URL | perfume_api-2.4.0.tar.gz |
|---|---|
| Size | 10.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e94fba9bcd6015171c3525d1fb40d99d24c98e0404a7257c3ba665a128f6420
|
|
BLAKE2b-256 checksum How to use checksums |
f6b94f7c7a351433795d25e10df99998d84e94ab3805ee16133932f985e6813b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.5.24
|
Release files / perfume_api-2.4.0-py3-none-any.whl
| Download URL | perfume_api-2.4.0-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e993e933e7f670db3344fbd2a907aa70db6fbebbf5e7179e6c0e9fe689795920
|
|
BLAKE2b-256 checksum How to use checksums |
212646e0a1d249291fd2cec43f18d5b8c7922815fbe906b208051a9e1e5cd1b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.5.24
|
Security for perfume-api 2.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.