# Upload Media

## Upload is Now Optional

You no longer need to upload media to Blotato before publishing. You can pass any publicly accessible image/video URL directly into the `mediaUrls` parameter in the Publish node. Blotato handles the media transfer automatically.

For local files without a public URL, use the [Presigned Upload](#presigned-upload-local-files) endpoint below. This lets you upload directly to Blotato without needing Google Drive, S3, or any other intermediate storage. Upload size depends on your plan (see [Plan Limits](/settings/billing-and-credits.md#plan-limits)).

The legacy Upload Media endpoint is still available if you prefer to use it, or if you need to host media on Blotato's servers.

***

## Upload Media

### Endpoint

**Base URL:** `https://backend.blotato.com/v2`

**URL:** `/media`

**Method:** `POST`

### Description

This endpoint allows users to upload media by providing a URL. The uploaded media will be processed and stored, returning a new media URL that is used to publish a new post. Most of the platforms require validated URLs for posting images.

You can upload:

* publicly accessible URLs
* base64 encoded image data

Media uploads are subject to your plan's upload size limit. See [Plan Limits](/settings/billing-and-credits.md#plan-limits) for more details.

If you are using the n8n "Binary Data" upload option, the file size limit is 15MB. For larger files, use the [Presigned Upload](#presigned-upload-local-files) endpoint above (no external hosting needed), or host your media on Google Drive, AWS S3, or another cloud storage service and pass the URL instead.

### Request

#### Request Body

<table data-header-hidden><thead><tr><th></th><th width="445"></th><th></th><th></th></tr></thead><tbody><tr><td>Field</td><td>Type</td><td>Required</td><td>Description</td></tr><tr><td><code>url</code></td><td><code>string</code></td><td>✅</td><td>The URL of the media to upload.</td></tr></tbody></table>

### Responses

#### Success Response

**Status Code:** `201 Created`

**Response Body:**

```
{
  "url": "https://database.blotato.com/path-to-uploaded-media.jpg"
}
```

#### Error Responses

**Internal Server Error**

**Status Code:** `500 Internal Server Error`

```
{
  "code": 9999,
  "message": "An unknown error occurred."
}
```

**Too Many Requests**

Media upload has a user-level rate limit of 30 requests / minute.

**Status Code:** `429 Too many requests`

```
{
  "statusCode": 429,
  "message": "Rate limit exceeded, retry in 49 seconds"
}
```

#### Error Codes

The following client error codes may be returned:

| Code   | Description    |
| ------ | -------------- |
| `9999` | Unknown error. |

### Examples

#### 1. Upload Media

```
POST https://backend.blotato.com/v2/media HTTP/1.1
Content-Type: application/json
blotato-api-key: YOUR_API_KEY

{
  "url": "https://example.com/image.jpg"
}
```

**Response:**

```
{
  "url": "https://database.blotato.com/d1655c49-0bc4-4dd0-88b2-323ce0069fa4.jpg"
}
```

## Presigned Upload (Local Files)

Upload local files directly to Blotato without hosting them on Google Drive or S3 first. This is the recommended approach for MCP users and anyone working with local files.

### Endpoint

**URL:** `/media/uploads`

**Method:** `POST`

**Rate limit:** 120 requests per minute

### Request

#### Request Body

| Field      | Type     | Required | Description                                                                               |
| ---------- | -------- | -------- | ----------------------------------------------------------------------------------------- |
| `filename` | `string` | Yes      | Filename with extension (e.g., "photo.jpg", "video.mp4"). Used to determine content type. |

### Response

**Status Code:** `201 Created`

```json
{
  "presignedUrl": "https://...",
  "publicUrl": "https://..."
}
```

* `presignedUrl` -- upload your file here via HTTP PUT. Expires after a short period.
* `publicUrl` -- the final public URL to use in `mediaUrls` when publishing.

### Complete Example: Upload a Local File and Post It

Three steps: get the presigned URL, upload the file, then publish the post.

**Step 1: Get a presigned upload URL**

```bash
curl -X POST https://backend.blotato.com/v2/media/uploads \
  -H "Content-Type: application/json" \
  -H "blotato-api-key: YOUR_API_KEY" \
  -d '{"filename": "product-photo.jpg"}'
```

Response:

```json
{
  "presignedUrl": "https://database.blotato.com/storage/v1/object/upload/sign/...",
  "publicUrl": "https://database.blotato.com/storage/v1/object/public/.../product-photo.jpg"
}
```

**Step 2: Upload the file to the presigned URL**

```bash
curl -X PUT "PRESIGNED_URL_FROM_STEP_1" \
  -H "Content-Type: image/jpeg" \
  --data-binary @product-photo.jpg
```

Use the correct Content-Type for your file (image/jpeg, image/png, video/mp4, etc.).

**Step 3: Publish a post using the public URL**

```bash
curl -X POST https://backend.blotato.com/v2/posts \
  -H "Content-Type: application/json" \
  -H "blotato-api-key: YOUR_API_KEY" \
  -d '{
    "post": {
      "accountId": "YOUR_ACCOUNT_ID",
      "content": {
        "text": "Check out this product!",
        "mediaUrls": ["PUBLIC_URL_FROM_STEP_1"],
        "platform": "instagram"
      },
      "target": { "targetType": "instagram" }
    }
  }'
```

Max file size depends on your plan. See: [Plan Limits](/settings/billing-and-credits.md#plan-limits) for more details. The presigned URL expires after a short period -- upload the file immediately after receiving it.

### Troubleshooting: presigned PUT fails in Claude Cowork or Claude Desktop

If `POST /v2/media/uploads` returns a `presignedUrl` but the **PUT step** to that URL fails with a sandbox, proxy, network egress, or "Stream closed" error, this is a Claude Cowork / Claude Desktop sandbox restriction, not a Blotato API issue.

Fix it by allowlisting `database.blotato.io` in Cowork's network egress settings. Full steps: [Presigned upload PUT fails / "Sandbox error" / network egress blocked](/api/mcp/faqs.md#presigned-upload-put-fails-sandbox-error-network-egress-blocked-in-claude-cowork-or-claude-desktop).

For Claude Code (sandbox not user-configurable), skip the local upload entirely and pass a public URL into `mediaUrls` directly.

***

## Using Google Drive as a Media Source

Google Drive share links open a viewer page, not a direct file. Blotato needs a direct file URL to fetch and read media.

### Set sharing permissions

For automation workflows (n8n, Make.com), set the entire Google Drive **folder** to "Anyone with the link" as Viewer. All files inside that folder inherit the same permission, so you do not need to set permissions on each file individually.

For one-off uploads, set the individual file to "Anyone with the link" as Viewer.

### Common Google Drive URL Errors

**Folder URLs don't work**: If your link looks like `/drive/folders/...`, it's a folder link. Blotato needs a direct link to a specific file, not a folder.

**Preview URLs don't work reliably**: If your link ends with `/view?...`, it points to Google Drive's preview page, not the raw file.

### Use the direct download URL format

For files under 100MB, replace the standard share link with this format:

```
https://drive.usercontent.google.com/download?id={FILE_ID}&export=download&confirm=t
```

Replace `{FILE_ID}` with the ID from your share link. For example, if your share link is:

```
https://drive.google.com/file/d/1aBcDeFgHiJkLmNoPqRsTuVwXyZ/view
```

The file ID is `1aBcDeFgHiJkLmNoPqRsTuVwXyZ`, so the direct URL is:

```
https://drive.usercontent.google.com/download?id=1aBcDeFgHiJkLmNoPqRsTuVwXyZ&export=download&confirm=t
```

Make sure the file is shared with "Anyone with the link" as Viewer.

### Seeing error "Google Drive can't scan this file for viruses"?

This is the most common issue when using Google Drive. For large videos (100MB+), Google Drive shows a virus-scan popup that blocks Blotato from accessing the file.

<figure><img src="/files/6YniwgUQ1g1DYnzyzFfM" alt=""><figcaption></figcaption></figure>

For large files, use Dropbox, AWS S3, or Google Cloud Storage instead. These services provide direct download URLs without the virus-scan popup.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.blotato.com/api/publish-post/upload-media-v2-media.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
