---
title: "Compare two ready snapshots the account may read, captured at the same width and check mode, at no charge: no site is opened, so nothing is drawn from the free allowance, a balance or an allowance, and a dry run quotes 0.00. Needs an account-bound key. The compare is recorded under that account: saved while it is Pro (at most 50 saved), otherwise removed with its files 30 days after creation. The cap, a missing or unready snapshot and an incomparable pair are refused before the job starts. Answers the POST /jobs shape plus compareId, which equals jobId."
method: POST
path: "/compares"
---

# Compare two ready snapshots the account may read, captured at the same width and check mode, at no charge: no site is opened, so nothing is drawn from the free allowance, a balance or an allowance, and a dry run quotes 0.00. Needs an account-bound key. The compare is recorded under that account: saved while it is Pro (at most 50 saved), otherwise removed with its files 30 days after creation. The cap, a missing or unready snapshot and an incomparable pair are refused before the job starts. Answers the POST /jobs shape plus compareId, which equals jobId.

`POST /compares`

## Request

- Base URL: `https://app.difficat.com/api/v1`
- URL: `https://app.difficat.com/api/v1/compares`
- Auth: HTTP bearer

## Request body

- StoredCompareRequest
  - `snapshotId` string, required: Site A, a snapshot the account owns or may read.
  - `otherSnapshotId` string, required: Site B, captured at the same width and check mode.
  - `threshold` number: Visual diff tolerance as a fraction from 0 (exact) to 1: 0.05 means 5%. The web app takes a percentage; the API does not.
  - `maxPriceUsd` number: Optional spend cap in USD. A job priced above it is refused before any challenge, so an auto-paying client is never asked for more.
  - `dryRun` boolean: Quote only: runs every check and the pricing, then answers the page count and exact amount. Nothing is charged, the free allowance is not used and no job starts; the same body without dryRun is priced from the retained result.
  - `callbackUrl` string, uri: Optional https URL (port 443) that receives one JSON POST when the job ends (jobId, status, summary, links; never the jobToken), a nudge to read the job. Public hosts only, no redirects followed, retried twice; delivery never affects the job.

## Response `200`

Dry run: the quote, 0.00, with no job started

- DryRunResponse
  - `dryRun` true, required
  - `pagesPriced` integer, required: The page count the job would be charged for.
  - `amount` string, required: The exact amount the same body without dryRun is charged.
  - `currency` 'USD', required
  - `allowanceRemaining` integer: For a request with no credential and no prepaid key: the comparison pages the client address may still run free today, read without consuming any.
  - `freeCovers` boolean: For a request with no credential and no prepaid key: true when the same body sent now without dryRun would run free (the priced pages are within allowanceRemaining and the shared daily pool).

## Other responses

- `201`: Job started
- `400`: Invalid input, a blocked callbackUrl, or a pair not in the snapshot
- `401`: No key, a share token, or a key with no active account behind it (credential_required or account_key_required)
- `404`: No such snapshot, or no access to it
- `409`: The account holds its compare cap (compare_limit), a snapshot is still capturing (snapshot_not_ready), or the two differ in width or check mode (snapshots_incomparable); nothing is charged
- `429`: The running-job limit or the hourly limit of unpaid requests for this address (Retry-After)
- `500`: The start failed; a settled charge is final
- `503`: Snapshots are not available on this deployment

---

[API](https://skmtc.dev/difficat/apis/difficat-agent-api.md) · [All operations](https://skmtc.dev/difficat/apis/difficat-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/difficat/apis/difficat-agent-api/revisions/49c291e77659?raw)
