# momox / medimops API — live data from Europe's biggest re-commerce dealer in second-hand media. momox buys used books, CDs, DVDs, Blu-rays, games and software in bulk, grades every copy by hand and sells it from its own warehouses: medimops.de (DE) and momox-shop.fr (FR), 9.4 million live listings on the German store alone. Search or browse the catalogue, or look a title up straight by its ISBN-13 or EAN-13 barcode, and get the complete condition ladder for that one product — a price AND a unit stock count for each of momox's five grades (New, Like New, Very Good, Good, Acceptable) — plus EAN, ISBN-10/13, author, translator, publisher, binding, page count, publication date, FSK rating and the store's own review score. One seller, one price per grade, no marketplace noise. No login, no API key.

> Search or browse one momox media storefront and get one row per product with its FULL condition ladder already attached — every grade momox has in stock with that grade's own price and unit count, so you do not need a second call to see whether the cheap copy is the acceptable one. Also the barcode lookup: hand `query` an ISBN-13 or a 13-digit EAN and the source returns exactly that one product (measured 1 row for three different barcodes across books, films and games). Every filter offered here was compared against an unfiltered control in the SAME run on two different queries and really changes the result; the two filters the source accepts and then ignores (`language`, and `category_id` as a query parameter) are not exposed at all. The response carries the source's own UNCAPPED facet counts next to its capped total, the exact-match flag, and a warning for anything the source dropped.
> ReefAPI engine `momox` · 3 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/momox/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed calls are free except verified SHEIN NOT_FOUND on product/detail and price (4 credits).
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/momox/v1/search — 3 credits
Search or browse one momox media storefront and get one row per product with its FULL condition ladder already attached — every grade momox has in stock with that grade's own price and unit count, so you do not need a second call to see whether the cheap copy is the acceptable one. Also the barcode lookup: hand `query` an ISBN-13 or a 13-digit EAN and the source returns exactly that one product (measured 1 row for three different barcodes across books, films and games). Every filter offered here was compared against an unfiltered control in the SAME run on two different queries and really changes the result; the two filters the source accepts and then ignores (`language`, and `category_id` as a query parameter) are not exposed at all. The response carries the source's own UNCAPPED facet counts next to its capped total, the exact-match flag, and a warning for anything the source dropped.

**Parameters:**
- `market` (enum, required, default "de") — Which momox media storefront to read. Prices, stock and the whole category id space are PER MARKET and really differ: on 2026-10-08 ISBN 9783551551672 read EUR 4.51 with 3,819 copies on `de` and EUR 4.49 with 3,818 copies on `fr` in the same minute. Both markets quote EUR, so nothing is converted here — a row always carries the market it was read from. `de` also publishes an FSK age-rating facet that `fr` does not have. [one of: de, fr]
- `query` (string, optional) — Free-text search over the whole catalogue — title, author, series, publisher. ALSO the barcode lookup: an ISBN-13 or a full 13-digit EAN returns exactly one row (measured: 9783551551672 -> 1 row, 4010324028952 -> 1 row, 0045496420468 -> 1 row). An ISBN-10 or an EAN with its leading zero stripped returns NOTHING at the source, so this engine converts an ISBN-10 to its ISBN-13 before sending and tells you in `meta.query_normalised`. ⚠️ The source DROPS the keyword on every category shelf except the whole catalogue, so `query` and `category` cannot be combined — asking for both is rejected rather than answered with a category dump. Narrow a keyword with `format`, `condition` or `price_min`/`price_max` instead.
- `category` (string, optional) — Browse one category shelf instead of searching. The id is the source's own numeric category id and it is PER MARKET — DE Bücher is 186606 while FR Livres is 8, so an id from the wrong market is rejected. The five top-level ids of each market are listed above; the `categories` action returns the full tree (300 shelves on `de`, 228 on `fr`) with every id, name, parent and url. The url slug is cosmetic: the id is what the source keys on. An unknown id is answered honestly by the source with 0 results. Cannot be combined with `query` (see `query`). [one of: 186606, 255882, 284266, 300992, 301927, 8, 9, 7, 11, 10]
- `condition` (array, optional) — Keep only products that have a copy in these condition grades — momox's own five tiers. Several allowed and the source really unions them: measured on `q=harry potter`, New alone 653, UsedGood alone 1,264 and ['New','UsedGood'] 1,820 against an unfiltered 2,378. Catalogue-wide on `de`: New 4,918,650 · UsedVeryGood 1,953,408 · UsedGood 1,868,315 · UsedAcceptable 399,668 · UsedLikeNew 231,173. A grade the source does not know (e.g. 'Mint') is silently ignored by it and returns the UNFILTERED catalogue, so unknown grades are rejected here instead. Snake-case spellings (`used_good`, `like_new`) are accepted. [one of: New, UsedLikeNew, UsedVeryGood, UsedGood, UsedAcceptable]
- `format` (string, optional) — Keep only this physical format, the source's own `medium` token. Measured on `q=harry potter`: 2,378 unfiltered -> 754 Taschenbuch -> 207 DVD -> 96 Audio CD. ⚠️ TWO THINGS THE SOURCE DOES THAT COST YOU ROWS IF NOBODY TELLS YOU. (1) It accepts exactly ONE value: repeating the parameter, comma-joining and pipe-joining were all swallowed and returned the unfiltered total. (2) It carries two parallel vocabularies, so the SAME human label appears under two different tokens — 'Taschenbuch' is both `Taschenbuch` (4,134,492) and `paperback` (1,010,506), and 'Gebundene Ausgabe' is both `Gebundene Ausgabe` (1,114,271) and `hardcover` (272,613). When your value has such a sibling the engine names it in `meta.format_sibling_value` so you can run the second call. An unknown format is answered honestly by the source with 0 results. [one of: Taschenbuch, Gebundene Ausgabe, paperback, Audio CD, hardcover, Broschiert, DVD, Sonstige Einbände, Blu-ray, Videospiel, audioCD, Computerspiel, CD-ROM, pocket_book, perfect, Paperback, blu_ray, Comic, spiral_bound, Hardcover]
- `price_min` (number, optional) — Lowest price in EUR, major units (2 = EUR 2.00). The source's price filter works in whole-euro bands: it is sent as `price=<min>,<max>` and the bands it publishes are 1-2, 2-5, 5-10, 10-20 and 20-max. Measured on `q=harry potter`: 2,378 unfiltered -> 50 in the 1-2 band -> 558 at 20 and above. Use with `price_max`; giving only `price_min` sets the upper end to the source's own `max` keyword.
- `price_max` (number, optional) — Highest price in EUR, major units, same whole-euro banding as `price_min`. Given alone it is sent as `price=0,<max>`. The band applies to the price the storefront shows for the product, which is the price of its best available condition — see `best_price` vs `lowest_price` in the returned rows.
- `age_rating` (string, optional) — German age rating (FSK) and its international equivalents, the source's own label verbatim. `de` ONLY — momox-shop.fr publishes no rating facet and asking for one there is rejected rather than silently ignored. Measured on `q=harry potter` / `de`: 2,378 unfiltered -> 2 at 'Freigegeben ab 16 Jahren'; on `q=star wars` the same filter gave 48. Only 193,223 listings carry a rating at all, so this filter cuts hard by design. [one of: Freigegeben ohne Altersbeschränkung, Freigegeben ab 6 Jahren, Freigegeben ab 12 Jahren, Freigegeben ab 16 Jahren, Freigegeben ab 18 Jahren, Nicht geprüft, Unbekannt, Unrated, Infoprogramm, Lehrprogramm, Indiziert, G (General Audience), NR (Not Rated), R (Restricted), PG-13 (Parental Guidance Suggested), X (Mature Audiences Only), FSK 0, FSK 12, FSK 16, FSK 18]
- `author` (string, optional) — Browse one contributor's shelf by the source's own slug — `j-k-rowling`, `coelho-paulo`, `lucinda-riley`. Measured: 409 rows for `j-k-rowling`, 660 for `coelho-paulo`, 0 for a slug that does not exist. ⚠️ TWO measured limits, both enforced rather than hidden. (1) It REPLACES the keyword, it does not narrow it: `from=j-k-rowling` returned the same 409 Rowling rows whether the query was 'harry potter' or 'star wars', so `query` + `author` is rejected. (2) The shelf is ONE FIXED PAGE: page 0, page 1 and page 2 of it, the same shelf with condition=New and the same shelf with sorting=-price all returned the byte-identical 28 rows, so `page`, `condition`, `format`, `price_min`/`price_max`, `age_rating`, `include_unavailable` and `sort` are rejected here instead of being silently dropped — the total still tells you how many the contributor has. Every `search` response lists the top contributor slugs of the current result set under `facets.manufacturer`.
- `include_unavailable` (boolean, optional, default false) — Include products momox currently has NO copy of. Off by default, exactly like the storefront. It opens the result up a lot and the counts prove the source really applies it: `q=harry potter` 2,378 -> 5,759 and `q=star wars` 4,184 -> 10,000 (the latter hits the source's own 10,000 total ceiling — see `total_is_capped`).
- `sort` (enum, optional, default "relevance") — Row order. Verified to really reorder on `q=harry potter` in one run: `price_desc` opened at EUR 316.00, 313.50, 259.60, 250.50, 249.40 and `date_desc` at four 2026-09-01 titles while `date_asc` opened at 1854-01-01 and 1900-01-01. One honest caveat about `price_asc`: the source sorts it on the CHEAPEST variant, not on the price it shows, and it is not strictly monotonic at every page boundary (measured page 1 max 1.54 <= page 2 min 1.54, but page 2 max 4.79 > page 3 min 1.99). Any word outside this list is silently ignored by the source, so it is rejected here. [one of: relevance, popularity, price_asc, price_desc, date_asc, date_desc]
- `page` (integer, optional, default 1) — Which page of results, 1-based. ⚠️ The source is 0-indexed and answers `page=0`, `page=-1`, `page=abc` and no page at all with the FIRST page, which makes its own `page=1` the SECOND page — this engine does the translation so page 1 really is the first page and nothing is skipped. Page size is VARIABLE: 29, 28, 30, 29, 30, 27, 26 and 8 rows were measured on consecutive pages of one query, so read `page_rows` and `last_page` instead of multiplying. One page past the end the source silently switches to a broader query and reports a different total, so and far past the end it answers with neither rows nor a page count, so a page beyond `last_page` is REFUSED here rather than answered with another query's rows.
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility and it changes nothing here. There is no personal data on this source: the only seller is momox SE, a company, and it is always returned in full.

**Returns:** products[]{product_id, mpid, ean, isbn13, isbn10, title, media_type, format, authors[], translators[], narrators[], artists[], contributor_name, contributor_url, contributor_slug, publisher, label, brand, edition, publication_date, release_date, page_count, weight_grams, item_count, language, age_rating, is_adult_content, best_price, best_price_display, best_price_condition, lowest_price, lowest_price_display, lowest_price_condition, best_price_is_lowest, new_price, saving_vs_new_pct, saving_vs_new_amount, on_sale, currency, stock, in_stock, stock_from_variants, stock_agrees_with_variants, conditions_available[], offer_count, offers[]{variant_id, condition, price, price_display, currency, stock, in_stock, on_sale, saving_pct, saving_amount}, is_active, image_url, url, market, storefront, country, amazon_url, rating_average, rating_count, rating_breakdown{}, category_path[], detail_params{}} + meta{market, storefront, country, currency, total_results, total_is_capped, catalogue_count_hint, pages, last_page, page, page_rows, page_size, has_more, exact_match, fallback_search, query_normalised, resolved_filters{}, format_sibling_value, price_range{min,max}, facets{}, seller{}}

**Example request body:**
```json
{
  "market": "de",
  "query": "harry potter",
  "condition": [
    "UsedGood"
  ],
  "sort": "price_desc"
}
```

### POST https://api.reefapi.com/momox/v1/detail — 3 credits
Everything one momox product page publishes, by momox article id, by ISBN or by EAN barcode. The complete condition ladder (a price, a unit stock count, a discount-versus-new figure and a sale flag for each grade momox holds), the bibliographic record (EAN, ISBN-10 and ISBN-13, author, translator, publisher, label, binding, page count, publication date, language, weight, FSK rating), the full description, the store's own review score and star breakdown, the category breadcrumb, the price momox quotes for a NEW copy for comparison, and optionally the other titles it links to the same author. Both of the page's price witnesses are read and reconciled: the headline `best_price` is the price of the best CONDITION in stock, not the cheapest copy — those two differed on 95 of 145 rows measured — so `lowest_price`, `lowest_price_condition` and `best_price_is_lowest` are published next to it, and `price_matches_page_schema` says whether the page's own schema.org block agrees. An id or barcode the store does not carry is NOT_FOUND, not an empty success.

**Parameters:**
- `market` (enum, required, default "de") — Which momox media storefront to read. Prices, stock and the whole category id space are PER MARKET and really differ: on 2026-10-08 ISBN 9783551551672 read EUR 4.51 with 3,819 copies on `de` and EUR 4.49 with 3,818 copies on `fr` in the same minute. Both markets quote EUR, so nothing is converted here — a row always carries the market it was read from. `de` also publishes an FSK age-rating facet that `fr` does not have. [one of: de, fr]
- `product_id` (string, optional) — The momox article id, exactly as it appears at the end of every product url and in every `search` row (`M03551551677`). It is `M0` + the item's Amazon ASIN, and for a book that ASIN is the ISBN-10 — which is why `isbn` below is answered in a single request. Give this, or `isbn`, or `ean`. An id the store does not carry is NOT_FOUND.
- `isbn` (string, optional) — Look a BOOK up by its barcode. ISBN-13 and ISBN-10 are both accepted, with or without hyphens and spaces, and the check digit is verified before anything is requested. An ISBN-13 that starts with 978 is turned into the momox article id directly, so the lookup costs ONE request; `meta.resolved_by` says which route was used. (The source's own search answers a bare ISBN-10 with zero hits, so handing it one is not an option — this engine converts it.)
- `ean` (string, optional) — Look a CD, DVD, Blu-ray, game or piece of software up by its barcode. The full 13-digit EAN is required including leading zeros — the source answers `711719833857` with nothing and `0711719833857` with the right product, which is a 12-vs-13-digit trap worth knowing. A 12-digit UPC is padded to 13 here. Unlike a book ISBN, a non-book EAN is not inside the article id, so this route costs two requests (the source's own exact-match search, then the product page) and `meta.resolved_by` says so.
- `include_related` (boolean, optional, default false) — Also return the other titles the source links to the same contributor on that page (up to a dozen rows with their own price, stock and url). Off by default because it roughly doubles the response for callers who only want the one product; it costs no extra request either way.
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility and it changes nothing here. There is no personal data on this source: the only seller is momox SE, a company, and it is always returned in full.

**Returns:** product{product_id, mpid, ean, isbn13, isbn10, title, media_type, format, authors[], translators[], narrators[], artists[], contributor_name, contributor_url, contributor_slug, publisher, label, brand, edition, publication_date, release_date, page_count, weight_grams, item_count, language, age_rating, is_adult_content, best_price, best_price_display, best_price_condition, lowest_price, lowest_price_display, lowest_price_condition, best_price_is_lowest, new_price, saving_vs_new_pct, saving_vs_new_amount, on_sale, currency, stock, in_stock, stock_from_variants, stock_agrees_with_variants, conditions_available[], offer_count, offers[]{variant_id, condition, price, price_display, currency, stock, in_stock, on_sale, saving_pct, saving_amount}, is_active, image_url, url, market, storefront, country, amazon_url, rating_average, rating_count, rating_breakdown{}, category_path[], detail_params{}, description} + offers[]{…the same condition ladder, flat…} + summary{offer_count, in_stock_offer_count, units_in_stock, min_price, max_price, price_by_condition{}, stock_by_condition{}, price_matches_page_schema, page_schema_price, page_schema_availability} + related[]{product_id, ean, title, authors[], best_price, currency, stock, url} + meta{market, storefront, currency, resolved_by, requested_key, seller{}}

**Example request body:**
```json
{
  "market": "de",
  "isbn": "9783551551672",
  "include_related": true
}
```

### POST https://api.reefapi.com/momox/v1/categories — 1 credit
The category tree of one storefront, straight from its own category sitemap: every shelf with the numeric id that `search` takes, its name, its depth, its parent and its url — 300 shelves on `de` and 228 on `fr` on 2026-10-08. This is the resolver for `search`'s `category`, which matters because the ids are NOT shared between markets: Bücher is 186606 on `de` while Livres is 8 on `fr`. One request, ~48 KB, and the five top-level shelves come back with the catalogue counts the storefront itself publishes for them.

**Parameters:**
- `market` (enum, required, default "de") — Which momox media storefront to read. Prices, stock and the whole category id space are PER MARKET and really differ: on 2026-10-08 ISBN 9783551551672 read EUR 4.51 with 3,819 copies on `de` and EUR 4.49 with 3,818 copies on `fr` in the same minute. Both markets quote EUR, so nothing is converted here — a row always carries the market it was read from. `de` also publishes an FSK age-rating facet that `fr` does not have. [one of: de, fr]
- `max_depth` (integer, optional, default 9) — How deep into the category tree to return. 1 = the five top-level shelves only, 2 = those plus their children, 9 = everything the source publishes (the deepest DE shelf measured sits 6 levels down). Depth is derived from the shelf's own url path, which is how the source itself nests them.
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility and it changes nothing here. There is no personal data on this source: the only seller is momox SE, a company, and it is always returned in full.

**Returns:** categories[]{category_id, name, slug, depth, parent_path[], url, search_params{market, category}} + top_level[]{category_id, name, listing_count} + meta{market, storefront, total_categories, returned_categories, max_depth}

**Example request body:**
```json
{
  "market": "de",
  "max_depth": 2
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

## More
- Try it live, no code: https://reefapi.com/playground?engine=momox
- Human docs page: https://reefapi.com/docs/momox
- Overview page: https://reefapi.com/momox-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
