Overview
As described in a previous article, I developed an application that delivers IIIF manifest files and provides the IIIF Content Search API.
https://zenn.dev/nakamura196/articles/76bdc86b1b7524
However, there were parts that did not conform to the API specifications.
This article shares the corrections made and introduces how to use the Presentation API Validator with practical examples.
https://presentation-validator.iiif.io/
Manifest File Corrections
Access the Presentation API Validator site above, enter the manifest file URL in URL of Manifest to Validate, and select the corresponding API version (in this case, 3.0).
If there are errors, they will be displayed as shown below.

The manifest files delivered by the application I developed, mentioned at the beginning, are large in size, so entering the URL directly into the validator takes a long time before validation results appear.
Therefore, I created a separate manifest file containing only the first canvas data. Running this through the validator revealed 59 error messages.
Below are the fixes for each error.
Error 1 of 59. Message: ['https://ocr.aws.ldas.jp/v1/manifest/3437686.json'] is not of type 'string'
before
{
"id": [
"https://ocr.aws.ldas.jp/v1/manifest/3437686.json"
]
}
The manifest file’s id was mistakenly provided as an array. This was corrected as follows.
after
{
"id": "https://ocr.aws.ldas.jp/v1/manifest/3437686.json"
}
Error 2 of 59. Message: 'Persistent ID' is not of type 'object'
before
{
"metadata": [
{
"label": "Persistent ID",
"value": "info:ndljp/pid/3437686"
}
]
}
This type of error was common across errors 2-23 and 26-59 (primarily in metadata sections).
The language (in this case none) must be specified, and the value must be provided as an array. Languages such as ja or en can be specified as needed.
after
{
"metadata": [
{
"label": {
"none": [
"Persistent ID"
]
},
"value": {
"none": [
"info:ndljp/pid/3437686"
]
}
}
]
}
Error 24 of 59. Message: {'@context': 'http://iiif.io/api/search/1/context.json', '@id': 'https://search.aws.ldas.jp/search/3437686', 'profile': 'http://iiif.io/api/search/1/search'} is not of type 'array'
before
{
"service": {
"@context": "http://iiif.io/api/search/1/context.json",
"@id": "https://search.aws.ldas.jp/search/3437686",
"profile": "http://iiif.io/api/search/1/search"
}
}
This is the section specifying the IIIF Search API. The service value was changed to an array, @id was corrected to id, and the required type property was set to SearchService1.
after
{
"service": [
{
"@context": "http://iiif.io/api/search/1/context.json",
"id": "https://search.aws.ldas.jp/search/3437686",
"profile": "http://iiif.io/api/search/1/search",
"type": "SearchService1"
}
]
}
Error 25 of 59. Message: 'https://dl.ndl.go.jp/ja/iiif_license.html' does not match 'http://creativecommons.org/licenses/.*'
before
{
"rights": "https://dl.ndl.go.jp/ja/iiif_license.html"
}
The values allowed for rights appear to be limited to URLs from https://creativecommons.org or https://rightsstatements.org.
https://iiif.io/api/presentation/3.0/#rights
Therefore, rights was removed and the value was moved to metadata.
(This approach makes the license terms harder to find in the viewer, so we continue to investigate whether there is a better method.)
after
{
"metadata": [
{
"label": {
"none": [
"rights"
]
},
"value": {
"none": [
"https://dl.ndl.go.jp/ja/iiif_license.html"
]
}
}
]
}
Error 29 of 59. Message: 'id' is a required property
before
{
"@id": "https://www.dl.ndl.go.jp/api/iiif/3437686/range/1",
"@type": "sc:Range",
"label": {
"none": [
"Table of Contents"
]
},
"items": [...]
}
In the structures section for the table of contents, v2 notation remained. @id was changed to id, @type was changed to type, and sc: was removed from the value.
after
{
"id": "https://www.dl.ndl.go.jp/api/iiif/3437686/range/1",
"type": "Range",
"label": {
"none": [
"Table of Contents"
]
},
"items": [...]
}
Result
After applying the above corrections, Validated successfully was displayed.

IIIF Content Search API
Next, corrections to the IIIF Content Search API. Since there is no validator available for this API (as far as I know), bugs were fixed by referring to the documentation. Some corrections or errors may still remain.
https://iiif.io/api/search/1.0/
@context Correction
before
{
"@context": "http://iiif.io/ns/search/canvas.json"
}
A non-existent URI was specified. This was corrected to the proper URI.
after
{
"@context": "https://iiif.io/api/search/1/context.json"
}
@type and Canvas xywh Correction
before
{
"@id": "https://www.dl.ndl.go.jp/api/iiif/3437686/canvas/3#xywh=2074,3786,711,222/searchResults",
"@type": "oa:Annotation",
"motivation": "sc:panting",
"resource": {
"@type": "cnt:ContextAstext",
"chars": "公央中"
},
"on": "https://www.dl.ndl.go.jp/api/iiif/3437686/canvas/3#xywh=2074,3786,711,222/searchResults"
}
The unnecessary string /searchResults appended to the end of on was removed. Additionally, the @type value was corrected from cnt:ContextAstext to cnt:ContentAsText. Furthermore, though not a mandatory fix, the @id specification was revised following the Wellcome Collection’s approach.
https://iiif.wellcomecollection.org/search/v0/b18035723?q=ab
after
{
"@id": "https://www.dl.ndl.go.jp/api/iiif/3437686/canvas/3/h1r2074,3786,711,222",
"@type": "oa:Annotation",
"motivation": "sc:panting",
"resource": {
"@type": "cnt:ContentAsText",
"chars": "公央中"
},
"on": "https://www.dl.ndl.go.jp/api/iiif/3437686/canvas/3#xywh=2074,3786,711,222"
}
Adding the hits Element
Previously, the API response did not include the hits element.
https://iiif.io/api/search/1.0/#search-api-specific-responses
By adding the hits element along with the other corrections, search results now display correctly in Image Annotator as well.

Summary
While there may still be incomplete aspects, we established the correction approach for each API as described above. We will proceed to apply these fixes to the actual services.
I hope this article serves as a useful reference for using IIIF validators and writing IIIF Presentation API v3 compliant descriptions. (Most of these were simple oversights on my part…)
Thanks to @_masaka for bringing these issues to our attention!
(And apologies for the inconvenience caused by having to modify your application to accommodate our bugs.)