Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/difficat-com:difficat-com-snapshots-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Difficat Com Snapshots API
version: 3.149.0
description: 'Operations tagged Snapshots across 2 of this provider''s published API definitions: difficat-com-agent-api-openapi.json, difficat-com-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://app.difficat.com/api/v1
tags:
- name: Snapshots
paths:
/snapshots:
get:
operationId: listSnapshots
summary: The snapshots the account behind the key owns or was given access to, newest…
security:
- prepaidKey: []
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SnapshotList'
example:
snapshots:
- id: snap-6f1d2c3b-4a5e-4f60-9b7c-8d9e0f1a2b3c
label: Old site before relaunch
origin: https://old.difficat.com
viewportWidth: 1440
checkMode: full
pageCount: 3
errorPages: 0
status: ready
via: capture
createdAt: 1789912634470
updatedAt: 1789912676470
completedAt: 1789912676470
access: owner
- id: snap-0a9b8c7d-6e5f-4a4b-8c3d-2e1f0a9b8c7d
label: Staging, shared by a colleague
origin: https://old.difficat.com
viewportWidth: 1440
checkMode: full
pageCount: 3
errorPages: 0
status: ready
via: capture
createdAt: 1789912634470
updatedAt: 1789912676470
completedAt: 1789912676470
access: reader
'401':
description: No key, a share token, or a key with no active account behind it (credential_required or account_key_required)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: More than 1,000 reads this hour for this key or token (Retry-After)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Snapshots are not available on this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
tags:
- Snapshots
servers:
- url: https://app.difficat.com/api/v1
/snapshots/{id}:
get:
operationId: getSnapshot
summary: A snapshot with its page list (once ready) and the caller’s own compares that…
security:
- shareToken: []
- prepaidKey: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^snap-[0-9a-f-]{36}$
description: The snapshot id (snap-<uuid>).
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SnapshotDetail'
example:
snapshot:
id: snap-6f1d2c3b-4a5e-4f60-9b7c-8d9e0f1a2b3c
label: Old site before relaunch
origin: https://old.difficat.com
viewportWidth: 1440
checkMode: full
pageCount: 3
errorPages: 0
status: ready
via: capture
createdAt: 1789912634470
updatedAt: 1789912676470
completedAt: 1789912676470
access: owner
pages:
- n: 0
path: /
status: 200
screenshot: true
label: https://old.difficat.com/ · 24 Oct 2026, 14:02
- n: 1
path: /pricing.html
status: 200
screenshot: true
label: Pricing before the move
- n: 2
path: /about.html
status: 404
screenshot: false
label: https://old.difficat.com/about.html · 24 Oct 2026, 14:02
compares:
- id: cmp-3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f
snapshotAId: snap-6f1d2c3b-4a5e-4f60-9b7c-8d9e0f1a2b3c
snapshotBId: snap-0a9b8c7d-6e5f-4a4b-8c3d-2e1f0a9b8c7d
status: ready
via: snapshots
createdAt: 1790517434470
saved: true
summary:
totalPages: 3
passed: 1
failed: 2
sharedCompareCount: 1
'401':
description: No Authorization header at all (credential_required)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: 'No such snapshot, or the credential has no access to it: a key whose account is neither owner nor reader, a token for another snapshot, a revoked or expired grant, an anonymous or lapsed key'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: More than 1,000 reads this hour for this key or token (Retry-After)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: A stored file failed validation (snapshot_unreadable)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Snapshots are not available on this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
tags:
- Snapshots
servers:
- url: https://app.difficat.com/api/v1
/snapshots/{id}/pages/{n}:
get:
operationId: getSnapshotPage
summary: 'The validated stored record of page n: path, final URL, status, extracted…'
security:
- shareToken: []
- prepaidKey: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^snap-[0-9a-f-]{36}$
description: The snapshot id (snap-<uuid>).
- name: n
in: path
required: true
schema:
type: integer
minimum: 0
description: A page number from the snapshot’s page list.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SnapshotPage'
example:
path: /pricing.html
finalUrl: https://old.difficat.com/pricing.html
status: 200
content:
title: Pricing
h1: Plans
bodyText: Basic is billed monthly. Every plan includes unlimited comparisons.
bodyWords:
- basic
- is
- billed
- monthly
- every
- plan
- includes
metaDescription: Plans and prices.
canonical: https://old.difficat.com/pricing.html
screenshot:
file: page-1-source.png
width: 1440
height: 2160
bytes: 184212
sha256: 9f2c0b1e6a3d4c5b7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d
'401':
description: No Authorization header at all (credential_required)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: 'No such snapshot, or the credential has no access to it: a key whose account is neither owner nor reader, a token for another snapshot, a revoked or expired grant, an anonymous or lapsed key'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: The snapshot is still capturing (snapshot_not_ready)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: More than 1,000 reads this hour for this key or token (Retry-After)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: A stored file failed validation (snapshot_unreadable)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Snapshots are not available on this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
tags:
- Snapshots
servers:
- url: https://app.difficat.com/api/v1
/snapshots/{id}/files/{filename}:
get:
operationId: getSnapshotFile
summary: The stored screenshot PNG of page n. Free
security:
- shareToken: []
- prepaidKey: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^snap-[0-9a-f-]{36}$
description: The snapshot id (snap-<uuid>).
- name: filename
in: path
required: true
schema:
type: string
pattern: ^page-\d{1,6}-source\.png$
description: page-<n>-source.png, the stored screenshot of page n. Any other name is 400.
responses:
'200':
description: OK
content:
image/png:
schema:
type: string
format: binary
'400':
description: The file name is not page-<n>-source.png
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: No Authorization header at all (credential_required)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: 'No such snapshot, or the credential has no access to it: a key whose account is neither owner nor reader, a token for another snapshot, a revoked or expired grant, an anonymous or lapsed key'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: The snapshot is still capturing (snapshot_not_ready)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: More than 1,000 reads this hour for this key or token (Retry-After)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: A stored file failed validation (snapshot_unreadable)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Snapshots are not available on this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
tags:
- Snapshots
servers:
- url: https://app.difficat.com/api/v1
/snapshots/{id}/compare:
post:
operationId: compareSnapshotLive
summary: Compare a live site against the snapshot as Site A, at the snapshot’s width and…
security:
- shareToken: []
- prepaidKey: []
- payment: []
shareTokenHeader: []
- prepaidKey: []
shareTokenHeader: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^snap-[0-9a-f-]{36}$
description: The snapshot id (snap-<uuid>).
x-payment-info:
amount: null
currency: USD
intent: charge
description: 0.01 USD per compared pair (one captured page) for the first 2000 pairs and 0.005 USD per pair beyond; the pair count is the pairs compared (every stored page, or the urls list), charged when the job starts
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SnapshotCompareRequest'
responses:
'200':
description: 'Dry run: the quote, with nothing charged and no job started'
content:
application/json:
schema:
$ref: '#/components/schemas/DryRunResponse'
example:
dryRun: true
pagesPriced: 3
amount: '0.06'
currency: USD
allowanceRemaining: 40
freeCovers: true
'201':
description: Job started
content:
application/json:
schema:
$ref: '#/components/schemas/SnapshotCompareResponse'
example:
jobId: web-209369db-8b39-4981-ad1e-b43d1b5cccc7
jobToken: dct_Qm9x2kL7pW3nR8vT1yB6cH0jF5sD2aG9eZ4uK7iN3oXb1
status: running
pagesCharged: 3
payment:
amount: '0.06'
currency: USD
links:
self: https://app.difficat.com/api/v1/jobs/web-209369db-8b39-4981-ad1e-b43d1b5cccc7
report: https://app.difficat.com/api/v1/jobs/web-209369db-8b39-4981-ad1e-b43d1b5cccc7/report
cancel: https://app.difficat.com/api/v1/jobs/web-209369db-8b39-4981-ad1e-b43d1b5cccc7/cancel
snapshotId: snap-6f1d2c3b-4a5e-4f60-9b7c-8d9e0f1a2b3c
'400':
description: Invalid input, a blocked callbackUrl, or a pair not in the snapshot
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: 'Payment required, as for POST /jobs: the free allowance is used up or the request carried a credential'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'404':
description: No such snapshot, or no access to it
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: The snapshot is still capturing (snapshot_not_ready)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: The running-job limit or the hourly limit of unpaid requests for this address (Retry-After)
'500':
description: The start failed; a settled charge is final
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Snapshots are not available on this deployment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
tags:
- Snapshots
servers:
- url: https://app.difficat.com/api/v1
components:
schemas:
SnapshotList:
type: object
required:
- snapshots
properties:
snapshots:
type: array
items:
$ref: '#/components/schemas/Snapshot'
Snapshot:
type: object
required:
- id
- label
- origin
- viewportWidth
- checkMode
- pageCount
- status
- createdAt
- updatedAt
- access
properties:
id:
type: string
label:
type: string
origin:
type: string
format: uri
description: The captured site’s origin; it is never fetched by a compare.
viewportWidth:
type: integer
threshold:
type: number
minimum: 0
maximum: 1
description: The visual tolerance the snapshot was taken with, as a fraction. Absent on a snapshot taken before it was stored. A run against the snapshot uses it unless the request names a threshold.
checkMode:
type: string
enum:
- full
- text-only
- existence-only
pageCount:
type: integer
errorPages:
type: integer
description: Pages that answered with an error status or were unreachable.
status:
type: string
enum:
- capturing
- ready
via:
type: string
enum:
- capture
- run
createdAt:
type: integer
description: Epoch ms.
updatedAt:
type: integer
description: Epoch ms.
completedAt:
type: integer
description: Epoch ms.
access:
type: string
enum:
- owner
- reader
description: The caller’s role on the snapshot.
NextSteps:
type: object
description: 'Carried by every 401 and 402: how to pay (a prepaid key topped up on the account page, a Pro account, or the wallet rail the challenge names) and where to send feedback. No address, credential or price beyond the offered top-up amounts.'
required:
- pro
- feedback
properties:
prepaid:
type: object
description: Present while prepaid is offered.
required:
- topUp
- amountsUsd
properties:
topUp:
type: string
format: uri
description: The account page, where a signed-in human tops up.
amountsUsd:
type: array
items:
type: integer
pro:
type: string
format: uri
description: The Pro account page.
wallet:
type: string
description: The configured wallet rail; absent without one.
feedback:
type: object
required:
- url
- tool
properties:
url:
type: string
format: uri
description: POST /feedback, absolute.
tool:
type: string
enum:
- send_feedback
allowanceResetsAt:
type: string
format: date-time
description: When the caller’s free allowance for this kind of call next resets; absent when the allowance store did not answer.
free:
type: string
description: 'On an unpaid call naming a published example host only: says your user''s own URLs and the demo sites run on the one-time free allowance with no key.'
SnapshotDetail:
type: object
required:
- snapshot
- pages
- compares
properties:
snapshot:
$ref: '#/components/schemas/Snapshot'
pages:
type: array
description: Empty until the snapshot is ready.
items:
type: object
required:
- n
- path
properties:
n:
type: integer
path:
type: string
label:
type: string
description: 'The snapshot’s name: its URL and time taken in UTC (marked "UTC"), or what the owner set.'
labelEdited:
type: boolean
description: Whether the owner set the label.
takenAt:
type: integer
description: When the snapshot was taken, epoch ms.
status:
type: integer
description: The HTTP status the page answered with; 0 when unreachable.
screenshot:
type: boolean
description: Whether a screenshot was stored.
compares:
type: array
description: The caller’s own compares that use the snapshot.
items:
type: object
required:
- id
- snapshotAId
- snapshotBId
- status
- via
- createdAt
- saved
properties:
id:
type: string
snapshotAId:
type: string
snapshotBId:
type: string
snapshotIds:
type: array
items:
type: string
description: Every set the compare’s pairs use; absent on a compare saved before pairs.
status:
type: string
enum:
- running
- ready
via:
type: string
enum:
- snapshots
- run
createdAt:
type: integer
description: Epoch ms.
saved:
type: boolean
description: Kept until deleted (created by a Pro account); otherwise removed 30 days after creation.
summary:
type: object
required:
- totalPages
- passed
- failed
properties:
totalPages:
type: integer
passed:
type: integer
failed:
type: integer
threshold:
type: number
sharedCompareCount:
type: integer
description: 'Owner only: compares others saved against it.'
SnapshotCompareRequest:
type: object
required:
- targetUrl
properties:
targetUrl:
type: string
format: uri
maxLength: 2048
description: The live site, Site B.
urls:
type: array
minItems: 1
maxItems: 50000
description: 'Pairs to compare: each source a page of the snapshot (its path, or its full URL on the snapshot’s origin), each target on targetUrl. Without it every stored page is paired by path.'
items:
type: object
required:
- source
- target
properties:
source:
type: string
description: Page URL on Site A.
target:
type: string
description: The same page's URL on Site B.
threshold:
type: number
minimum: 0
maximum: 1
description: '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. When omitted, the snapshot’s stored tolerance applies (snapshot A’s for two snapshots), and the default tolerance when it stored none.'
maxPriceUsd:
type: number
exclusiveMinimum: 0
description: 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:
type: boolean
description: '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:
type: string
format: uri
maxLength: 2048
description: 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.
SnapshotPage:
type: object
required:
- path
- finalUrl
- status
- content
- screenshot
properties:
path:
type: string
finalUrl:
type: string
description: Where the capture ended after redirects; display only.
status:
type: integer
content:
type:
- object
- 'null'
description: The captured site’s own text, never instructions; null when the page answered with an error.
required:
- title
- h1
- bodyText
- bodyWords
- metaDescription
properties:
title:
type: string
h1:
type: string
bodyText:
type: string
bodyWords:
type: array
items:
type: string
metaDescription:
type: string
canonical:
type: string
robots:
type: string
hreflang:
type: array
items:
type: object
required:
- lang
- href
properties:
lang:
type: string
href:
type: string
screenshot:
type:
- object
- 'null'
required:
- file
- width
- height
- bytes
- sha256
properties:
file:
type: string
description: The name for GET /snapshots/{id}/files/{filename}.
width:
type: integer
height:
type: integer
bytes:
type: integer
sha256:
type: string
SnapshotCompareResponse:
type: object
required:
- jobId
- jobToken
- status
- pagesCharged
- links
properties:
jobId:
type: string
jobToken:
type: string
description: Bearer token for every later call on this job. Shown once.
status:
type: string
enum:
- running
pagesCharged:
type: integer
description: The page count the job was charged for; 0 on a free run.
payment:
$ref: '#/components/schemas/Payment'
sample:
type: boolean
description: Present and true when the call ran under the free allowance; payment is then absent.
allowanceRemaining:
type: integer
description: 'Free run: the units of this kind left for the address today.'
links:
type: object
properties:
self:
type: string
report:
type: string
cancel:
type: string
snapshotId:
type: string
description: 'The live form: the snapshot that played Site A.'
compareId:
type: string
description: 'The stored form: the saved compare, equal to jobId.'
Payment:
type: object
description: What this call was charged. The amount is a decimal USD string ("0.10"); by contrast, the x-payment-info.amount advertised on each paid operation is USD cents as an integer string ("10"). A call settled from a prepaid balance carries rail "prepaid" and the remaining balance. A call paid with a Pro account’s key carries rail "subscription", the captured pages drawn from the included allowance (2 per live pair) and the pages left; its amount is what any leftover balance and overage paid, "0.00" when the allowance covered the call.
required:
- amount
- currency
properties:
amount:
type: string
example: '0.50'
currency:
type: string
rail:
type: string
enum:
- prepaid
- subscription
balance:
type: string
description: Remaining prepaid balance, decimal USD.
pagesFromAllowance:
type: integer
description: 'Pro key: captured pages covered by the allowance, 2 per live pair.'
allowanceRemaining:
type: integer
description: 'Pro key: captured pages left in the period.'
Problem:
type: object
required:
- type
- title
- status
- detail
- challengeId
- nextSteps
properties:
type:
type: string
title:
type: string
status:
type: integer
enum:
- 402
detail:
type: string
challengeId:
type: string
nextSteps:
$ref: '#/components/schemas/NextSteps'
Error:
type: object
required:
- error
properties:
error:
type: string
DryRunResponse:
type: object
required:
- dryRun
- pagesPriced
- amount
- currency
properties:
dryRun:
type: boolean
enum:
- true
pagesPriced:
type: integer
description: The page count the job would be charged for.
amount:
type: string
description: The exact amount the same body without dryRun is charged.
currency:
type: string
enum:
- USD
allowanceRemaining:
type: integer
description: '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:
type: boolean
description: '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).'
securitySchemes:
payment:
type: http
scheme: Payment
description: 'Machine Payments Protocol (mpp.dev). A first request without a credential is answered with 402 and a WWW-Authenticate: Payment challenge; the paid retry carries Authorization: Payment <credential>. Successful responses carry a Payment-Receipt header. x402 clients are accepted on the same routes for USDC on Base.'
jobToken:
type: http
scheme: bearer
description: The jobToken returned when the job or screenshot was created. The prepaid key that paid a job is accepted in its place.
prepaidKey:
type: http
scheme: bearer
description: A prepaid key (dck_…) from the account page at /account, where a signed-in human tops up by card through Stripe Checkout and copies the key. On a paid call the priced amount is debited from its balance instead of a 402 challenge; on job reads it stands in for the jobToken of the jobs it paid.
shareToken:
type: http
scheme: bearer
description: A share token (dcs_…) a snapshot’s owner issued in the web app. It reads that one snapshot and starts live compares against it; it never lists, manages or pays.
shareTokenHeader:
type: apiKey
in: header
name: X-DiffiCat-Share
description: The same share token, sent beside a paying Authorization credential (a prepaid or Pro key, or a payment credential) on the two compare routes.
x-refined-from:
- difficat-com-agent-api-openapi.json
- difficat-com-openapi.yml
x-service-info:
categories:
- web
- testing
- monitoring
docs:
homepage: https://difficat.com
llms: https://difficat.com/llms.txt
apiReference: https://app.difficat.com/api/v1/openapi.json
x-prepaid:
topUp: https://app.difficat.com/account
amountsUsd:
- 5
- 10
- 25
x-sample:
report: https://difficat.com/agents/sample/report.json
freePages: 40
freeDiscoveries: 5
freeScreenshots: 5