ClipUGC Uploads API
Send large files straight to storage, then pass the key to another endpoint.
Send large files straight to storage, then pass the key to another endpoint.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/clipugc-com:clipugc-com-uploads-api"
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.
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: 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