IIIF Publication Preflight
Pricing
$0.05 / useful iiif report
IIIF Publication Preflight
Experimental bounded IIIF Presentation 3 image-resource and metadata diagnostics attributed to canvases.
Pricing
$0.05 / useful iiif report
Rating
0.0
(0)
Developer
L3Digital
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
4 hours ago
Last modified
Categories
Share
Get an experimental dependency report for one IIIF Presentation 3 Manifest with embedded annotation pages. The report joins HTTP, CORS and limited Image API metadata observations to the affected canvases. Use it to locate missing or restricted image dependencies and metadata problems before investigating them in a viewer. It does not check image pixels, viewer behavior or full IIIF conformance.
Use from an AI agent through MCP
Add this URL to a client that supports remote HTTP MCP, then authorize with your own Apify account using the Apify MCP setup guide:
https://mcp.apify.com/?tools=l3digital/iiif-publication-preflight
This configuration selects the Actor directly. Availability still depends on
Apify account and Actor eligibility. Call l3digital/iiif-publication-preflight
with the example input below; this page's pricing and input restrictions apply.
When using call-actor, its response contains run status and storage IDs. If the
run is still active, check that run with get-actor-run. After success, retrieve
the report with get-dataset-items using the returned dataset ID
(defaultDatasetId in the run API). These retrieval tools load with the Actor.
Retrieve the existing result instead of starting another run, which can incur
another report charge. Inspect coverage, resource access, robots and
diagnostics before using the observations.
Input
Submit one public HTTPS manifest URL. Optionally supply the HTTPS origin of your viewer to observe credentialless CORS responses for that origin.
For a public reference example, use:
{"manifestUrl": "https://iiif.io/api/cookbook/recipe/0008-rights/manifest.json","viewerOrigin": "https://example.org"}
This example uses the IIIF Cookbook's rights recipe, credited to Glen Robson, IIIF Technical Coordinator, under CC BY-SA 3.0. A hosted owner check of this reference produced complete coverage for three resources, with four HTTP attempts including robots, allowed CORS for the supplied origin and valid ImageService3 metadata. These are reference observations, not institutional coverage, visual validation or IIIF endorsement. Future responses can change. A complete useful report from this example qualifies for the $0.05 charge described below; it is not a free hosted demo.
manifestUrl must be caller-authorized public HTTPS without credentials, query,
fragment or a custom
port. viewerOrigin is optional and must be an HTTPS origin without an application
path. Runtime validation rejects unsafe DNS answers and checks robots before each
resource/redirect. No cookies, credentials or environment proxy settings are sent.
DNS resolution failure is inconclusive (dns_unresolved); private or mixed DNS
answers are refused. Origin robots policies and acquisition failures are cached
for one report, preserving the first diagnosis without repeated failed attempts.
Requests identify as IIIFPublicationPreflight. Robots groups match case-folded
prefixes of the product token before any /version; suffix/version substrings
do not override wildcard policy.
Robots decoding accepts a leading UTF-8 BOM and refuses invalid UTF-8. Empty
Allow/Disallow directives end a group's user-agent list. This intentionally
corrects earlier handling that could merge an empty wildcard Disallow group with
the following unrelated bot's deny rules; those unrelated rules no longer apply
to this Actor. Both group orders preserve the bot-specific policy.
Applicable Crawl-delay or Request-rate declarations make a source unsupported
for acquisition: this bounded Actor has no pacing scheduler. It conservatively
refuses protected requests for any declared value, including zero, empty or
malformed values. These extensions are not mandatory RFC 9309 directives; this
refusal honors the Actor's stronger source-policy contract. Only the selected
most-specific groups, including ties, impose this restriction; unrelated bot
groups and overridden wildcard groups do not. Robots reads are still permitted.
The report is partial, with resource robots: unknown, access: refused and
robots_pacing_unsupported in diagnostics; the refused source's manifest or
dependency is not contacted. Cached origins and redirect destinations receive
the same check. These partial reports do not charge a report event.
Use only sources you are authorized to inspect; public availability and robots
permission do not grant reuse rights. Confirm the source's terms and your intended
use before submitting a manifest. The reference example's license does not grant
permission to inspect or reuse material from other sources.
Output and interpretation
The Actor writes one default dataset row and the same JSON to the default key-value
store's OUTPUT record. Open either output after the run to read the report.
Resources are deduplicated by URL and method; each retains source-order canvasIds.
Images receive HEAD only. Root manifests, service info.json and robots receive
bounded GET. HTTP 200 proves neither decodable image content nor correct tiles,
pixels or viewer behavior. Image API metadata checks only the documented subset:
object, identifier, type/protocol/profile and positive integer dimensions. Canvas
and image dimensions may legitimately differ.
| Field | Meaning |
|---|---|
observedAt | UTC report start time |
coverage | complete supported observations; partial omitted/unknown surfaces; unsupported root outside embedded P3 Manifest scope |
resources | URL, GET/HEAD method, roles, affected canvases, final URL, status, access, robots, CORS and metadata diagnostics |
access | Observed 200 accessible, 404/410 missing, 401/403 restricted; rate limits, server errors, HEAD 405 and transport failures unknown; rejected acquisition hops refused |
cors | Observation for the supplied origin and actual GET/HEAD method, with no credentials; no universal browser compatibility claim |
findings | Explicit unsupported, inconsistent or omitted surfaces; bounded code strings, with canvas IDs where available |
omittedResources, omittedFindings | Counts of report omissions, making partial output explicit |
requestCount, byteCount | HTTP attempts including robots/redirects, and downloaded JSON/robots body bytes |
coverage: complete means the supported observations were performed, not that
every dependency is healthy. Missing (404/410) or restricted (401/403) image HEAD
responses can be complete negative observations. A non-200 service info.json
response leaves metadata unchecked and makes coverage partial.
Malformed/repeated CORS headers, wrong origins and invalid metadata are concrete
observations. Restricted access is reported without retrying with credentials.
refused can concern the resource, its redirect or a required robots hop; it does
not establish that the original resource's address itself is unsafe.
Collections, Presentation 2, remote annotation pages, nonpainting annotations,
Choice/SpecificResource/non-image bodies, ambiguous targets and unsupported
services remain explicit unchecked surfaces. The Actor does not fetch remote pages,
perform migration, validate full IIIF conformance, decode images or run a viewer.
Reference output excerpt
This excerpt comes from the hosted reference check above. The complete report
also contains the manifest and Image API metadata resources; those entries and
other fields are omitted here for readability. complete describes coverage,
while image content and full conformance remain unchecked.
{"reportVersion": 1,"coverage": "complete","resources": [{"url": "https://iiif.io/api/image/3.0/example/reference/918ecd18c2592080851777620de9bcb5-gottingen/full/max/0/default.jpg","method": "HEAD","roles": ["image"],"canvasIds": ["https://iiif.io/api/cookbook/recipe/0008-rights/canvas/p1"],"status": 200,"access": "accessible","robots": "allowed","cors": "allowed","imageContent": "unchecked"}],"requestCount": 4,"byteCount": 2711,"fullConformance": "unchecked"}
Bounds
The ceilings are 50 canvases, 100 URL/method resources including the manifest, 1,000 annotation traversal attempts, 128 HTTP attempts, 2 MiB downloaded metadata, 20 JSON nesting levels, 20,000 JSON value nodes, 45 seconds inside the worker, a 50-second process watchdog, three redirects per chain and 2,048-character URLs/identifiers. Raw ASCII path characters requiring encoding must already be percent-encoded; Unicode paths use UTF-8 percent encoding consistently with robots. The normalized URL must also fit 2,048 characters before robots work or DNS. Robots matching shares at most 100,000 work units per report, charging name and pattern characters plus wildcard-loop iterations. Policies are parsed once per origin. Over-limit policy remains unknown and protected acquisition is skipped. This deterministic work cap is not a measured CPU-time guarantee. One complex policy can exhaust the shared allowance and leave later resources, including other origins, unknown. Coverage on such hosts is unmeasured. Findings retain at most 100 entries and 100,000 UTF-8 bytes with explicit omitted counts. The JSON report is at most 750,000 UTF-8 bytes. Whole resource records are omitted when necessary; canvas attribution within retained records is never truncated. Policy and acquisition-budget failures produce partial observations when the worker returns. If the hard watchdog terminates the worker, or the isolated worker fails, the SDK run fails with a fixed error and persists no report. Partial recovery from a killed worker is not implemented.
Initial test pricing
The initial test price is $0.05 per useful report, billed as one
report-produced event. There is no Actor startup or dataset charge. One event is
requested only after both the default dataset row and KVS OUTPUT are successfully written,
and only when coverage is complete with at least one image resource carrying
nonempty canvasIds. Complete negative observations such as missing (404/410),
restricted (401/403) images or invalid Image API metadata still qualify.
Partial, unsupported, empty and unattributed reports are persisted without this event. Invalid input, worker failure/timeout and failed output writes do not request a charge. Pay-per-event mode requires the configured event and available capacity before acquisition. If a charge request fails, the run fails after output has been persisted; whether the platform accepted that charge may be unknown. An unexpected provider charge count also fails visibly. There is no automatic charge retry, restart recovery or cross-run deduplication. Rerunning the same input can produce another billable report.
Local offline example
For developers with this repository, Python 3.13 and uv are required to use the pinned SDK and model contracts. From the repository root, run the closed synthetic example on the worker:
rexec --shell 'cd actors/iiif-publication-preflight && uv run --locked python -m iiif_publication_preflight.demo'
This command makes no network or platform calls and bypasses billing. It prints
one JSON report with coverage: complete, three HTTP fixture attempts, and one
missing image resource attributed to both https://museum.example/c1 and
https://museum.example/c2. imageContent and fullConformance remain unchecked.
These synthetic results do not establish customer usage, source permission or demand.
See PRODUCT.md and VALIDATION.md for development scope, test receipts and limitations. The package pins Apify 4.0.1 and Pydantic 2.11.9; the output schemas are generated from the report model.