Filtering
Filter, sort and paginate /query results, plus what each provider supports.
Filtering applies to
/queryonly (multi-result search). The single-hit/movieand/randomendpoints do not take filters.
1. Quick start
Every /query request accepts four optional inputs on top of the search term
q: filter, sort, page, and num (page size). Works on both GET
(query string) and POST (JSON body).
KEY="jvi_..." # your API key, sent as the x-javinfo-key header
# POST — genre + runtime filter, newest first
curl -s -X POST https://api.javinfo.dev/query \
-H "x-javinfo-key: $KEY" -H 'content-type: application/json' \
-d '{
"q": "ema kisaki",
"filter": { "genre": "Big Tits", "runtimeMin": 120 },
"sort": "release",
"page": 1
}'
# GET — same thing, flattened into query params
curl -s "https://api.javinfo.dev/query?q=ema%20kisaki&filter[genre]=Big%20Tits&filter[runtimeMin]=120&sort=release" \
-H "x-javinfo-key: $KEY"q is optional when a filter is present. You can browse a whole category
with no keyword:
# All "creampie" titles, no search term
curl -s -X POST https://api.javinfo.dev/query \
-H "x-javinfo-key: $KEY" -H 'content-type: application/json' \
-d '{ "filter": { "genre": "creampie" } }'A request with neither q nor a filter is rejected with 400
("provide a search query or at least one filter").
Cost is a flat 200 units per successful /query call regardless of filters.
A request that returns no result (404) or errors is free.
2. The inputs
filter
An object. All fields are optional. Set only the ones you need.
| Field | Type | Meaning |
|---|---|---|
genre | string | Category / genre |
actress | string | Actress (performer) |
maker | string | Maker / studio |
series | string | Series |
director | string | Director |
label | string | Label |
actor | string | Male actor |
censored | "censored" | "uncensored" | Mosaic status |
runtimeMin | number (minutes) | Minimum runtime |
runtimeMax | number (minutes) | Maximum runtime |
releaseAfter | string YYYY-MM-DD | Released on/after |
releaseBefore | string YYYY-MM-DD | Released on/before |
availability | "playable" | "magnets" | "subtitle" | "single" | Has stream / magnets / subtitles / is a single title |
sort
One of: relevance (default), release, update, rating, views.
relevance: text-match relevance (default when omitted).release: newest release date first.update: recently added/updated first.rating: highest rated first.views: reserved; no provider supports it yet (always rejected).
page
1-based integer. Omitted = page 1. Page size is set by num.
num
Results per page. Integer, default 10, max 50 (higher values are
clamped, not rejected; num < 1 → 400). Best-effort per provider:
javdatabase honors it only up to the site's fixed archive page size (caps
the returned rows); missav caps at its search count. page offset is
computed from num (page 2 with num=10 skips the first 10).
GET numeric coercion.
numis coerced from the query string, butpageand nestedfilter[...]numbers are not yet. On GET they reject with400 "expected number, received string". Sendpageand numeric filters via POST JSON until that's fixed.
3. Provider capability matrix
/query searches multiple providers. Not every provider supports every filter.
This is the crux of the system. Read it before building queries.
Providers (and their ids): query:fanza, query:dmm (the two DMM + FANZA
storefronts), query:missav, query:javdb, query:javdatabase.
| Capability | fanza / dmm | missav | javdb | javdatabase |
|---|---|---|---|---|
genre | ✅ | ✅ | ❌ | ✅ |
actress | ✅ | ✅ | ❌ | ✅ |
maker | ✅ | ✅ | ❌ | ✅ |
series | ✅ | ✅ | ❌ | ✅ |
director | ✅ | ✅ | ❌ | ✅ |
label | ✅ | ✅ | ❌ | ❌ |
actor | ✅ | ✅ | ❌ | ❌ |
censored: censored | ✅ | ✅ | ✅ | ✅ |
censored: uncensored | ❌ | ✅ | ✅ | ✅ |
runtimeMin / runtimeMax | ✅ | ✅ | ❌ | ❌ |
releaseAfter / releaseBefore | ❌ | ✅ | ❌ | ❌ |
availability | ❌ | ❌ | ✅ | ❌ |
| Combine multiple filters | any combo | any combo | ❌ none | only genre+maker |
sort: relevance | ✅ | ✅ | ✅ | ✅ |
sort: release | ✅ | ❌ | ✅ | ≈ (always newest) |
sort: update | ❌ | ❌ | ✅ | ❌ |
sort: rating | ❌ | ❌ | ✅ | ❌ |
sort: views | ❌ | ❌ | ❌ | ❌ |
page | ✅ | ❌ (always page 1) | ✅ | ✅ |
Legend: ✅ supported · ❌ not supported · ≈ accepted but behaves as the default.
Notes:
- fanza/dmm are censored-only. An
uncensoredfilter excludes them. - javdb has no per-field filters (genre/actress/etc). It only offers the
coarse censored tab,
availability, and rich sort. Asking javdb for any named dimension excludes it. - javdatabase combines only
genre+maker(a genre ∩ studio archive); any other pair excludes it. - missav ignores
page. It always returns the first page of matches. - missav ignores
sort. Results are relevance-ranked only.
4. How provider selection works (the waterfall)
/query tries providers in order, first valid result wins:
FANZA → DMM → missav → javdb → javdatabaseWhat happens to a filter a provider can't satisfy depends on whether you pinned providers.
Unpinned (default): skip and advance
If you don't send providers, every registered provider is a candidate. Any
provider that can't fully satisfy your filter is silently skipped, and the
waterfall advances to the next one. You get a result from the first provider
that can honor the whole request.
# sort=rating is javdb-only → fanza/dmm/missav/javdatabase are skipped,
# javdb answers.
curl -s -X POST https://api.javinfo.dev/query \
-H "x-javinfo-key: $KEY" -H 'content-type: application/json' \
-d '{ "q": "ABP", "sort": "rating" }'
# → { "source": "javdb", ... }If no provider can satisfy the request, you get 422 with a message
explaining why.
Pinned: hard error
Send providers to force a specific provider (or set). If a pinned provider
can't satisfy the filter, the request fails with 422 and a message
naming the reason. It is never silently dropped.
# Pin javdb, ask for a genre it can't filter → 422
curl -s -X POST https://api.javinfo.dev/query \
-H "x-javinfo-key: $KEY" -H 'content-type: application/json' \
-d '{ "q": "x", "providers": ["query:javdb"], "filter": { "genre": "creampie" } }'
# → 422 { "message": "does not support filter \"genre\"" }
# Pin fanza, ask for uncensored → 422
# → { "message": "does not support uncensored content" }providers accepts a comma list or array, with or without the query: prefix:
"providers": ["fanza","missav"] or "providers": "javdb".
5. Values are passed through verbatim (important)
This is a v1 pass-through design: whatever you put in a filter value is sent to that provider's own backend as-is. There is no shared taxonomy and no fuzzy matching across providers. A value that works on one provider will often not match on another, because each source stores its metadata differently.
Consequences you must plan for:
-
Genres differ per provider.
- fanza/dmm and javdatabase use English genre names (
"Big Tits","creampie"). - missav genres are Japanese (
"巨乳","フィスト"). An English genre will return nothing on missav.
- fanza/dmm and javdatabase use English genre names (
-
Names must match exactly. Filters are exact-match, not substring. On the DMM + FANZA index an actress is stored with her aliases baked into the name, e.g.
"Ema Kisaki (HARUKI, Haruki Kato)". Filteringactress: "Ema Kisaki"returns zero rows there. You need the full stored string. (This is a known v1 rough edge.) -
censoredmeans different things per source. On missav there is no literal "censored/uncensored" flag; the API mapscensored→ mainstream mosaic titles anduncensored→ everything else (Caribbeancom, 10musume, Tokyo-Hot, 1pondo, FC2, uncensored leaks, …). -
javdatabase paths are slugified.
maker: "S1 NO.1 STYLE"becomes thes1-no-1-stylestudio archive automatically. Pass the human-readable name; the slug is derived for you.
If a value doesn't match, you simply get fewer/no rows from that provider (and, if unpinned, the waterfall moves on). Nothing errors. An empty match is not an error.
6. Response shape
The body is the result set from the winning provider:
{
"q": "ema kisaki", // your query, echoed
"source": "fanza", // which provider answered
"count": 10, // page size (default 10, set by num)
"results": [
{
"id": "118abp00999", // stable id (content id or dvd id)
"dvdId": "ABP-999",
"title": "…",
"cover": "https://…/…ps.jpg",
"releaseDate": "2019-01-04",
"extra": { // provider-specific, keys vary by source
"runtimeMins": 130,
"maker": "…",
"categories": ["Big Tits", "…"],
"actresses": [{ "name": "…", "image": "https://…|null" }],
"duration": 7800, // missav (seconds)
"genres": ["巨乳"], // missav
"studio": "…", "url": "…" // javdatabase
// …
}
}
]
}extra is a superset: only the keys the answering provider produces are
present. Core fields (id, dvdId, title, cover, releaseDate) are
consistent across all providers.
Response headers carry metadata: X-Response-Time-Ms, X-Balance-Remaining.
7. Per-provider cheat sheet
fanza / dmm (the DMM + FANZA storefronts): richest structured filtering.
- All named dims, freely combined. English genre/name values.
runtimeMin/runtimeMaxin minutes.sort: release. Paginated.- Censored catalog only. No
uncensored, noavailability, no release range.
# Big-tits titles over 120 min, newest first
-d '{ "filter": { "genre": "Big Tits", "runtimeMin": 120 }, "sort": "release" }'missav: broadest filter surface, relevance-ranked only.
- All named dims (genres are Japanese), runtime and release ranges,
censoredboth ways. - No
sort(relevance only), no real pagination (page 1 only), noavailability.
# Uncensored titles matching "caribbean"
-d '{ "q": "caribbean", "filter": { "censored": "uncensored" } }'
# Japanese genre browse
-d '{ "q": "SSIS", "filter": { "genre": "巨乳" } }'javdb: no field filters, but best sort + availability.
- Only
censored(both),availability, andsort(relevance/release/update/rating). - Paginated. No genre/actress/maker/etc.
# Playable, highest rated
-d '{ "q": "ABP", "filter": { "availability": "playable" }, "sort": "rating" }'javdatabase: archive browsing by one dimension (or genre ∩ maker).
genre,actress,maker,series,director; onlygenre+makercombine. Censored + uncensored. English values, auto-slugified. Paginated.- No runtime/release range, no
label/actor, no real sort (newest-first).
# Creampie titles from S1
-d '{ "filter": { "genre": "creampie", "maker": "S1 NO.1 STYLE" } }'8. What is NOT possible (v1)
- Cross-provider value normalization. No unified genre/actress vocabulary; you must speak each provider's dialect (English vs Japanese, exact strings).
sort: viewsis reserved; no provider implements it.- Multi-value per dimension. One value per field (no
genre: ["a","b"]). - Ranges outside their provider. Runtime range is fanza/dmm/missav only; release range is missav only.
availabilityanywhere but javdb.- Combining arbitrary filters on javdb (none) or javdatabase (genre+maker only).
- Pagination / sort on missav.
- Filtering
/movieor/random.
When a combination isn't possible: pinned → 422 with a reason; unpinned →
that provider is skipped and the next one is tried.
Next
- Browse the full API reference. The
/queryoperation lists every filter param with a live playground. - See Errors for the
400and422cases.