Sizy is an image CDN that fronts pictures you already host. Point a bucket at your origin, then ask for a size in the query string — nothing is migrated, nothing is installed, and the URL you write is the whole API surface.
$0.40 per 1,000 transforms · 5,000 free every month · delivery is never billed

Fill the box and crop whatever hangs over the edge. The default, and what a thumbnail wants.
center
https://sizy.io/sizy/samples/img.jpg?width=480&height=320&fit=coverNot a mock-up and not a screenshot. That image was fetched from sizy.io by your browser using the URL under it, and if nobody had asked for that exact combination before, it was rendered while you were reading this sentence.
Your originals stay on whatever already serves them — S3, a CDN, a folder on a box. A bucket is two fields: a name and the origin to fetch from. There is no upload step, no migration and no second copy of your library to keep in step.
No SDK, no build plugin, no loader, no framework integration. A plain GET with a query string on it, which means every language, every template engine and every CMS already supports it.
Serving an image is free at any volume, and always has a fair-use edge published rather than implied — about 500 deliveries per transform a month, which is between two and twenty times what a busy site does.
Fit
Ask for 260×340 — a portrait box over a landscape photograph, which is the awkward case — and fit decides what happens to the difference. cover, contain and fill all hand back the box you asked for and disagree about what to sacrifice for it. inside and outside refuse to distort or crop anything, and hand back a different size instead.

fit=coverreturns 260×340
The box, cropped to it.

fit=containreturns 260×340
The box, padded to it.

fit=fillreturns 260×340
The box, stretched to it.

fit=insidereturns 260×173
Smaller than the box.

fit=outsidereturns 510×340
Bigger than the box.
All five are live URLs against the same source, and all five are drawn at 40% of the pixels they came back as — so their sizes are comparable to each other rather than each one fitted to its own box, which would hide the only difference that matters between the last two.
Position
fit=cover throws away whatever hangs over the edge, and by default it throws it away symmetrically. position moves the anchor to one of nine places, which is usually the difference between a thumbnail you can ship and one you have to crop by hand. Three of the nine are below, cropping a 240×420 portrait out of a landscape photograph — the case where the parameter has the most to say. Multi-word values need encoding: position=left%20top.
gravity is the same idea under an older name, compass spellings only, and it is read only when position is absent. Reach for position in anything new.



The URL
Not the popular ones — all of them. Nine, plus the short spellings w and h, with the default beside each one so you can tell what happens when you leave it out. If a table below and the docs ever disagree, the docs are the ones kept in step with the code.
Give one and the other follows the source aspect ratio. Give both and fit decides the rest.
widthdefault: The source widthA whole number of pixels
Alias: w. They are the same parameter, and the short form wins if you send both — w=400&width=800 renders 400. Send one.
heightdefault: The source heightA whole number of pixels
Alias: h, with the same rule — h beats height when both appear.
Only read when there is a size to act on — with neither dimension set, all four of these do nothing.
fitdefault: covercover · contain · fill · inside · outside
How the image is made to fit the box you asked for. The five below are the whole set.
positiondefault: centercenter · top · right top · right · right bottom · bottom · left bottom · left · left top, and the compass spellings of the same nine
Where the crop is anchored when fit=cover has to throw part of the image away. Multi-word values need encoding: position=left%20top.
gravitydefault: centerThe nine compass names only
A compatibility alias for position. position is read first and gravity only when it is absent — prefer position in new code.
backgrounddefault: Opaque blackAny CSS colour — `#ffffff`, `white`, `rgba(0,0,0,0.5)`
What fills the space when fit=contain leaves some. A colour the parser does not recognise fails the render with a 500, so encode the # as %23.
You need none of these on an ordinary day. The default behaviour is the interesting part, below.
formatdefault: Negotiated from `Accept`, then the source formatavif · webp · jpeg · png
Pins the container. An explicit format always beats negotiation, so reach for it when a URL has to return the same bytes to every client. Anything else is a 400.
withoutEnlargementdefault: falsetrue · 1 · yes · false · 0 · no, or the bare parameter
Never scale past the source dimensions. Sending it with no value at all — ?withoutEnlargement — reads as true.
fastShrinkOnLoaddefault: falsetrue · 1 · yes · false · 0 · no, or the bare parameter
Take the JPEG decoder's shrink-on-load path on a large downscale. Faster and much easier on memory, at a little quality.
Format
Resizing a photograph is usually the second-largest win available on it. The largest is the container: a modern encoder on the same pixels routinely beats the resize. Sizy reads the Accept header the browser already sends and picks, with no parameter from you.
Each format is stored as its own object and every response carries Vary: Accept, so nothing in between can hand an AVIF to something that cannot decode one. It is still one billable transform per URL — you are not charged again because a visitor turned up in a different browser.
Two sources are never converted at all. An animated GIF would lose every frame but the first, and rasterising an SVG is a decision rather than an optimisation.
image/avif,image/webp,*/*image/webp,*/**/*A client sending only */* gets the source format on purpose: plenty of things that are not browsers send that and cannot decode AVIF. Set ?format= when a URL has to return the same bytes to everyone.
Integration
This is the part of the page where a product like this usually shows you an install command. There isn't one. A responsive image is the markup you already know with a different hostname in it, and if you want a helper, the helper is three lines and you should write it rather than depend on it.
<!-- One image, three sizes, no build step. -->
<img
src="https://sizy.io/sizy/photos/beach.jpg?w=600"
srcset="https://sizy.io/sizy/photos/beach.jpg?w=600 600w,
https://sizy.io/sizy/photos/beach.jpg?w=1200 1200w,
https://sizy.io/sizy/photos/beach.jpg?w=1800 1800w"
sizes="(max-width: 640px) 100vw, 600px"
alt="The beach at Zuma"
/>
<!-- A square thumbnail of the same file, anchored so the faces live. -->
<img
src="https://sizy.io/sizy/photos/team.jpg?w=96&h=96&fit=cover&position=top"
width="96" height="96" alt="The team"
/>
<!-- And in a stylesheet, because it is still only a URL. -->
<div style="background-image:
url('https://sizy.io/sizy/photos/hero.jpg?w=1600&h=500')"></div>// The entire client library, if you want one.
const BUCKET = "https://sizy.io/sizy";
export const img = (path, opts = {}) =>
`${BUCKET}/${path}?` + new URLSearchParams(opts);
img("photos/beach.jpg", { width: 400, height: 300 });
// https://sizy.io/sizy/photos/beach.jpg?width=400&height=300
img("photos/team.jpg", { w: 400, h: 300, position: "top" });
// A crop anchored to the top, so the faces survive it.
// There is no key on an image URL and no request to make from a server:
// a bucket is a public front for a source you already serve publicly.What counts
One number is billed, so it is worth being exact about what increments it — including the two cases where you would reasonably expect it to and it does not.
With neither width nor height present, fit, position, gravity and background have nothing to act on and the image comes back at its original dimensions.
Sizy reads the Accept header the browser already sends: AVIF where it is offered, then WebP, then the source format. A client sending only */* gets the source — plenty of non-browser clients send that and cannot decode AVIF.
Each format is stored as its own object and every response carries Vary: Accept, so no cache in between can hand an AVIF to something that cannot read it. You are still billed once for the URL, not once per browser your visitors use.
Anything outside the tables above is dropped before the image is identified, so ?utm_source=newsletter will not quietly render and bill a second copy of the same picture.
v, x-id and the X-Amz-* signing parameters are forwarded to your source so a bucket can front a presigned S3 URL. They are forwarded but not part of the identity, so a signature that changes every hour does not re-render anything.
An image is identified by its bucket, its path and the parameters above. The first request renders and stores it; every later one that month is answered from the stored copy. The URL never changes, so it is safe to hard-code, to cache and to put in an email.
A render is kept until the end of the calendar month it was made in, or seven days, whichever is longer. A URL you serve every month costs one transform a month — not one forever — and a URL nobody asks for again costs nothing after it clears.
Rendered copies are cleared at the end of the calendar month they were made in — or after seven days, whichever is longer, so an image rendered on the 30th is not swept away the next morning. The next request re-renders it, and that is the one more transform. The upshot is that your bill tracks the images people are actually looking at this month, and a page you retired in March stops costing anything in April without you doing a thing.
Price
No monthly fee, no seats, no minimum, no storage line and no bandwidth line. The first 5,000 transforms every month are free on every account, including accounts that pay. Below is the same month priced on all six, which is harder than it sounds — every one of these bills on a different axis.
Pay as you go — $0.40 per 1,000 transforms after 5,000 free — delivery is not billed at any volume
Pay as you go — $0.50 per 1,000 unique transformations after 5,000 free
Pro — $20/mo including $20 of credit, then $0.05 per 1,000 transformations plus 10 cache units an image
Starter — 195.6 credits — one per 500 renders, one per GB delivered, two per GB stored
Lite — 152.6GB delivered and 1.5GB stored — transformations are not billed separately
Plus — 174.1 credits — one credit is 1,000 transformations, 1GB stored or 1GB delivered
Rates read from each vendor's published pricing on 2026-09-03, always at the cheapest plan that covers the usage rather than the one they would sell you. Storage is modelled at one stored object per transform, because three of these bill for it and leaving it out would flatter them. Cloudflare Images · Cloudinary · imgix · ImageKit · Vercel
The shape this is best at: an agency running twenty client sites — 20,000 images and 2 million views a month — is $6 here against $99 on Cloudinary. Rates checked 2026-09-03; the full working is on the pricing page.
A very large catalogue served lightly. ImageKit meters bandwidth, not transforms. A quarter of a million distinct images with modest traffic against them is us charging per image for something they charge per gigabyte for — and the gigabytes are small.
You are already paying for Vercel. Their image optimisation comes with the hosting rather than being something you buy. The comparison includes that $20 because there is no way to get the feature without it; if it is already on your bill, subtract it. The images themselves cost very little, and that is hard to argue with.
You fit inside somebody's free tier. Cloudinary and ImageKit are both more generous than us right at the bottom. If you fit and expect to stay there, take it.
You need more than resizing. No video, no asset manager, no AI editing, no custom domain, no SLA, no procurement paperwork.
Responses
Sizy does not stream your image through itself on every request. It answers a 307 pointing at a signed URL for the rendered object in its own storage, good for fifteen minutes — which is why delivery can be free, and why the thing you should never do is save where the redirect went. Keep pointing at the sizy.io URL; browsers and <img> follow it for you.
When the fetch from your origin fails, Sizy hands you back your origin's own status rather than inventing one — a missing file at your source is a 404 here, and a 403 stays a 403.
The normal answer. Sizy redirects to a signed URL for the rendered image in its own storage, good for fifteen minutes. Follow the redirect rather than saving where it points — browsers and <img> do that for you. Keep pointing at the sizy.io URL.
A parameter was there but not accepted: a width that is not a number, a fit outside the five, a format outside the four. The body is plain text naming the parameter.
No active bucket by that name — or a name the site reserves for itself.
When the fetch from your source fails, Sizy answers with the status your origin gave. A missing file at your origin is a 404 here; a 403 stays a 403.
The source answered but the bytes could not be rendered — not an image Sharp understands, or a background that is not a colour.
Fine print
Better to read this now than to find out after you have rewritten your image tags.
Encoder quality is fixed. You choose the container with format and not how hard it is squeezed — which is the right default far more often than it is a problem, but it is a fixed default.
The three tables above are the complete API. Sizy resizes, crops and re-encodes; anything else is a job for the thing that made the image.
Images are served from sizy.io. If the hostname on your image URLs has to be yours, this is the wrong product today.
Converting an animated GIF would lose every frame but the first, and rasterising an SVG is a decision rather than an optimisation. Neither is ever converted.
There is no API key on an image URL, because a bucket is a public front for a source you already serve publicly. Private originals want a presigned source URL, not a private bucket.
Names are unique across all of Sizy, must be three characters or more, and are not released when a bucket is deleted. Keep them to thirty characters — longer ones save, but the image route will not resolve them.
And these names belong to the site itself, so they always 404 as a bucket:
assetsavatarsimagesfilesfavicon.ico.well-knowndevpublicA bucket takes a name and an origin URL, and it is live the moment you save it. The first 5,000 transforms each month are free and no card is needed to find out whether this is what you wanted.