image-to-webp
Convert images in almost any format to WebP, from Python or the command line.
- Over 70 input formats with Pillow alone: PNG, JPEG, GIF, BMP, TIFF, ICO/ICNS, TGA, PSD, JPEG 2000, PCX, PPM/PGM/PBM, DDS, SGI, QOI, WebP, …
- More formats through optional installs: HEIC/HEIF/AVIF, camera RAW (CR2, CR3, NEF, ARW, DNG, …) and SVG
- Applies EXIF rotation, keeps transparency, and turns animated GIF/APNG into animated WebP
- Converts files in parallel and writes each output in one step, so a failed run never leaves a half-written file
Install
pip install image-to-webp # core formats (Pillow only)
pip install "image-to-webp[all]" # + HEIC/AVIF, RAW and SVG
| Optional install | Adds | Package |
|---|---|---|
[heif] |
HEIC / HEIF / AVIF | pillow-heif |
[raw] |
Camera RAW | rawpy |
[svg] |
SVG / SVGZ | cairosvg (also needs the system cairo library: brew install cairo or apt install libcairo2) |
Requires Python 3.9+.
Library usage
from image_to_webp import convert_file, convert_bytes, convert_image, convert_many, ConversionOptions
# One file -> photo.webp next to it (returns the output Path)
convert_file("photo.heic")
convert_file("photo.png", "out/photo.webp", quality=90)
# In memory
webp_bytes = convert_bytes(png_bytes, lossless=True)
webp_bytes = convert_bytes(open("img.cr2", "rb"), filename="img.cr2") # filename hints RAW decoding
webp_bytes = convert_image(pil_image, quality=75)
# Reusable settings
opts = ConversionOptions(quality=85, method=6, max_size=(2560, 2560), keep_metadata=True)
convert_file("big.tif", options=opts)
# Batch: files and/or directories, in parallel
summary = convert_many(["images/", "extra.gif"], output_dir="webp", recursive=True,
jobs=4, on_result=lambda r: print(r.status, r.src))
print(summary.converted, summary.skipped, summary.failed, summary.bytes_saved)
API reference
| Name | Description |
|---|---|
convert_file(src, dst=None, options=None, *, overwrite=True, **opts) -> Path |
Convert one file. dst defaults to src with a .webp extension. |
convert_bytes(data, options=None, *, filename=None, **opts) -> bytes |
Convert bytes or a binary file object. |
convert_image(image, options=None, **opts) -> bytes |
Encode a PIL.Image.Image. |
convert_many(inputs, output_dir=None, options=None, *, recursive, overwrite=False, delete_original, jobs, on_result, **opts) -> BatchSummary |
Convert many files. One failed file doesn't stop the run. |
plan_conversions(inputs, output_dir=None, *, recursive, overwrite) |
Preview what convert_many would do (dry run). |
ConversionOptions(quality=80, lossless=False, method=4, max_size=None, keep_metadata=False) |
Encoder settings, checked when created. |
ConversionResult / BatchSummary |
Results for each file and for the whole batch. |
supported_extensions(), is_supported(path), plugin_status() |
See which formats work in the current environment. |
open_image(source, filename=None) |
Open any supported input as a PIL image. |
ImageToWebPError → UnsupportedFormatError, ConversionError |
Exceptions. |
**opts accepts the same keywords as ConversionOptions (quality, lossless, method, max_size, keep_metadata).
The library never prints. Messages go to Python's logging under the image_to_webp logger.
Command line
image-to-webp photo.jpg # -> photo.webp next to source
image-to-webp ./images -r -o ./webp # recursive, mirror tree into ./webp
image-to-webp *.png --lossless
image-to-webp ./raw -q 85 --max-size 2560x2560 -j 8
image-to-webp ./images -r --dry-run
image-to-webp --list-formats # formats + optional plugin status
python -m image_to_webp --help # same tool
| Option | Description |
|---|---|
-r, --recursive |
Search directories recursively |
-o, --output DIR |
Output directory (default: next to source) |
-q, --quality N |
Quality 0–100 (default 80) |
--lossless |
Lossless encoding |
-m, --method N |
Compression effort 0–6 (default 4) |
--max-size WxH |
Downscale to fit, keeping aspect ratio |
--keep-metadata |
Keep EXIF and ICC profile |
--overwrite |
Overwrite existing .webp (default: skip) |
--delete-original |
Delete source after successful conversion |
-j, --jobs N |
Parallel workers (default: CPU count) |
--dry-run |
Show what would be converted |
-v, --verbose |
Print each file |
--list-formats |
Show supported formats and plugin status |
Exit code is 0 on success, 1 if any file failed, 2 for invalid arguments.
The command is called
image-to-webp, notimg2webp, because libwebp already ships a tool namedimg2webp.
Development
./install.sh # .venv + editable install with all optional formats + link command to /usr/local/bin (macOS) or /usr/bin
./install.sh --uninstall # remove the linked command
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest
install.sh uses sudo only when the target directory isn't writable. Override with BIN_DIR=~/.local/bin or CMD_NAME=....
Releasing
- Update
__version__insrc/image_to_webp/__init__.pyandCHANGELOG.md. - Check the build locally:
python -m build && python -m twine check --strict dist/*
- Publish:
- Automated (recommended): set up PyPI trusted publishing for
stakmi/image-to-webpwith workflowpublish.ymland environmentpypi, then create a GitHub release taggedv<version>. - Manual:
python -m twine upload dist/*
- Automated (recommended): set up PyPI trusted publishing for
License
MIT
Metadata
Release files for image-to-webp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| image_to_webp-0.1.0.tar.gz | 16.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| image_to_webp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.4 kB
Release files / image_to_webp-0.1.0.tar.gz
| Download URL | image_to_webp-0.1.0.tar.gz |
|---|---|
| Size | 16.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
63a6f0d2219a12a0a4e5da73fccb908ef4cc068961b40659f23818ae53f57bc4
|
|
BLAKE2b-256 checksum How to use checksums |
c9174504647008756a1c3d3bb96953e2eed0889106b8a882dc5674afeab9a871
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 6, 2026.
Transparency logRelease files / image_to_webp-0.1.0-py3-none-any.whl
| Download URL | image_to_webp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2a75c1c19085f402716733f3d971c25b8a88144ec82e8d6198160c7e273d25aa
|
|
BLAKE2b-256 checksum How to use checksums |
c86824ab787f68700f608b44ebe73f9a132a3860ff3f6bd4b72ab04a43df5fe2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 6, 2026.
Transparency log