> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mage.space/llms.txt
> Use this file to discover all available pages before exploring further.

# Create upload

> POST /v1/uploads returns a signed URL to upload one image or video input of up to 100 MB to Mage storage.

A signed URL to `PUT` one file onto Mage storage, for inputs too large for a data URL. Request bodies are capped at 4.5 MB on the platform, so a data URL carries about 3 MB; an upload takes up to 100 MB.

Create a ticket with the file's `content_type`, `PUT` the bytes to `upload_url` with exactly the `headers` returned, then send `url` in any media field of a generate request. The upload URL is valid for 15 minutes (`upload_url_expires_at`); the file lives 30 days (`url_expires_at`), like every input and result. See [Inputs and uploads](/api/inputs) for the full flow.


## OpenAPI

````yaml api/openapi.json POST /v1/uploads
openapi: 3.1.0
info:
  title: Mage API
  version: v1
  description: >-
    Run Mage models from your own code: submit a generation, poll its status,
    download the result. Every request is paid in gems.
  contact:
    name: Mage
    url: https://www.mage.space
    email: mage@mage.space
servers:
  - url: https://api.mage.space
    description: Production
security:
  - apiKey: []
tags:
  - name: Requests
    description: Submit, poll, and cancel generations.
  - name: Catalog
    description: What the API can generate with.
  - name: Account
    description: The gems balance.
  - name: Uploads
    description: Large input files.
  - name: Image models
    description: One endpoint per image architecture.
  - name: Video models
    description: One endpoint per video architecture.
paths:
  /v1/uploads:
    post:
      tags:
        - Uploads
      summary: Create upload
      description: >-
        A signed URL to upload one input file, for files too large for a data
        URL.
      operationId: create_upload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UploadRequest'
            example:
              content_type: video/mp4
      responses:
        '200':
          description: The upload ticket.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadTicket'
              example:
                upload_url: >-
                  https://storage.googleapis.com/mage/temp/30d/uploads/user/video/6f1c.mp4?X-Goog-Signature=…
                method: PUT
                headers:
                  Content-Type: video/mp4
                  x-goog-content-length-range: 0,104857600
                url: https://cdn3.mage.space/temp/30d/uploads/user/video/6f1c.mp4
                content_type: video/mp4
                max_bytes: 104857600
                upload_url_expires_at: '2026-09-17T15:07:48.000Z'
                url_expires_at: '2026-10-17T14:52:48.000Z'
        '400':
          description: >-
            The body is not a JSON object, or `content_type` is missing or not
            an accepted image or video type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: invalid_request
                  message: >-
                    `content_type` must be one of: image/jpeg, image/png,
                    video/mp4, video/quicktime, video/webm.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    UploadRequest:
      type: object
      properties:
        content_type:
          type: string
          enum:
            - image/jpeg
            - image/png
            - video/mp4
            - video/quicktime
            - video/webm
          description: The media type of the file you will upload.
      required:
        - content_type
      additionalProperties: false
    UploadTicket:
      type: object
      properties:
        upload_url:
          type: string
          format: uri
          description: PUT the file here.
        method:
          type: string
          const: PUT
        headers:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: string
          description: >-
            Headers the PUT must carry exactly; storage checks them against the
            signature.
        url:
          type: string
          format: uri
          description: >-
            The URL the file has once the PUT succeeds. Send it in any media
            field.
        content_type:
          type: string
          description: The media type the PUT must declare.
        max_bytes:
          type: integer
          description: 'The largest body the upload accepts: 100 MB.'
        upload_url_expires_at:
          type: string
          format: date-time
          description: When `upload_url` stops accepting the PUT.
        url_expires_at:
          type: string
          format: date-time
          description: 'When the uploaded file is deleted: 30 days after the ticket.'
      required:
        - upload_url
        - method
        - headers
        - url
        - content_type
        - max_bytes
        - upload_url_expires_at
        - url_expires_at
      additionalProperties: false
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - unauthorized
                - invalid_request
                - invalid_config
                - architecture_not_found
                - architecture_retired
                - request_not_found
                - not_found
                - insufficient_gems
                - forbidden
                - content_blocked
                - request_finished
                - too_many_requests
                - generation_failed
                - internal_error
              description: A stable error code; treat an unknown code by its HTTP status.
            message:
              type: string
              description: What went wrong, for a person.
            request_id:
              description: >-
                The request the refusal belongs to, when one was recorded before
                the refusal.
              type: string
              format: uuid
            gems_required:
              description: The price of the refused generation, on `insufficient_gems`.
              type: number
            docs_url:
              description: Where to read the API reference, on `not_found`.
              type: string
              format: uri
          required:
            - code
            - message
          additionalProperties: true
          description: Further fields depend on the code.
      required:
        - error
      additionalProperties: false
  responses:
    Unauthorized:
      description: The key is missing, malformed, unknown, or revoked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unauthorized
              message: The API key is invalid or has been revoked.
    InternalError:
      description: >-
        Something failed on Mage's side. Retry, and contact support if it
        persists.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: internal_error
              message: >-
                Something went wrong on our side. Retry, and contact support if
                it persists.
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        An API key from Settings → API, sent as `Authorization: Bearer
        mage_sk_…`. Keys are for server-side code only.

````