ClipUGC Uploads API

Send large files straight to storage, then pass the key to another endpoint.

GitHub OpenAPI

Operations 1

POST /uploads/presign Get an upload URL #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/clipugc-com:clipugc-com-uploads-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

clipugc-com-uploads-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: ClipUGC Uploads API
  version: '2'
  description: 'Make UGC videos for your app from code. Create AI influencers who keep the same face,

    animate them into short clips, and turn a clip plus your app screen recording into the

    video you post. - **Base URL:** `https://clipugc.com/api/v2`

    - **Format:** JSON in and out. File fields also accept multipart. - **OpenAPI spec:** https://clipugc.com/docs/api.json. Import it into Postman or Insomnia, or generate

    a client from it. The URL stays the same between releases. - **Access:** the Professional and Business plans. See pricing. Prefer a terminal or an AI assistant? The same features ship as the

    `clipugc` CLI and as a connector for Claude and

    ChatGPT. ## Getting started


    **1. Create a key.** In the dashboard open API Keys and press

    *Create API Key*. Copy the key right away. It is shown once. **2. Make your first request.**


    ```bash

    curl https://clipugc.com/api/v2/user \

    -H "Authorization: Bearer $CLIPUGC_API_KEY" \

    -H "Accept: application/json"

    ```


    **3. Create a character.** Describe the person in plain words. ```bash

    curl -X POST https://clipugc.com/api/v2/ai-characters \

    -H "Authorization: Bearer $CLIPUGC_API_KEY" \

    -H "Content-Type: application/json" \

    -d ''{"description": "Maya, 26, a friendly barista from Lisbon with curly dark hair and freckles"}''

    ```


    The response holds the character and its first look in `data.reference_images`, with

    `status: "pending"`. This costs 2 credits. **4. Poll the look until it is ready.**


    ```bash

    curl https://clipugc.com/api/v2/character-images/LOOK_ID/check-status \

    -H "Authorization: Bearer $CLIPUGC_API_KEY"

    ```


    Repeat every 5 to 10 seconds until `data.status` is `completed` (or `failed`, which refunds

    the credits). The picture is in `data.media.image_url`. **5. Animate the look into a clip.**


    ```bash

    curl -X POST https://clipugc.com/api/v2/character-videos/image-to-video \

    -H "Authorization: Bearer $CLIPUGC_API_KEY" \

    -H "Content-Type: application/json" \

    -d ''{"character_reference_image_id": LOOK_ID, "prompt": "looks at the phone, surprised, then smiles", "duration": "5"}''

    ```


    Poll `GET /character-videos/CLIP_ID/check-status` the same way. A 5 second clip costs

    7 credits. **6. Upload your app screen recording.**


    ```bash

    curl -X POST https://clipugc.com/api/v2/uploads/presign \

    -H "Authorization: Bearer $CLIPUGC_API_KEY" \

    -H "Content-Type: application/json" \

    -d ''{"purpose": "app_video", "extension": "mp4"}''


    # then PUT the raw file to data.upload_url, adding any headers from data.headers

    curl --upload-file recording.mp4 "UPLOAD_URL"

    ```


    **7. Make the finished video.**


    ```bash

    curl -X POST https://clipugc.com/api/v2/character-videos/CLIP_ID/merge \

    -H "Authorization: Bearer $CLIPUGC_API_KEY" \

    -H "Content-Type: application/json" \

    -d ''{"app_video_key": "KEY_FROM_STEP_6", "hook_text": "this app saved me 3 hours this week"}''

    ```


    The response carries `data.merged_video_id`. Poll `GET /merged-videos/MERGED_VIDEO_ID` until

    `data.status` is `completed`, then call `GET /merged-videos/MERGED_VIDEO_ID/download` for a

    link to the MP4. ## Authentication


    Send your key as a bearer token on every request:


    ```

    Authorization: Bearer YOUR_API_KEY

    ```


    A key acts as your account and spends its credits, so keep it on your server and never ship

    it in an app. You can create several keys and revoke any of them on the

    API Keys page. Keys work while your account is on Professional or Business, including a trial. When the

    subscription ends, at the end of the paid period, the keys pause. They work again as soon as

    you subscribe again. ## Responses and errors


    Every response uses the same JSON envelope:


    ```json

    {

    "statusCode": 200,

    "errorMessage": null,

    "data": { },

    "message": "Success"

    }

    ```


    **Read `statusCode`, not only the HTTP status.** Most errors arrive with HTTP 200 and the real

    code in `statusCode`, with a readable `errorMessage`. The exceptions are listed in the HTTP

    column below. | statusCode | HTTP | Meaning |

    |---|---|---|

    | `200`, `201` | 200 | Success. `201` means something was created or started.'
servers:
- url: https://clipugc.com/api/v2
security:
- http: []
tags:
- name: Uploads
  description: Send large files straight to storage, then pass the key to another endpoint.
paths:
  /uploads/presign:
    post:
      operationId: v2.uploads.presign
      description: 'Send large files (anything over 2 MB) this way instead of as multipart.


        1. Call this with `purpose` and the file `extension`.

        2. `PUT` the raw file bytes to `upload_url`, with the returned `headers`, within 30 minutes.

        3. Pass `key` to the endpoint that needs the file, for example `app_video_key`.


        | purpose | extensions | used by |

        |---|---|---|

        | `photo` | png, jpg, jpeg, webp | `source_image_key` |

        | `driver_video` | mp4, mov | `reference_video_key` |

        | `app_video` | mp4, mov, qt, m4v, 3gp, 3g2, webm | `app_video_key` |

        | `music` | mp3, wav, m4a | `music_key` |


        Uploaded files are deleted after 48 hours.


        **Errors:** `400` unknown purpose or extension.'
      summary: Get an upload URL
      tags:
      - Uploads
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                purpose:
                  type: string
                  enum:
                  - driver_video
                  - app_video
                  - music
                  - photo
                extension:
                  type: string
                  maxLength: 5
              required:
              - purpose
              - extension
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                examples:
                - statusCode: 200
                  errorMessage: null
                  data:
                    key: tmp/api-uploads/19/app_video/c7588b43-7833-4124-b92d-7a9d08f934c5.mp4
                    upload_url: https://clipugc.com/storage/tmp/api-uploads/19/app_video/c7588b43-7833-4124-b92d-7a9d08f934c5.mp4?...
                    method: PUT
                    headers: []
                    expires_at: '2026-09-30T20:00:03+00:00'
                  message: Upload URL created
                anyOf:
                - type: object
                  properties:
                    statusCode:
                      type: integer
                      const: 200
                    errorMessage:
                      type: 'null'
                    data:
                      anyOf:
                      - type: object
                        properties:
                          key:
                            type: string
                          upload_url:
                            type: string
                          method:
                            type: string
                            const: PUT
                          headers:
                            type: array
                            items:
                              type: string
                            minItems: 0
                            maxItems: 0
                          expires_at:
                            type: string
                        required:
                        - key
                        - upload_url
                        - method
                        - headers
                        - expires_at
                      - type: object
                        properties:
                          key:
                            type: string
                          upload_url:
                            type: string
                          method:
                            type: string
                            const: PUT
                          headers:
                            anyOf:
                            - type: string
                            - type: array
                              items:
                                type: string
                              minItems: 0
                              maxItems: 0
                          expires_at:
                            type: string
                        required:
                        - key
                        - upload_url
                        - method
                        - headers
                        - expires_at
                    message:
                      type: string
                      const: Upload URL created
                  required:
                  - statusCode
                  - errorMessage
                  - data
                  - message
                - type: object
                  properties:
                    statusCode:
                      type: integer
                      const: 400
                    errorMessage:
                      type: string
                    data:
                      type: 'null'
                    message:
                      type: 'null'
                  required:
                  - statusCode
                  - errorMessage
                  - data
                  - message
        '402':
          description: Your plan does not include API access (app code 1002).
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    const: 1002
                  errorMessage:
                    type: string
                    examples:
                    - API keys need the Professional or Business plan. Upgrade at https://clipugc.com/#pricing
                  data:
                    type: object
                    description: Details, such as pricing_url or retry_after.
                  message:
                    type: 'null'
                required:
                - statusCode
                - errorMessage
                - data
                - message
        '429':
          description: Rate limit for this API key. Wait `data.retry_after` seconds (also in the Retry-After header).
          content:
            application/json:
              schema:
                type: object
                properties:
                  statusCode:
                    type: integer
                    const: 429
                  errorMessage:
                    type: string
                    examples:
                    - 'Too many requests for this API key: 60 per minute. Retry after 42 seconds.'
                  data:
                    type: object
                    description: Details, such as pricing_url or retry_after.
                  message:
                    type: 'null'
                required:
                - statusCode
                - errorMessage
                - data
                - message
components:
  securitySchemes:
    http:
      type: http
      description: Your API key from Dashboard, API Keys. Needs the Professional or Business plan.
      scheme: bearer