HTTP API Reference (imagehoster)
This document describes the public HTTP interface exposed by the imagehoster service.
This document will be updated to devlopers portal then.
Conventions
- Base URL: determined by
SERVICE_URL(https://steemitimages.com). - Success responses:
- JSON endpoints:
{"success": true, ...} - Binary image endpoints: return image bytes with
Content-Type: image/*
- JSON endpoints:
- Errors:
Most user-controllable errors intentionally return HTTP 200 with a JSON body:
{ "success": false, "error": { "name": "ErrorCode", "message": "Human-readable message", "info": {} } }Rationale and details: see
docs/AWS_HEALTH_CHECK_AND_ERROR_HANDLING.mdanddocs/HTTP_STATUS_CODES.md.
Endpoints
Health check
GET /
- Purpose: lightweight health check.
- Response: JSON.
Example response:
{
"ok": true,
"version": "dev-<git>-<buildtime>",
"date": "2026-04-26T15:04:05Z",
"redis_status": "ok|error|not_configured"
}
GET /.well-known/healthcheck.json
Same response format and behavior as GET /.
Upload
POST /:username/:signature
- Purpose: upload an image and receive a canonical URL.
- Content-Type: must include
multipart/form-data. - Headers:
Content-Lengthis required and must be ( \le MAX_IMAGE_SIZE ).
- Body:
- Any multipart field name is accepted.
- The service reads the first file part found in the multipart form.
- Accepted file types: JPEG, PNG, GIF, WebP.
- Auth model:
:signatureis verified using the account’s Steem keys (server-side via RPC).- The exact signature algorithm is implemented in
internal/steem(not an HTTP concern).
Success response:
{
"success": true,
"url": "https://cdn.steemitimages.com/<DKEY>/<sanitized-filename>"
}
Common error codes (returned as HTTP 200 with success:false):
BadRequest: invalid multipart request, missing/invalid headers, invalid filename, etc.LengthRequired: missingContent-Length.PayloadTooLarge: file exceedsMAX_IMAGE_SIZE.FileMissing: no multipart file part present.InvalidImage: file is not a supported image type.InvalidSignature: signature verification failed.NoSuchAccount: Steem account not found.Deplorable: account reputation too low.Blacklisted: account blacklisted.QoutaExceeded: upload rate limit exceeded (Redis-backed).
Notes:
- Upload rate limiting is enforced in the service layer; the HTTP handler does not currently add
X-RateLimit-*headers on upload responses. - Filenames are sanitized server-side; a filename that sanitizes to empty is rejected.
Serve uploaded image (by key)
GET /:hash
GET /:hash/:filename
- Purpose: serve an image from the upload store by key (typically a
D*key). - Response: binary image bytes.
- Headers:
Content-Type: inferred from file extension (defaults toimage/jpeg).Cache-Control: public,max-age=29030400,immutable
User-controllable errors (HTTP 200 + JSON):
NotFound: blob key does not exist.Blacklisted: request blocked (key/IP blacklist).
Server errors (may return non-200):
UpstreamError: blob store read failure or other internal failure.
Proxy (fetch + optional transform + cache)
This endpoint is enabled only when proxy: true (IMAGEHOSTER_PROXY=true in config/env).
GET /p/:url
:urlis a Base58-encoded absolute URL (HTTP/HTTPS).- Response: binary image bytes.
- Caching:
- If the transformed image is served from cache:
Cache-Control: public,max-age=29030400,immutable - Otherwise:
Cache-Control: public,max-age=600
- If the transformed image is served from cache:
Query parameters (all optional):
width: positive integer. If set alone, height is derived to preserve aspect ratio.height: positive integer. If set alone, width is derived to preserve aspect ratio.mode:cover(default): cover the requested rectangle; crop overflow.fit: fit within the requested rectangle; downsample-only.
format:match(default): keep input formatjpeg/jpgpngwebp
User-controllable errors (HTTP 200 + JSON):
InvalidProxyUrl: base58 decode fails / URL parse fails.InvalidImage: upstream content-type is not one ofimage/gif,image/jpeg,image/png,image/webp.PayloadTooLarge: upstream image exceedsMAX_IMAGE_SIZE.NotFound: proxied URL points to an upload key that does not exist.Blacklisted: blocked by image URL blacklist, IP blacklist, or proxy prefix blacklist.RefererNotAllowed: blocked by referer whitelist (when configured).TargetHostNotAllowed: SSRF/allowlist policy blocked the outbound host.
Security and policy notes:
- Outbound fetch is restricted to http/https.
- Private/internal IPs are blocked (SSRF protection), including DNS rebinding defenses at dial time.
- If
PROXY_ALLOWED_HOSTSis configured, only those target hosts (plus the service’s own host) are allowed. - Additionally, for
/p/:urlrequests, the service restricts outbound fetches to the service’s registrable domain and its subdomains by default (defense-in-depth).
Avatar
This endpoint is enabled only when proxy: true.
GET /u/:username/avatar
GET /u/:username/avatar/:size
- Purpose: fetch the user’s
profile_imagefrom Steem metadata and serve it through the same proxy/transform/cache pipeline. - Response: binary image bytes.
- Caching:
Cache-Control: public,max-age=600for uncached responsesCache-Control: public,max-age=29030400,immutableif served from cache
Path parameters:
:username: Steem account name.:size:small(64x64)medium(128x128)large(512x512)- Any other value (or omitted): natural aspect ratio with width=128 and adaptive height.
Notes:
- If the account has no avatar URL, the service uses
DEFAULT_AVATARor falls back to the embeddedstatic/default-avatar.webp.
Upvoted! Thank you for supporting witness @jswit.