/api/generateWallpaper Generator API
A single HTTP endpoint that generates a phone wallpaper image (PNG or SVG) showing your year progress, life calendar, or goal countdown — updated to the current day in any timezone.
Quick Start
Make a GET request with query parameters. The response is a raw image — you can use it directly in <img> tags, iOS Shortcuts, or Android MacroDroid.
curl "https://365tsf.vercel.app/api/generate?country=us&type=year&bg=000000&accent=FFFFFF&width=1206&height=2622" \
--output wallpaper.pngFormat
PNG (default) or SVG
Rate limit
20 req / 60s per IP
Cache
24h Cache-Control
Parameters
All parameters are passed as query strings. Only country is strictly required; everything else falls back to a sensible default.
| Parameter | Type | Default | Description |
|---|---|---|---|
countryrequired | string | us | ISO 3166-1 alpha-2 country code (lowercase). Used to derive the timezone so the correct day is highlighted. E.g. us, gb, in, jp. |
type | enum | year | Wallpaper style. One of year, life, or goal. |
bg | hex | 000000 | Background color as a 6-digit hex string without the #. E.g. 0a0a0a. |
accent | hex | FFFFFF | Accent / dot color as a 6-digit hex string. E.g. FFD700. |
width | integer | 1170 | Canvas width in pixels. Min 300, max 8000. |
height | integer | 2532 | Canvas height in pixels. Min 300, max 8000. |
clockHeight | float | 0.18 | Fraction of canvas height reserved for the lock screen clock (0–0.5). iPhone 16 Pro uses 0.18; adjust per device. |
format | enum | png | Output format. png (default) or svg. |
type=life)| Parameter | Type | Default | Description |
|---|---|---|---|
dob | YYYY-MM-DD | Date of birth. If omitted, defaults to 25 years before today. | |
lifespan | integer | 80 | Expected lifespan in years (1–120). Determines total grid rows. |
type=goal)| Parameter | Type | Default | Description |
|---|---|---|---|
goalrequired | YYYY-MM-DD | Target date to count down to. | |
goalStart | YYYY-MM-DD | When you started tracking. Used to calculate arc progress. Must be ≤ goal. Defaults to 30 days before the target. | |
goalName | string | Goal | Label shown on the wallpaper (max 100 chars). URL-encode special characters. |
Year Progress
365 or 366 dots arranged in a 15-column grid. Completed days are filled, today's dot is slightly larger, and remaining days are dimmed. A stats line below the grid shows days left and percentage complete.
countrybgaccentwidthheightclockHeightcurl "https://365tsf.vercel.app/api/generate?country=us&type=year&bg=000000&accent=FFFFFF&width=1206&height=2622"Life Calendar
52 columns × lifespan rows — one dot per week of your life. Weeks lived are filled, the current week is highlighted, and future weeks are faint. Stats show weeks remaining and percentage lived.
countrybgaccentwidthheightclockHeightdoblifespancurl "https://365tsf.vercel.app/api/generate?country=us&type=life&bg=000000&accent=FFFFFF&width=1206&height=2622&dob=1995-06-15&lifespan=80"Goal Countdown
A circular arc that shrinks as days pass toward the target date. The center shows days remaining. The arc represents the proportion of time left from start to goal.
countrybgaccentwidthheightclockHeightgoalgoalStartgoalNamecurl "https://365tsf.vercel.app/api/generate?country=us&type=goal&bg=000000&accent=FFFFFF&width=1206&height=2622&goal=2025-12-31&goalName=Launch"Examples
Year progress — iPhone 17 Pro, gold accent
curl "https://365tsf.vercel.app/api/generate?country=us&type=year&bg=0a0a0a&accent=FFD700&width=1206&height=2622&clockHeight=0.18"Life calendar — born 1995, 85 year lifespan
curl "https://365tsf.vercel.app/api/generate?country=gb&type=life&bg=000000&accent=00D4FF&width=1206&height=2622&dob=1995-06-15&lifespan=85"Goal countdown — product launch
curl "https://365tsf.vercel.app/api/generate?country=us&type=goal&bg=0a0a0a&accent=FF3B30&width=1206&height=2622&goal=2025-12-31&goalStart=2025-01-01&goalName=Product%20Launch"Fetch in JavaScript
const params = new URLSearchParams({
country: "us",
type: "year",
bg: "000000",
accent: "FFFFFF",
width: "1206",
height: "2622",
});
const res = await fetch(`/api/generate?${params}`);
const blob = await res.blob();
const url = URL.createObjectURL(blob);
document.querySelector("img").src = url;iOS Shortcut — "Get Contents of URL" action
URL:
https://365tsf.vercel.app/api/generate?country=us&type=year&bg=000000&accent=FFFFFF&width=1206&height=2622&clockHeight=0.18
Then: Set Wallpaper Photo → Lock Screen
↳ Disable "Crop to Subject"
↳ Disable "Show Preview"Errors & Rate Limits
The endpoint is rate limited to 20 requests per 60 seconds per IP address. Every response includes rate limit headers so you can track usage.
Success — image data returned.
Validation error — a parameter is missing, out of range, or malformatted. Response body is JSON with an issues array.
Too Many Requests — rate limit exceeded. Check Retry-After header.
Internal Server Error — image generation failed.
400 Validation error body
{
"error": "Validation Error",
"issues": [
{
"code": "invalid_type",
"path": ["width"],
"message": "Expected number, received nan"
}
]
}429 Rate limit body
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 42s."
}Response Headers
| Header | Description |
|---|---|
Content-Type | image/png or image/svg+xml depending on format param |
Cache-Control | public, max-age=86400 — safe to cache for 24 hours |
X-RateLimit-Limit | Maximum requests allowed per window (20) |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Seconds until the window resets |
Retry-After | Seconds to wait before retrying (only on 429) |
Built by avalynndev