ImagesWP REST API
One JSON API for search and metadata, one CDN host for delivery. Both take the same image ids, and delivery URLs accept transform parameters directly — there is no separate rendering endpoint to call.
Overview
The API is REST over HTTPS. Requests take query parameters, responses are JSON encoded as UTF‑8, and every endpoint is versioned in the path. TLS 1.2 or newer is required; plain HTTP is redirected once and then refused.
| Host | Purpose | Auth |
|---|---|---|
| api.imageswp.com | Search, metadata, uploads, account | Bearer key |
| cdn.imageswp.com | Image delivery and transforms | Public or signed |
- Base URL for the JSON API:
https://api.imageswp.com/v1 - Base URL for delivery:
https://cdn.imageswp.com/v1 - Image ids are opaque, URL-safe and stable:
img_7f3a - All timestamps are RFC 3339 in UTC:
2026-03-14T09:22:41Z
Authentication
Send your API key as a bearer token on every request to api.imageswp.com. Keys are 40 characters, prefixed by environment, and are shown once when issued.
Authorization: Bearer iwp_live_9c4f2ab7e01d4a63b8f5726d3ec8a1f0
iwp_live_…keys read the collection, your uploads and your usage.iwp_test_…keys behave identically but are metered separately and never billed.- A missing or malformed header returns
401 unauthorized. A valid key without the right scope returns403 forbidden.
Quickstart
Search for something, take the urls.raw value off the first result, and append the transform you want. That is the whole integration.
# 1. find an image
curl -s "https://api.imageswp.com/v1/images/search?q=coastline&per_page=1" \
-H "Authorization: Bearer $IMAGESWP_KEY" | jq '.results[0].urls.raw'
# -> "https://cdn.imageswp.com/v1/img_2c91"
# 2. serve it at the size you need
<img src="https://cdn.imageswp.com/v1/img_2c91?w=800&fit=cover&format=auto"
width="800" height="533" alt="Volcanic ridge under haze">
Search images
Full-text search across titles, tags and machine-generated descriptions, restricted to the ImagesWP collection plus any images your account has uploaded.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| q | string | Search terms. Required unless tag is present. |
| tag | string | Restrict to one tag, for example night. Repeatable; repeated tags are ANDed. |
| orientation | enum | landscape, portrait or square. |
| min_width | integer | Exclude images narrower than this, in pixels. |
| source | enum | collection, uploads or all. Defaults to all. |
| per_page | integer | 1–80. Defaults to 20. |
| page | integer | 1-indexed. Defaults to 1. |
Example response
{
"total": 1284,
"page": 1,
"per_page": 2,
"results": [
{
"id": "img_2c91",
"title": "Volcanic ridge under haze",
"width": 5200,
"height": 3467,
"orientation": "landscape",
"dominant_color": "#6b7a86",
"tags": ["nature"],
"source": "collection",
"license": "ImagesWP Open License 1.0",
"urls": {
"raw": "https://cdn.imageswp.com/v1/img_2c91",
"thumb": "https://cdn.imageswp.com/v1/img_2c91?w=320&fit=cover"
},
"created_at": "2026-01-08T14:02:19Z"
},
{
"id": "img_5e19",
"title": "Barn under a clear night sky",
"width": 4608,
"height": 3072,
"orientation": "landscape",
"dominant_color": "#1b2430",
"tags": ["night", "nature"],
"source": "collection",
"license": "ImagesWP Open License 1.0",
"urls": { "raw": "https://cdn.imageswp.com/v1/img_5e19" },
"created_at": "2026-02-27T21:44:03Z"
}
]
}
Retrieve an image
Returns the full metadata record for one image, including EXIF where the photographer released it and a blurhash you can render while the real file is in flight.
curl -s "https://api.imageswp.com/v1/images/img_7f3a" \
-H "Authorization: Bearer $IMAGESWP_KEY"
{
"id": "img_7f3a",
"title": "Suspension bridge at sunset",
"width": 4800,
"height": 3200,
"bytes": 7418240,
"blurhash": "L9C%b0IU00~qofj[ayfQ4nWB",
"dominant_color": "#c8794b",
"tags": ["architecture", "travel"],
"exif": {
"camera": "Canon EOS R6",
"aperture": "f/8",
"shutter": "1/500",
"iso": 200
},
"license": "ImagesWP Open License 1.0",
"urls": { "raw": "https://cdn.imageswp.com/v1/img_7f3a" },
"created_at": "2026-03-14T09:22:41Z"
}
Delivery URLs
Delivery needs no API key and no round trip through the JSON API. Take an id, put it on the CDN host, and append whatever transform you need.
The extension is optional and equivalent to format=. These three URLs are the same request:
https://cdn.imageswp.com/v1/img_7f3a.avif?w=1440
https://cdn.imageswp.com/v1/img_7f3a?w=1440&format=avif
https://cdn.imageswp.com/v1/img_7f3a?width=1440&fm=avif # short aliases
Response headers
| Header | Value |
|---|---|
| Cache-Control | public, max-age=31536000, immutable |
| Content-Type | Negotiated format, e.g. image/avif |
| Vary | Accept when format=auto is used |
| X-IWP-Cache | HIT, MISS or REVALIDATED |
| X-IWP-Edge | Edge location that served the byte stream, e.g. iad1 |
| Timing-Allow-Origin | *, so your RUM can read the real timings |
POST /v1/images/{id}/purge clears every derived frame in under 30 seconds.
Transform parameters
Parameters compose in any order. Unrecognised parameters are ignored rather than rejected, so a typo degrades to the original image instead of a broken layout.
| Parameter | Type | Description |
|---|---|---|
| w | 1–5000 | Output width in pixels. Aliased as width. |
| h | 1–5000 | Output height in pixels. Aliased as height. |
| fit | enum | cover (default, crops to fill), contain (letterboxes), fill (stretches), inside (never enlarges). |
| crop | enum | Anchor for fit=cover: center (default), top, bottom, left, right, faces, entropy. |
| format | enum | auto (default), avif, webp, jpeg, png. Aliased as fm. |
| q | 1–100 | Quality. Defaults to 78 for lossy formats. Ignored for png. |
| dpr | 1–3 | Multiplies w and h. ?w=400&dpr=2 renders 800px wide. |
| blur | 0–100 | Gaussian radius. Useful for placeholders and background plates. |
| bg | hex | Fill colour behind fit=contain or a transparent PNG, e.g. bg=0d0f13. |
| strip | boolean | Defaults to true. Set strip=false to keep EXIF and ICC data. |
Responsive markup
Because a transform is just a URL, srcset needs no build step and no image component:
<img
src="https://cdn.imageswp.com/v1/img_7f3a?w=960&format=auto"
srcset="https://cdn.imageswp.com/v1/img_7f3a?w=480&format=auto 480w,
https://cdn.imageswp.com/v1/img_7f3a?w=960&format=auto 960w,
https://cdn.imageswp.com/v1/img_7f3a?w=1440&format=auto 1440w"
sizes="(max-width: 720px) 100vw, 720px"
width="960" height="600" loading="lazy" decoding="async"
alt="Suspension bridge at sunset">
Rate limits
Limits apply to api.imageswp.com only. Delivery from cdn.imageswp.com is not rate limited — it is metered against your plan allowance and never throttled mid-month.
| Plan | Burst | Sustained |
|---|---|---|
| Free | 30 requests / 10 s | 5,000 / month |
| Pro | 120 requests / 10 s | 250,000 / month |
| Scale | 600 requests / 10 s | 2,000,000 / month |
Every response carries the current window, so a client never has to guess:
HTTP/2 200
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1773413041
# and when you go over
HTTP/2 429
Retry-After: 4
429 and 5xx with exponential backoff and full jitter, starting at the Retry-After value. Do not retry 4xx other than 429 — the request will fail the same way every time.
Errors
Errors use conventional status codes and always return the same envelope. The request_id is what support will ask for.
{
"error": {
"code": "invalid_parameter",
"message": "per_page must be between 1 and 80.",
"param": "per_page",
"docs": "https://www.imageswp.com/docs/#search",
"request_id": "req_01JQ8Z3M6K2VXH"
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameter | A parameter is missing, malformed or out of range. |
| 401 | unauthorized | No key, malformed header, or the key has been revoked. |
| 403 | forbidden | Valid key, wrong scope, or a signed URL failed verification. |
| 404 | not_found | No image with that id, or it is not visible to this key. |
| 409 | already_exists | Upload conflicts with an existing checksum. |
| 413 | payload_too_large | Upload exceeds 64 MB. |
| 415 | unsupported_media | Source file is not JPEG, PNG, WebP, AVIF, HEIC or TIFF. |
| 422 | transform_failed | Parameters are individually valid but cannot be satisfied together. |
| 429 | rate_limited | Burst window exhausted. See Retry-After. |
| 503 | temporarily_unavailable | Transform capacity is saturated. Safe to retry. |
Versioning
The version lives in the path. Within v1 we add fields, endpoints and parameters, but we do not remove or repurpose them. A field you read today will still be there, with the same meaning, for the life of the version.
- Breaking changes ship as
v2, andv1stays available for at least 24 months afterwards. - Deprecations arrive as a
Sunsetheader on affected responses, at least 180 days ahead. - Treat unknown JSON fields as additive and ignore them — new ones appear without notice.
Recent changes
| Date | Change |
|---|---|
| 2026‑06‑18 | Added crop=faces and crop=entropy anchors. |
| 2026‑04‑02 | format=auto now prefers AVIF wherever the Accept header allows it. |
| 2026‑02‑11 | Search gained source, so uploads and the collection can be queried separately. |
| 2025‑11‑30 | v1 declared stable. |