# Find a Matching Chainguard Image Using the API

URL: https://edu.chainguard.dev/chainguard/migration/image-matcher/find-match.md
Last Modified: May 26, 2026
Tags: Chainguard Images, Migration, SBOM, API

How to call the Chainguard Image Matcher API with an existing SBOM to find the closest Chainguard image equivalent.

This guide walks through calling the Chainguard Image Matcher API to find the best Chainguard equivalent for an existing container image. It assumes you already have an SBOM for the image you want to migrate.
For background on how the matcher works and how it scores recommendations, see Image Matcher Overview.
Prerequisites Before getting started, you will need:
An SBOM for your source image in CycloneDX JSON format, with purl values on each component. SBOMs produced by Syft, Trivy, docker sbom, or cdxgen all work. chainctl installed and authenticated. jq installed. Your Chainguard organization UID. Retrieve it from Settings &gt; General in the Chainguard Console, or run chainctl iam groups list. Step 1: Reshape your SBOM for the API The Image Matcher API does not accept a raw CycloneDX document. It expects an sbomComponents array containing only distribution packages, with a small number of fields per component.
Save the following as build-request.jq:
{ parent_id: $org_id, dist_name: $dist, arch: $arch, sbom: { sbomComponents: [ .components[] | select(.purl != null and (.purl | startswith($purl_prefix))) | { type: .type, purl: .purl, name: .name, cpe: .cpe, bomref: .&#34;bom-ref&#34;, version: .version } ] } }This filter does three things: it selects only distribution packages by PURL prefix, drops irrelevant CycloneDX fields (licenses, properties, publisher, etc.) that the API ignores, and renames bom-ref to bomref to match the API&rsquo;s expected field name.
Run it with values for your source distribution:
export ORG_ID=&#34;&lt;your-org-uid&gt;&#34; jq \ --arg org_id &#34;$ORG_ID&#34; \ --arg dist &#34;debian&#34; \ --arg arch &#34;x86_64&#34; \ --arg purl_prefix &#34;pkg:deb/&#34; \ -f build-request.jq \ your-image.cdx.json \ &gt; request.jsonUse the following table to match dist, arch, and purl_prefix to your source image:
Distribution dist purl_prefix Debian debian pkg:deb/ Ubuntu ubuntu pkg:deb/ Alpine alpine pkg:apk/ Red Hat / RHEL redhat pkg:rpm/ SUSE suse pkg:rpm/ Amazon Linux amazon-linux pkg:rpm/ Pass dist_version as an additional --arg if you know the exact version (for example, 12 for Debian 12, 24.04 for Ubuntu 24.04). This is optional, but it narrows the candidate set and improves results.
Example request body The reshaped request body for a Debian nginx image looks like the following:
{ &#34;parent_id&#34;: &#34;&lt;your-org-uid&gt;&#34;, &#34;dist_name&#34;: &#34;debian&#34;, &#34;arch&#34;: &#34;x86_64&#34;, &#34;sbom&#34;: { &#34;sbomComponents&#34;: [ { &#34;type&#34;: &#34;library&#34;, &#34;purl&#34;: &#34;pkg:deb/debian/nginx@1.28.0-1~bookworm?arch=amd64&amp;distro=debian-12&#34;, &#34;name&#34;: &#34;nginx&#34;, &#34;cpe&#34;: &#34;cpe:2.3:a:nginx:nginx:1.28.0-1~bookworm:*:*:*:*:*:*:*&#34;, &#34;bomref&#34;: &#34;pkg:deb/debian/nginx@1.28.0-1~bookworm?arch=amd64&amp;distro=debian-12&amp;package-id=...&#34;, &#34;version&#34;: &#34;1.28.0-1~bookworm&#34; }, { &#34;type&#34;: &#34;library&#34;, &#34;purl&#34;: &#34;pkg:deb/debian/libssl3@3.0.17-1~deb12u3?arch=amd64&amp;distro=debian-12&amp;upstream=openssl&#34;, &#34;name&#34;: &#34;libssl3&#34;, &#34;cpe&#34;: &#34;cpe:2.3:a:libssl3:libssl3:3.0.17-1~deb12u3:*:*:*:*:*:*:*&#34;, &#34;bomref&#34;: &#34;pkg:deb/debian/libssl3@3.0.17-1~deb12u3?arch=amd64&amp;distro=debian-12&amp;package-id=...&#34;, &#34;version&#34;: &#34;3.0.17-1~deb12u3&#34; } ] } } Step 2: Call the API Run the following:
curl -sS \ -H &#34;Authorization: Bearer $(chainctl auth token)&#34; \ -H &#34;Content-Type: application/json&#34; \ -X POST &#34;https://console-api.enforce.dev/image-recommendation/v1/match&#34; \ -d @request.json Step 3: Interpret the results The response contains a ranked images array, ordered from highest to lowest probabilityScore. For example:
{ &#34;images&#34;: [ { &#34;imageRef&#34;: &#34;cgr.dev/&lt;your-org&gt;/nginx:latest&#34;, &#34;probabilityScore&#34;: 98.57, &#34;satisfiedCount&#34;: 6, &#34;totalRequired&#34;: 86, &#34;coverage&#34;: 0.07, &#34;satisfiedPackages&#34;: [...], &#34;missingPackages&#34;: [...], &#34;extraPackages&#34;: 14 } ], &#34;totalExternalPackages&#34;: 143, &#34;requiredApks&#34;: [&#34;nginx-mainline&#34;, &#34;libgcc&#34;, &#34;glibc&#34;, ...], &#34;unmatchedExternalPkgs&#34;: [&#34;adduser&#34;, &#34;apt&#34;, &#34;base-files&#34;, ...] }Key fields to check:
probabilityScore: the matcher&rsquo;s confidence estimate (0–100). A score of 90 or above is a strong match. Scores in the 50–70 range warrant human review. imageRef: the full OCI reference for the recommended image. Use the final path segment and tag (for example, nginx:latest) when displaying this to users or comparing against the Chainguard image catalog. Keep the full reference if you intend to pull the image. missingPackages: packages from your SBOM that are not present in the recommended image. Some missing packages are expected: Chainguard images are intentionally minimal. Review this list to identify any packages your application requires at runtime. unmatchedExternalPkgs: source packages the matcher could not map to any Chainguard APK. A long list is normal for general-purpose distributions like Debian, where many entries are build-time tools or base-image scaffolding. Low coverage with a high probabilityScore You may notice that coverage (the fraction of required APKs present in the image) can be low even when probabilityScore is high. This is expected behavior. Package weights are not uniform: the package that defines an image&rsquo;s purpose carries a weight orders of magnitude larger than common shared dependencies. A probabilityScore of 98 with coverage of 0.07 means the matcher is highly confident it has found the right image, even though many low-level library names differ between your source distribution and Chainguard&rsquo;s APK catalog.
Learn more about the probability score in the Image Matcher overview.
Reviewing multiple candidates The API returns up to 10 candidates by default. For common stacks the top result is typically correct, but it is good practice to review the second and third results, particularly when:
The top score is below 90. The top two scores are within a few points of each other. The source image is not a well-known public image. To request more or fewer candidates, add a count field to your request body. To suppress weak matches, add a threshold field (default: 50.0).

