skivvy
Declarative integration testing for HTTP APIs.
Skivvy is a tiny, opinionated CLI tool that lets you write API tests as plain JSON files and run them from the terminal. No JS hooks. No GUI. No snapshots. You declare what you expect, and skivvy tells you if reality disagrees — with readable diffs when it does.
It works with REST, GraphQL, and anything else that speaks HTTP. It's built for CI/CD pipelines but just as useful on your local machine.
The core idea
Most API testing tools push you toward one of two bad patterns: writing imperative code in a language you didn't ask for, or snapshot-testing entire responses so every unrelated field change breaks your suite. Skivvy takes a different approach — assert only what you care about. Check the status code, a single field, a subset of a nested object, or the length of a list. Ignore the rest.
{ "url": "/api/items", "response": { "results": [{ "name": "Widget42" }] } }
That's it. That's a test.
This works especially well with deeply nested responses like GraphQL, where you can match subsets without caring about the full envelope:
{
"url": "/graphql",
"method": "post",
"body": { "query": "{ posts { id title author { name } tags } }" },
"match_subsets": true,
"match_every_entry": true,
"response": {
"errors": null,
"data": {
"posts": [{ "author": { "name": "$text" }, "tags": "$len_gt 0" }],
"totalCount": "$gt 0"
}
}
}
Every post must have a named author and at least one tag. No GraphQL errors. You don't have to spell out the full response shape.
Matchers can reach beyond string comparison — they can enforce constraints across entire collections:
{
"url": "/users/all",
"match_every_entry": true,
"response": {
"images": [{ "thumbnail": "$valid_url", "id": "$unique" }]
}
}
Every user's thumbnail is a live, reachable URL. Every ID is unique across the whole list. Two invariants over an arbitrarily large dataset, in a few lines of JSON.
What you get
- All the HTTP plumbing you'd expect — every verb, headers, cookies, file uploads, form data, response chaining via variables and brace expansion
- Subset matching — assert against deeply nested fields without specifying the full path
- Built-in matchers —
$contains,$regexp,$len,$gt,$between,$valid_url,$date,$store/$fetch, approximate values with$~, and more — plus automatic negation ($!contains) for all of them (full list) - Custom matchers — drop a Python file with a
match(expected, actual)function into a directory and it just works - Readable diffs — when a test fails, you see exactly what went wrong in a human-friendly format, with multiple diff styles to choose from
- Flexible configuration — per-test overrides, environment configs, CLI flags, env vars, with a clear precedence order and sane defaults (full list)
- Setup & teardown — use directory naming and include/exclude filters to control execution order
- Deterministic execution — serial by default, predictable every time
Check out the playground for trying it from the comfort of your browser. It provides a basic unix shell, and allows you to edit and run test suites that examplify the list above. It also contains a running server, with responses you can edit as well.
Install
uvx skivvy --version # no install needed with uv
pipx run skivvy --version # or pipx
pip install skivvy # or plain pip
docker run --rm hyrfilm/skivvy:examples # or docker
Reference
- Settings — all configuration options with defaults and descriptions
- Matchers — all built-in matchers with usage examples
- CLI help also includes
skivvy --help-settingsandskivvy --help-matchersif you want to explore available knobs before enabling more logging
MIT license.
Release files for skivvy 0.811
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| skivvy-0.811.tar.gz | 31.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| skivvy-0.811-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.9 kB
Release files / skivvy-0.811.tar.gz
| Download URL | skivvy-0.811.tar.gz |
|---|---|
| Size | 31.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc0a31a2183d1f9108d16dbed93a60c92a832d5e08f3d183efa79add3cdf7413
|
|
BLAKE2b-256 checksum How to use checksums |
ed8a43aebf355c9b231a1b97808871317e399de7504b4f9f81624f27de692f97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 16, 2026.
Transparency logRelease files / skivvy-0.811-py3-none-any.whl
| Download URL | skivvy-0.811-py3-none-any.whl |
|---|---|
| Size | 37.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0da2b539655fb568fdab72812fab0cc19c8a321faf41350d1a325001ab189234
|
|
BLAKE2b-256 checksum How to use checksums |
01360070a486ac2c64ff2bd6ed85a3835135328c1ca3e2816b61e95c3c6b73e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 16, 2026.
Transparency log