# 4kwallpaper API
Cloudflare Worker proxy for 4kwallpapers.com.
Forwards wallpaper metadata and download URLs to client apps with KV cache and edge cache headers. No object storage — data forwarding only.
| Base URL | https://wp.luc.wtf |
| Version | v1 |
| Auth | Optional X-API-Key (if API_KEY is configured) |
| CORS | Access-Control-Allow-Origin: * |
| Source | Upstream HTML + /search-api suggest endpoint |
Live links: Health · Categories · Recent · This doc (HTML) · Raw Markdown
# Quick start
BASE=https://wp.luc.wtf
# Health
curl "$BASE/v1/health"
# Home feed
curl "$BASE/v1/wallpapers?feed=recent&page=1"
# Search
curl "$BASE/v1/search?q=goku"
# Detail (path from list item)
curl "$BASE/v1/wallpapers/nature/stac-a-phris-arch-26748"
# Download 4K (302 redirect to origin CDN)
curl -I "$BASE/v1/download/nature/stac-a-phris-arch-26748?resolution=3840x2160"
# Authentication
By default the API is open.
If the worker env var API_KEY is set, every request (except OPTIONS) must include:
X-API-Key: <your-key>
or
Authorization: Bearer <your-key>
Missing/invalid key → 401:
{ "error": "Unauthorized" }
# Caching
Responses include cache metadata so clients and CDNs behave well.
| Header | Meaning | ||
|---|---|---|---|
Cache-Control | public, max-age=…, s-maxage=…, stale-while-revalidate=… | ||
CDN-Cache-Control | Edge-oriented max-age | ||
X-Cache | HIT \ | MISS \ | BYPASS |
X-Cache-Source | kv \ | edge \ | origin |
# Default TTLs (seconds)
| Data | Env var | Default |
|---|---|---|
| Lists / feeds / categories pages | CACHE_TTL_LIST | 900 |
| Wallpaper detail | CACHE_TTL_DETAIL | 86400 |
| Search results | CACHE_TTL_SEARCH | 1800 |
| Category catalog | CACHE_TTL_CATEGORIES | 86400 |
| Suggest | CACHE_TTL_SUGGEST | 3600 |
| Proxied images | CACHE_TTL_IMAGE | 604800 |
Flow: Client → CF edge (Cache API / CDN) → Worker → KV (JSON) → 4kwallpapers.com
# Endpoints
# GET / · GET /docs
Human-readable HTML documentation (this page).
# GET /docs.md
Raw Markdown source (text/markdown).
# GET /v1 · GET /v1/openapi.json
Machine-readable endpoint map (JSON).
# GET /v1/health
Liveness probe.
Response 200
{
"ok": true,
"origin": "https://4kwallpapers.com",
"time": "2026-07-20T17:37:30.309Z"
}
# GET /v1/categories
Catalog of primary categories and named feeds.
Response 200
{
"categories": [
{ "slug": "anime", "name": "Anime", "path": "/anime/" },
{ "slug": "nature", "name": "Nature", "path": "/nature/" }
],
"feeds": [
{ "slug": "recent", "name": "Recent", "path": "/" },
{ "slug": "popular", "name": "Popular", "path": "/most-popular-4k-wallpapers/" },
{ "slug": "featured", "name": "Featured", "path": "/best-4k-wallpapers/" },
{ "slug": "random", "name": "Random", "path": "/random-wallpapers/" }
]
}
Primary category slugs:
abstract · animals · anime · architecture · bikes · black-dark · cars · celebrations · cute · fantasy · flowers · food · games · gradients · graphics-cgi · lifestyle · love · military · minimal · movies · music · nature · people · photography · quotes · sci-fi · space · sports · technology · world
# GET /v1/wallpapers
List wallpapers from a named feed.
| Query | Type | Default | Description | |||
|---|---|---|---|---|---|---|
feed | string | recent | recent \ | popular \ | featured \ | random |
page | int | 1 | Page number (≥ 1) |
Example
GET /v1/wallpapers?feed=popular&page=2
Response 200 — see Paginated list
# GET /v1/categories/:slug
List wallpapers for a category or free-form tag page.
| Param / query | Description |
|---|---|
slug | e.g. anime, nature, scotland, goku |
page | Page number (default 1) |
Example
GET /v1/categories/anime?page=1
GET /v1/categories/scotland?page=1
Errors
404— empty first page for unknown slug
# GET /v1/search
Full-text / tag search (upstream search page).
| Query | Required | Description |
|---|---|---|
q or query | yes | Search string (spaces → hyphens upstream) |
page | no | Default 1 |
Example
GET /v1/search?q=goku&page=1
# GET /v1/search/suggest
Tag autocomplete (proxies upstream /search-api?action=tagsearch).
| Query | Required |
|---|---|
q or query | yes |
Example
GET /v1/search/suggest?q=goku
Response 200
{
"query": "goku",
"suggestions": [
"Son Goku",
"Goku",
"Ultra Instinct Goku",
"Goku Black"
]
}
# GET /v1/wallpapers/:category/:slugId
Wallpaper detail: tags, thumbnails, all resolutions, related items.
| Path part | Example |
|---|---|
category | nature |
slugId | stac-a-phris-arch-26748 (must end with -{numericId}) |
Example
GET /v1/wallpapers/nature/stac-a-phris-arch-26748
Response 200
{
"id": 26748,
"title": "Stac a Phris Arch Scotland",
"category": "Nature",
"categoryPath": "/nature/",
"slug": "stac-a-phris-arch",
"path": "/nature/stac-a-phris-arch-26748.html",
"description": "Get this Cool Stac a Phris Arch background in 5120x3413 5K resolution…",
"tags": ["Scotland", "Long exposure", "Sunset"],
"keywords": ["Stac a Phris Arch", "Scotland", "5K"],
"originalResolution": "5120x3413",
"thumbnails": {
"small": "https://4kwallpapers.com/images/walls/thumbs/26748.jpg",
"medium": "https://4kwallpapers.com/images/walls/thumbs_2t/26748.jpg",
"large": "https://4kwallpapers.com/images/walls/thumbs_3t/26748.jpg"
},
"resolutions": [
{
"width": 3840,
"height": 2160,
"label": "4K UHD",
"group": "primary",
"url": "https://4kwallpapers.com/images/wallpapers/stac-a-phris-arch-3840x2160-26748.jpg",
"filename": "stac-a-phris-arch-3840x2160-26748.jpg"
}
],
"related": [],
"sourceUrl": "https://4kwallpapers.com/nature/stac-a-phris-arch-26748.html"
}
# Resolution groups
group | Devices |
|---|---|
primary | 4K + Original |
desktop | 1080p → 5K, ultrawide |
mobile | Phone portrait sizes |
tablet | Square tablet sizes |
iphone | iPhone-specific |
ipad | iPad-specific |
other | Unclassified fallback |
# GET /v1/download/:category/:slugId
Resolve a wallpaper + resolution and either redirect or proxy the image.
| Query | Default | Description |
|---|---|---|
resolution or res | best 4K / first | e.g. 3840x2160, 1080x1920 |
mode | redirect | redirect = 302 to origin · proxy = stream via worker |
Examples
GET /v1/download/nature/stac-a-phris-arch-26748?resolution=3840x2160
GET /v1/download/nature/stac-a-phris-arch-26748?resolution=1080x1920&mode=proxy
mode=redirect → 302 + Location: https://4kwallpapers.com/images/wallpapers/...
mode=proxy → 200 image body with long Cache-Control (no storage; streamed).
# GET /v1/download
Query-style download helper.
| Query | Description | |
|---|---|---|
path | Origin path, e.g. /nature/stac-a-phris-arch-26748.html | |
resolution / res | Target size | |
url | Direct origin image URL (must be /images/* on origin host) | |
mode | redirect \ | proxy |
Examples
GET /v1/download?path=/nature/stac-a-phris-arch-26748.html&resolution=3840x2160
GET /v1/download?url=https://4kwallpapers.com/images/wallpapers/….jpg&mode=proxy
# GET /v1/image
Thumbnail / image proxy. Only allows https://4kwallpapers.com/images/*.
GET /v1/image?url=https://4kwallpapers.com/images/walls/thumbs/26748.jpg
# Schemas
# Wallpaper list item
{
"id": 26748,
"title": "Stac a Phris Arch",
"category": "nature",
"slug": "stac-a-phris-arch",
"path": "/nature/stac-a-phris-arch-26748.html",
"tags": ["Scotland", "Long exposure", "Sunset"],
"thumbnails": {
"small": "https://4kwallpapers.com/images/walls/thumbs/26748.jpg",
"medium": "https://4kwallpapers.com/images/walls/thumbs_2t/26748.jpg"
},
"sourceUrl": "https://4kwallpapers.com/nature/stac-a-phris-arch-26748.html"
}
Use category + slug + id (or path) for detail and download calls.
# Paginated list
{
"items": [ /* Wallpaper list item */ ],
"page": 1,
"pageSize": 24,
"totalPages": 950,
"hasMore": true,
"feed": "recent",
"query": null,
"category": null
}
totalPages may be null when upstream pagination is incomplete. Prefer hasMore for infinite scroll.
# Error
{
"error": "Human-readable message",
"available": ["1920x1080", "3840x2160"]
}
| Status | When |
|---|---|
400 | Bad params |
401 | API key required/invalid |
404 | Not found |
405 | Method not allowed (only GET / OPTIONS) |
500 / 502 | Worker or origin failure |
# Client integration
# Flutter / mobile sketch
final base = 'https://wp.luc.wtf/v1';
// Feed
final feed = await http.get(Uri.parse('$base/wallpapers?feed=recent&page=1'));
final items = jsonDecode(feed.body)['items'] as List;
// Detail
final w = items.first;
final detail = await http.get(Uri.parse(
'$base/wallpapers/${w['category']}/${w['slug']}-${w['id']}',
));
// Best phone wallpaper URL
final resolutions = jsonDecode(detail.body)['resolutions'] as List;
final phone = resolutions.firstWhere(
(r) => r['width'] == 1080 && r['height'] == 1920,
orElse: () => resolutions.first,
);
// Prefer redirect (lets OS/WebView hit origin CDN)
final downloadUrl =
'$base/download/${w['category']}/${w['slug']}-${w['id']}'
'?resolution=${phone['width']}x${phone['height']}';
# TypeScript sketch
const API = "https://wp.luc.wtf/v1";
const feed = await fetch(`${API}/wallpapers?feed=recent&page=1`).then((r) =>
r.json(),
);
const w = feed.items[0];
const detail = await fetch(
`${API}/wallpapers/${w.category}/${w.slug}-${w.id}`,
).then((r) => r.json());
const res4k =
detail.resolutions.find(
(r: { width: number; height: number }) =>
r.width === 3840 && r.height === 2160,
) ?? detail.resolutions[0];
// Option A — open origin URL from detail (fastest)
window.open(res4k.url);
// Option B — go through API redirect
window.open(
`${API}/download/${w.category}/${w.slug}-${w.id}?resolution=${res4k.width}x${res4k.height}`,
);
# Recommended resolution picks
| Device class | Prefer |
|---|---|
| Desktop 4K | 3840x2160 (group: primary or desktop) |
| Desktop FHD | 1920x1080 |
| Ultrawide | 3440x1440 / 2560x1080 |
| Android FHD | 1080x1920 |
| Android QHD | 1440x2560 |
| iPhone 14/15/16 | 1179x2556 |
| iPhone Pro Max | 1290x2796 |
| Original | match originalResolution or group: primary “Original” |
# Design notes
- No storage — Worker does not keep images in R2/Disk. Images are redirected or streamed.
- Upstream has no public JSON API — listings/detail are parsed from stable HTML; suggest uses
/search-api. - Prefer
mode=redirectfor full-size downloads so large files stay on the origin CDN. - List items always include
path/slug/id/category— detail URLs cannot be built from numeric id alone. - Upstream markup may change — empty
itemsor missingresolutionsusually means a parser break.
# Rate limiting & fair use
This proxy is intended for a personal/client app. Respect Terms of Service and Copyright of 4kwallpapers.com. Do not hammer the origin; lean on the provided cache TTLs. Consider setting API_KEY before exposing the worker publicly.
# Deploy
pnpm install
pnpm deploy # wrangler deploy
| Binding / var | Purpose |
|---|---|
CACHE (KV) | Parsed JSON cache |
ORIGIN | Upstream base URL |
API_KEY | Optional gate |
CACHE_TTL_* | Tunable TTLs |
Repo layout:
docs/API.md ← this document (source of truth)
src/index.ts ← router
src/parse.ts ← HTML → JSON
src/cache.ts ← KV + edge cache
src/origin.ts ← upstream fetch + allowlist
wrangler.toml
# Changelog
| Version | Notes |
|---|---|
1.0.0 | Initial public API: feeds, categories, search, detail, download, KV + edge cache, online docs |