---
title: "Initialize a multipart upload — returns image_id and R2 upload token"
method: POST
path: "/v1/images/init-upload"
tags: ["Uploads"]
---

# Initialize a multipart upload — returns image_id and R2 upload token

`POST /v1/images/init-upload`

## Request

- Base URL: `https://api.memestack.ai`
- URL: `https://api.memestack.ai/v1/images/init-upload`
- Auth: HTTP bearer

Creates a pending image record and a pre-authorized R2 multipart upload session. Callers upload directly to R2 using the returned upload token, then call /v1/images/complete-upload to queue processing. Requires authentication. Rate-limited per tier (daily quota and concurrent-pending cap) plus request pacing at 120/min per user (300/min for admin/creator); paced 429s return code RATE_LIMITED with a Retry-After header.

## Request body

- object
  - `mime_type` 'image/jpeg' | 'image/png' | 'image/webp', required: MIME type of the image to be uploaded.
  - `size` integer, required: File size in bytes (max 50 MB).
  - `file_hash` string: SHA-256 hex digest of the file — used for pre-upload duplicate detection. Optional but strongly recommended.
  - `mark_controversial` boolean: Uploader self-label: true marks the image controversial from the moment it is created, so it is hidden from safe-mode browsing and blurred for signed-out viewers. Only the boolean true counts. An uploader can only raise a rating — there is no way to mark an image safe from here; that is an admin decision. Can also be set later with POST /v1/images/{id}/mark-controversial.

## Response `200`

Upload session created.

- object
  - `image_id` string, uuid: Pending image record ID. Pass to /v1/images/complete-upload after the R2 upload finishes.
  - `r2_key` string: R2 object key for the upload. Send the bytes with PUT /v1/images/upload, headers X-Image-ID and X-R2-Key.
  - `expires_in` integer: Seconds until the upload session expires (900 = 15 minutes).

## Other responses

- `400`: Missing or invalid fields, unsupported MIME type, or file too large.
- `401`: Authentication required.
- `403`: Account banned or frozen — uploads disallowed.
- `409`: Duplicate file — a record with this SHA-256 hash already exists.
- `429`: Upload limit reached — daily quota, concurrent-pending cap, or request-rate pacing. Inspect `code` to distinguish. RATE_LIMITED 429s (120 requests/min per user; 300/min for admin/creator) carry a Retry-After header — wait that many seconds and retry the same request. Quota 429s (DAILY_LIMIT_REACHED / PENDING_LIMIT_REACHED) are terminal until the quota resets or pending uploads drain.

---

[API](https://skmtc.dev/memestack/apis/memestack-api.md) · [All operations](https://skmtc.dev/memestack/apis/memestack-api/llms.txt) · [OpenAPI document](https://skmtc.dev/memestack/apis/memestack-api/revisions/9d3a19565f75?raw)
