Resolve API
Normalize any code spelling and get variants, sources, a share card, provenance and streams for one title.
/resolve tells you what a code is and what every source says. /movie returns
the first provider that answers; these endpoints query every source and keep the
disagreements.
Use them to normalize abp002, ABP-002 or ABP-2, list every source for a title, or get a
share card or stream map in one call.
| Endpoint | Returns | Per 1,000 calls |
|---|---|---|
GET /resolve | Canonical code, variants, sources, link, inline card | $1.20 |
POST /resolve/batch | Found or not found for up to 100 codes | $0.40 per started group of 10 unique codes |
GET /resolve/compare | Two titles side by side | $0.40 |
GET /resolve/provenance | Source and confidence per field | $0.40 |
GET /resolve/streams | Stream, sample and magnet availability | $0.40 |
GET /resolve
Normalizes any spelling to one canonical code and returns everything attached: variants,
sources, a link to /movie, a 1200×630 card.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
q | string | yes | The code or spelling to resolve. At least 2 characters. |
format | png, jpg or svg | no | Card encoding. Defaults to png. |
curl -s 'https://api.javinfo.dev/resolve?q=abp002&format=jpg' \
-H "x-javinfo-key: jvi_your_key_here"{
"input": "abp002",
"code": "ABP-002",
"shareSlug": "85150B9G60S0M",
"found": true,
"variants": ["ABP-002", "ABP002"], // 7 total
"contentIdGuess": "118abp00002",
"sources": ["fanza", "javdatabase"], // 6 total
"link": "/movie?q=ABP-002",
"card": {
"format": "jpg",
"mime": "image/jpeg",
"dataUri": "data:image/jpeg;base64,…"
}
}The card is inline in card.dataUri, so responses are large: about 1.1 MB with png (default), 900 KB with svg, 180 KB with jpg. Pick jpg when size matters — a lossy raster about six times smaller than png. The base64 payload is cut in the example; dataUri is a complete data: URI for <img src>.
| Field | Meaning |
|---|---|
input | The string you sent, unchanged. |
code | The canonical code. |
variants | Every spelling that keys the same title, most canonical first. |
sources | Public ids of the sources that list the title. |
contentIdGuess | DMM content id when the storefront index has it, otherwise null. |
link | API path for the same title, /movie?q=<code>. Relative to https://api.javinfo.dev; call it with your x-javinfo-key header. |
card | The 1200×630 card as a data URI, or null. |
found | true on a 200. A miss is a 404. |
shareSlug | Checksummed slug for the canonical code. |
The card shows the code, English title (shortened, or the code when none), up to three cast names and the studio, up to four genre chips, then rating, runtime, release date and magnet count when known. A STREAMABLE badge marks a stream. The jacket sits right over a blurred cover. null if rendering is unavailable; treat it as optional and keep the metadata.
A code that matches nothing is a 404, not an empty card:
{
"defined": false,
"code": "NOT_FOUND",
"status": 404,
"message": "no title found for \"ZZZZ-99999\""
}POST /resolve/batch
Checks up to 100 codes in one request: clean a user's list or match your catalog without 100 sequential calls.
| Body field | Type | Required | Meaning |
|---|---|---|---|
codes | string array | yes | 1 to 100 non-empty strings. |
curl -s -X POST 'https://api.javinfo.dev/resolve/batch' \
-H "x-javinfo-key: jvi_your_key_here" \
-H 'content-type: application/json' \
-d '{"codes":["ABP-002","beb001","ZZZZ-99999"]}'{
"count": 3,
"found": 2,
"results": [
{ "input": "ABP-002", "code": "ABP-002", "found": true },
{ "input": "beb001", "code": "BEB-001", "found": true },
{ "input": "ZZZZ-99999", "code": "ZZZZ-99999", "found": false }
]
}A miss is found: false inside a normal 200, not a 404; code then holds the best
canonical guess. count is codes checked after deduplication; found is hits.
Codes are trimmed and deduplicated before counting. "ABP-002" and " ABP-002 " cost one
slot and appear once in results. Matching is exact after trimming, so abp-002 and
ABP-002 are two slots.
An empty codes array, more than 100 codes, or an empty string is a 400:
{
"defined": false,
"code": "BAD_REQUEST",
"status": 400,
"message": "Input validation failed",
"data": {
"issues": [
{
"origin": "array",
"code": "too_small",
"minimum": 1,
"inclusive": true,
"path": ["codes"],
"message": "Too small: expected array to have >=1 items"
}
]
}
}The same shape covers the other validation failures, with a different issues entry.
GET /resolve/compare
Two titles side by side: each core card, shared fields, a similarity score.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
a | string | yes | First code. At least 2 characters. |
b | string | yes | Second code. At least 2 characters. |
curl -s 'https://api.javinfo.dev/resolve/compare?a=SSIS-001&b=ABP-002' \
-H "x-javinfo-key: jvi_your_key_here"{
"a": {
"input": "SSIS-001",
"code": "SSIS-001",
"shareSlug": "AD9MJMSD60R32E",
"found": true,
"variants": [
"SSIS-001",
"SSIS001",
"…"
],
"contentIdGuess": "ssis00001",
"sources": [
"fanza",
"javdatabase",
"javlibrary",
"javbus",
"missav",
"sextb"
],
"link": "/movie?q=SSIS-001"
},
"b": {
"input": "ABP-002",
"code": "ABP-002",
"shareSlug": "85150B9G60S0M",
"found": true,
"variants": [
"ABP-002",
"ABP002",
"…"
],
"contentIdGuess": "118abp00002",
"sources": [
"fanza",
"javdatabase",
"javlibrary",
"javbus",
"missav",
"sextb"
],
"link": "/movie?q=ABP-002"
},
"shared": {
"actresses": [],
"categories": [],
"maker": null
},
"similarity": 0.05
}Each side has the /resolve core shape, sans card. sources lists every matching source;
contentIdGuess is the DMM content id when the storefront index has it — both as on
/resolve.
shared holds the case-insensitive intersection of actresses and categories; maker
fills only when both titles carry the exact same maker string.
similarity is a 0 to 1 blend, mostly shared actresses (0.34) and categories (0.26), plus the
same maker (0.1), runtime (0.06) and release-date proximity (0.06), rounded to 3 decimals. If
either code is missing, that side is found: false, shared empty, similarity 0. Never
404.
GET /resolve/provenance
Shows each field's source, how much the API trusts it, and what other sources said.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
q | string | yes | The code or spelling to resolve. At least 2 characters. |
curl -s 'https://api.javinfo.dev/resolve/provenance?q=ABP-002' \
-H "x-javinfo-key: jvi_your_key_here"{
"code": "ABP-002",
"found": true,
"sources": ["fanza", "javbus"], // 6 total, alphabetical
"fields": {
"title": {
"value": "NEW TOKYO Style 01 - Aikarin",
"source": "fanza",
"confidence": 1,
"candidates": [ // 6 total, trust order
{ "source": "fanza", "value": "NEW TOKYO Style 01 - Aikarin" },
{ "source": "javdatabase", "value": "NEW TOKYO Style 01 - Aikarin" }
]
},
"titleJa": {
"value": "NEW TOKYO流儀 01 あいかりん",
"source": "javlibrary",
"confidence": 0.8,
"candidates": [ // 4 total
{ "source": "javlibrary", "value": "NEW TOKYO流儀 01 あいかりん" },
{ "source": "javbus", "value": "NEW TOKYO流儀 01 あいかりん" }
]
},
"rating": {
"value": 8.1,
"source": "javlibrary",
"confidence": 0.8,
"candidates": [{ "source": "javlibrary", "value": 8.1 }]
}
// releaseDate, runtimeMins, maker, label, series, categories,
// actresses and cover follow the same shape
},
"data": {
"title": "NEW TOKYO Style 01 - Aikarin",
"titleJa": "NEW TOKYO流儀 01 あいかりん",
"rating": 8.1
// the merged value for every field above
}
}fields covers title, titleJa, releaseDate, runtimeMins, maker, label, series,
categories, actresses, cover and rating.
Scalars go to the highest-weight source; arrays such as categories and actresses are
unioned, not replaced. candidates lists every source that offered a value, best first, so
disagreements stay visible.
confidence is the winning weight over the best weight in the response: 1 for the top
source, lower for the rest. Weights, highest first: storefronts 1, javdatabase 0.85,
javlibrary 0.8, javbus 0.7, missav 0.6, sextb 0.55. sources is alphabetical;
candidates is trust order.
An unknown code is a 404 with the same body as GET /resolve.
GET /resolve/streams
Returns where to watch or download a title: HLS streams, samples, magnets, subtitles, gallery size.
| Parameter | Type | Required | Meaning |
|---|---|---|---|
q | string | yes | The code or spelling to resolve. At least 2 characters. |
curl -s 'https://api.javinfo.dev/resolve/streams?q=ABP-002' \
-H "x-javinfo-key: jvi_your_key_here"{
"code": "ABP-002",
"found": true,
"playback": "hls",
"availability": {
"magnets": 47,
"stream": true,
"sample": false,
"trailer": false,
"gallery": 0,
"subtitles": [],
"uncensored": false,
"score": 0.7
},
"streams": {
"master": "https://surrit.com/c511196b-b4f8-47bf-a6b0-64ba625fc2a4/playlist.m3u8",
"variants": [
"https://surrit.com/c511196b-b4f8-47bf-a6b0-64ba625fc2a4/1280x720/video.m3u8",
"https://surrit.com/c511196b-b4f8-47bf-a6b0-64ba625fc2a4/842x480/video.m3u8"
]
},
"sampleUrl": null,
"galleryCount": 106,
"sources": ["fanza", "javdatabase"] // 6 total
}playback answers in one line: hls with a stream, else magnet with magnets, else sample
with only a sample or trailer, else none.
availability adds detail:
| Field | Meaning |
|---|---|
magnets | Distinct magnet infohashes across every source. |
stream | An HLS master is present. |
sample | A sample clip or trailer is present. |
trailer | A trailer specifically. |
gallery | Gallery image count from the source picked for media. |
subtitles | Subtitle languages detected in the metadata. zh is the only one tagged today. |
uncensored | The metadata marks the title as uncensored. |
score | 0 to 1 completeness: magnets 0.35, stream 0.35, sample 0.15, gallery 0.1, subtitles 0.05. |
streams is {master, variants} with HLS, else null. master is the playlist for a player;
variants are per-resolution playlists. sampleUrl is the sample clip when present;
galleryCount is the largest across sources.
An unknown code is a 404 with the same body as GET /resolve.
Cost
Only a fresh 200 is charged. Cache hits (X-Cache: HIT), 4xx responses and degraded
answers (X-Degraded) are free. Resolve, compare and provenance cache 1 hour; streams 15
minutes. Batch does not cache.
GET /resolve costs $1.20 per 1,000 calls because it renders the card; other resolve
endpoints cost $0.40 per 1,000 calls.
Batch bills the codes left after trimming and deduplication, in started groups of 10:
| Unique codes | Per 1,000 calls |
|---|---|
| 1–10 | $0.40 |
| 11–20 | $0.80 |
| 21–30 | $1.20 |
| 100 | $4.00 |
Status codes
| Endpoint | Codes |
|---|---|
GET /resolve | 200, 400, 401, 402, 404, 429 |
POST /resolve/batch | 200, 400, 401, 402, 429 |
GET /resolve/compare | 200, 400, 401, 402, 429 |
GET /resolve/provenance | 200, 400, 401, 402, 404, 429 |
GET /resolve/streams | 200, 400, 401, 402, 404, 429 |
A q under 2 characters, an unknown format, and the batch body failures above are 400.
Common errors
No key and a bad key both give 401, with different bodies.
# No key
curl -s 'https://api.javinfo.dev/resolve?q=ABP-002'{
"defined": false,
"code": "UNAUTHORIZED",
"status": 401,
"message": "Unauthorized"
}# Bad key
curl -s 'https://api.javinfo.dev/resolve?q=ABP-002' \
-H "x-javinfo-key: jvi_not_a_real_key"{ "error": "invalid api key" }402 means the balance is below the call price. Nothing is charged; Retry-After: 3600 hints
for automated clients.
{
"defined": false,
"code": "PAYMENT_REQUIRED",
"status": 402,
"message": "insufficient balance"
}429 is the per-account per-minute cap; Retry-After gives seconds to wait.
{ "error": "rate limit exceeded" }Account side: Rate limits & billing and Errors. Generated
reference: /docs/api/discovery/resolve.