Quick start
imgsweet is a URL-based image proxy. There is no SDK to install. You construct a URL, point your <img> tag at it, and imgsweet handles the rest.
1. Get your secret key
Sign in to app.imgsweet.com, create a key, and optionally set a domain allowlist.
2. Build a proxy URL
Take any public image URL, pass it as the url param, and add your transforms:
https://img.imgsweet.com/proxy ?url=https%3A%2F%2Fcdn.myapp.com%2Fphoto.jpg &w=800 &q=75 &secret=YOUR_KEY
encodeURIComponent() in JS, urlencode() in PHP, or urllib.parse.quote() in Python.
3. Use it in your markup
<img src="https://img.imgsweet.com/proxy?url=https%3A%2F%2Fcdn.myapp.com%2Fphoto.jpg&w=800&q=75&secret=YOUR_KEY" alt="My photo" />
Authentication
Every request to /proxy must include a secret query param matching one of your API keys. Keys are managed in the dashboard.
To rotate a key, go to the dashboard → Keys → Rotate. The old secret stops working immediately.
Domain allowlist
Each key has an optional list of allowed source domains. When set, the proxy rejects requests whose source URL hostname doesn't match.
myapp.com
cdn.myapp.com/photo.jpg — subdomain matchmyapp.com/logo.png — exact matchevil.com/photo.jpg — rejectedfakemyapp.com/x.jpg — rejectedLeave the allowlist empty to allow any public domain. Recommended only during development.
Endpoint
GET https://img.imgsweet.com/proxy
Always returns image/webp with Cache-Control: public, max-age=432000 (5 days). Source images are downloaded server-side — the client never touches the origin URL.
Parameters
All parameters are passed as query string values.
| Param | Type | Description | |
|---|---|---|---|
| url | string | required | Source image URL, percent-encoded. |
| secret | string | required | Your API key secret. |
| w | integer | optional | Output width in pixels (1–10000). Height auto-scales unless h is also set. |
| h | integer | optional | Output height in pixels (1–10000). |
| fit | string | optional | Resize strategy when both w and h are set. One of: inside (default), cover, contain, fill, outside. |
| q | integer | optional | WebP quality, 1–100. Default 80. |
| strip | boolean | optional | Strip EXIF metadata. Default true. Pass strip=false to preserve. |
| blur | float | optional | Gaussian blur sigma (0.3–100). Useful for placeholder/preview images. |
| grayscale | boolean | optional | Convert to greyscale. Pass grayscale=true. |
Errors
All errors return JSON with statusCode and message.
| Status | Cause |
|---|---|
401 | Missing or invalid secret. |
403 | Source domain not in allowlist, or private IP blocked (SSRF guard). |
402 | Credit balance is zero. Top up at app.imgsweet.com/billing. |
429 | Free tier daily limit (10/day) reached. |
502 | Source image could not be fetched (bad URL, origin down, timeout). |
413 | Source image exceeds 25 MB. |
JavaScript (browser)
A lightweight helper function — no dependency needed.
function imgsweet(url, params = {}) { const base = 'https://img.imgsweet.com/proxy' const qs = new URLSearchParams({ url, secret: 'YOUR_KEY', ...params, }) return `${base}?${qs}` } // Usage img.src = imgsweet('https://cdn.myapp.com/photo.jpg', { w: 800, q: 75 }) img.src = imgsweet('https://cdn.myapp.com/avatar.jpg', { w: 64, h: 64, fit: 'cover' })
Node.js / Next.js
Keep the secret server-side in an environment variable.
// lib/imgsweet.ts export function imgsweet(url: string, params: Record<string, string | number> = {}) { const qs = new URLSearchParams({ url, secret: process.env.IMGSWEET_SECRET!, ...Object.fromEntries( Object.entries(params).map(([k, v]) => [k, String(v)]) ), }) return `https://img.imgsweet.com/proxy?${qs}` }
// app/products/[id]/page.tsx (Next.js App Router) import { imgsweet } from '@/lib/imgsweet' export default function ProductPage({ product }) { return ( <img src={imgsweet(product.imageUrl, { w: 800, q: 80 })} alt={product.name} /> ) }
PHP
function imgsweet(string $url, array $params = []): string { $params = array_filter(array_merge([ 'url' => $url, 'secret' => getenv('IMGSWEET_SECRET'), ], $params)); return 'https://img.imgsweet.com/proxy?' . http_build_query($params); } // Usage $src = imgsweet($product->image_url, ['w' => 800, 'q' => 75]); $avatar = imgsweet($user->avatar, ['w' => 64, 'h' => 64, 'fit' => 'cover']);
Liquid (Shopify)
Add this snippet to your theme as snippets/imgsweet.liquid:
{%- comment -%} Usage: {% render 'imgsweet', url: image.src, w: 800, q: 75 %} {%- endcomment -%} {%- assign _secret = 'YOUR_KEY' -%} {%- capture _proxy -%} https://img.imgsweet.com/proxy?url={{ url | url_encode }}&secret={{ _secret }} {%- if w -%}&w={{ w }}{%- endif -%} {%- if h -%}&h={{ h }}{%- endif -%} {%- if fit -%}&fit={{ fit }}{%- endif -%} {%- if q -%}&q={{ q }}{%- endif -%} {%- endcapture -%} {{ _proxy | strip }}
{%- comment -%} In your template: {%- endcomment -%} {% render 'imgsweet', url: product.featured_image.src, w: 800, q: 75 %}
Python
from urllib.parse import urlencode, quote import os def imgsweet(url: str, **params) -> str: qs = urlencode({ "url": url, "secret": os.environ["IMGSWEET_SECRET"], **{k: v for k, v in params.items() if v is not None}, }) return f"https://img.imgsweet.com/proxy?{qs}" # Usage src = imgsweet("https://cdn.myapp.com/photo.jpg", w=800, q=75) avatar = imgsweet("https://cdn.myapp.com/avatar.jpg", w=64, h=64, fit="cover")
Responsive images
Use srcset to serve different sizes at different breakpoints. Each unique URL combination is a separate transformation credit — but subsequent requests at the same size are free (CDN-cached).
function imgsweetSrcset(url, widths, params = {}) { return widths .map(w => `${imgsweet(url, { ...params, w })} ${w}w`) .join(', ') } // In your markup: img.srcset = imgsweetSrcset( 'https://cdn.myapp.com/hero.jpg', [400, 800, 1200, 1600], { q: 80 } ) img.sizes = '(max-width: 768px) 100vw, 50vw'
CDN caching
imgsweet sets Cache-Control: public, max-age=432000 (5 days) on every response. Point any CDN at img.imgsweet.com and it will cache transformed images automatically.
secret param from the cache key if you want different users requesting the same image to share a cache entry.
On a cache hit, the request never reaches our origin and no credit is consumed.
Credits & billing
One credit is consumed each time a request reaches our proxy and a transformation runs. Cache hits are free.
| Tier | Credits | Price |
|---|---|---|
| Free | 10 / day | Always free |
| Starter | 5,000 | $10 one-time |
| Growth | 15,000 | $25 one-time |
| Scale | 50,000 | $60 one-time |
Credits never expire. Buy at app.imgsweet.com/billing. When your balance reaches zero, requests return 402 until you top up.