HTTP API Reference (imagehoster)steemCreated with Sketch.

in #steem2 days ago

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/*
  • 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.md and docs/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-Length is 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:
    • :signature is 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: missing Content-Length.
  • PayloadTooLarge: file exceeds MAX_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 to image/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

  • :url is 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

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 format
    • jpeg / jpg
    • png
    • webp

User-controllable errors (HTTP 200 + JSON):

  • InvalidProxyUrl: base58 decode fails / URL parse fails.
  • InvalidImage: upstream content-type is not one of image/gif, image/jpeg, image/png, image/webp.
  • PayloadTooLarge: upstream image exceeds MAX_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_HOSTS is configured, only those target hosts (plus the service’s own host) are allowed.
  • Additionally, for /p/:url requests, 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_image from Steem metadata and serve it through the same proxy/transform/cache pipeline.
  • Response: binary image bytes.
  • Caching:
    • Cache-Control: public,max-age=600 for uncached responses
    • Cache-Control: public,max-age=29030400,immutable if 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_AVATAR or falls back to the embedded static/default-avatar.webp.
Sort:  

Upvoted! Thank you for supporting witness @jswit.