Skip to main content

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.

Official MCP Registry PyPI

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)

Source distribution for perfume-api 2.4.0
File Size Uploaded Provenance
perfume_api-2.4.0.tar.gz 10.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for perfume-api 2.4.0
File Interpreter ABI Platform
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.

Release history Release notifications | RSS feed

This release

2.4.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page