# Add Watermark to Video & Images – Logo or Text, Bulk (`gazidev/watermark-adder`) Actor

Add a watermark to videos and images in bulk from URLs: text or PNG logo, 9 positions or tiled pattern, opacity, size, margin. JPG/PNG/WebP and MP4 output with download links. No API key. $1.50 per 1,000 images, $0.03 per video minute.

- **URL**: https://apify.com/gazidev/watermark-adder.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 image watermarkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Add Watermark to Video & Images – Logo or Text, Bulk

Add a **watermark to videos and images in bulk** from file URLs. Use your **text** (`© 2026 Your Brand`, `yourshop.com`,
`CONFIDENTIAL`) or your **logo** (PNG with transparency), choose one of **9 positions or a tiled diagonal pattern**, and set
**opacity, size and margin**. Every result gets a public download link: images as **JPG, PNG or WebP**, videos as **MP4
(H.264 + AAC)**. No API key, no software to install, no upload limits of online watermark tools.

- Images **and** videos in one run - paste both lists, or one bulk list (sorted by file extension)
- Text watermark (any language, bold, outline for readability on light and dark areas, optional box) or logo watermark
- Positions: bottom-right, bottom-left, bottom-center, top-right, top-left, top-center, center, center-left, center-right, **tiled** (anti-theft pattern, adjustable angle)
- Size is a percentage of each file's width, so the mark looks the same on a 600 px thumbnail and a 50 MP photo
- Keeps transparency (PNG/WebP), respects EXIF rotation, keeps the colour profile, copies AAC audio without re-encoding
- Optional video downscale (1080p, 720p…) and quality preset
- Pay only for files that were watermarked - errors are free

### What can I use it for?

- **Photographers & stock sellers**: protect previews before sharing them with clients or on social media
- **E-commerce**: add the shop name or logo to all product photos (chain after a product scraper or our Image Downloader)
- **Real estate & car dealers**: brand every listing photo and walkthrough video
- **Social media & agencies**: put a handle (`@yourbrand`) on Reels, Shorts and TikTok clips you produced, in bulk
- **Confidential documents & drafts**: tile `CONFIDENTIAL` / `DRAFT` over screenshots and mock-ups
- **AI agents & automations**: one API call returns watermarked file links (Make, Zapier, n8n, MCP)

### Input

| Field | Default | Notes |
|---|---|---|
| `imageUrls` | demo photo | Direct links to JPG, PNG, WebP, HEIC, AVIF, GIF, BMP, TIFF |
| `videoUrls` | demo 5 s clip | Direct links to MP4, MOV, WebM, MKV, AVI… (not YouTube/TikTok pages) |
| `bulkText`, `datasetId` | – | Paste many URLs / read them from another Actor's dataset |
| `watermarkText` | `© Your Brand` | Ignored when `logoUrl` is set |
| `logoUrl` | – | PNG with transparent background works best |
| `position` | `bottom-right` | 9 positions or `tiled` |
| `opacity` | 70 | % (tiled looks best at 25–40) |
| `size` | 25 | Watermark width in % of the file's width |
| `margin` | 3 | % of the shorter side |
| `textColor`, `boldText`, `textOutline`, `textBackground` | white, bold, outline | Text style |
| `imageFormat` | `same` | `same`, `jpg`, `png`, `webp` |
| `maxResolution` | `1080` | Video downscale; never upscales |

Example - logo in the top-right corner of product photos and a promo video:

```json
{
  "imageUrls": ["https://example.com/products/shoe-1.jpg", "https://example.com/products/shoe-2.png"],
  "videoUrls": ["https://example.com/videos/promo.mp4"],
  "logoUrl": "https://example.com/brand/logo.png",
  "position": "top-right",
  "size": 18,
  "opacity": 80
}
```

Example - tiled anti-theft text:

```json
{ "imageUrls": ["https://example.com/preview.jpg"], "watermarkText": "PREVIEW • yourstudio.com", "position": "tiled", "opacity": 30 }
```

### Output

One dataset row per file; the watermarked file is in the run's key-value store (`fileUrl` is a public link).

```json
{
  "sourceUrl": "https://download.samplelib.com/mp4/sample-5s.mp4",
  "type": "video",
  "status": "ok",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/vid-0001-sample-5s-19afb3.mp4",
  "format": "mp4",
  "width": 1920,
  "height": 1080,
  "duration": 5.759,
  "inputBytes": 2848208,
  "outputBytes": 3113058,
  "billedEvents": { "video-minute-watermarked": 2 },
  "processingSec": 9.4
}
```

Image rows have `format` (jpg/png/webp), `width`, `height`, `sourceFormat`, `inputBytes`, `outputBytes`.
Failed files have `status: "error"`, an `errorCode` (`HTTP_404`, `NOT_AN_IMAGE`, `NOT_MEDIA`, `FILE_TOO_LARGE`,
`NO_VIDEO_STREAM`, `TOO_LONG`, `UNSUPPORTED_PLATFORM`, `MAX_CHARGE_REACHED`…) and are **not charged**.
A `SUMMARY` record in the key-value store has the totals.

### Pricing

Pay per result, no subscription:

| What | Price |
|---|---|
| Image watermarked (up to 16 MP; larger = 1 per started 16 MP) | **$0.0015** ($1.50 per 1,000 images) |
| Video, per started output minute, ≤ 720p | **$0.03** |
| Video, per started output minute, 1080p | $0.06 (2 events) |
| Video, per started output minute, 1440p / 4K | $0.24 (8 events) |
| Actor start | $0.00005 |

Comparison (Apify Store, October 2026):

| Actor | Images | Videos | Logo + text | Tiled | Price |
|---|---|---|---|---|---|
| **This Actor** | ✅ | ✅ | ✅ | ✅ | $0.0015/image, from $0.03/video minute |
| whitewalk/ffmpeg-cloud-mcp (generic FFmpeg) | via FFmpeg commands | ✅ | write your own filter | – | $0.03–0.04 per operation |
| parseforge/watermark-image-api | ✅ | – | – | – | – |

Set a **maximum total charge** on the run: the Actor checks the cost before each file and stops gracefully when the limit is reached.

### FAQ

**Can I use my logo?** Yes - set `logoUrl` to a direct link to a PNG (transparent background works best), WebP or JPG.
Transparent borders are trimmed, then the logo is scaled to `size` % of each file's width.

**Which fonts and languages work?** The text uses DejaVu Sans (Latin, Cyrillic, Greek, Turkish, Vietnamese, symbols like © ® ™ •).

**Does it lower the quality?** Images are saved at quality 90 by default (visually lossless). Videos have to be re-encoded
to burn in the watermark (H.264 CRF 23, `videoQuality: high` = CRF 19); AAC audio is copied unchanged.

**Will the watermark look the same on small and big files?** Yes - `size` and `margin` are percentages, not pixels.

**Can it watermark YouTube/TikTok/Instagram videos?** No. It processes direct links to files you own or are allowed to edit.

**How long can videos be?** Default limit 30 minutes and 500 MB per file (raise `maxDurationMinutes` / `maxFileSizeMb`).
For long or 4K videos give the run more memory (more CPU = faster encoding).

**Can it remove watermarks?** No, it only adds them.

### Use with AI agents / Apify MCP

The Actor works as a tool in the [Apify MCP server](https://mcp.apify.com) (Claude, Cursor, VS Code, ChatGPT and other
MCP clients) and through the Apify API. An agent can say *"put our logo in the bottom-right corner of these 200 product
photos"* or *"add @mybrand to this video"* and get back `fileUrl` links.

```bash
curl -X POST "https://api.apify.com/v2/acts/gazidev~watermark-adder/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"imageUrls":["https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg"],"videoUrls":[],"watermarkText":"© My Shop","position":"bottom-right"}'
```

# Actor input Schema

## `imageUrls` (type: `array`):

Direct links to JPG, PNG, WebP, HEIC/HEIF, AVIF, GIF (first frame), BMP or TIFF files. Video links pasted here are moved to videos automatically.

## `videoUrls` (type: `array`):

Direct links to MP4, MOV, WebM, MKV, AVI… files (your CDN, S3/R2/GCS bucket, Dropbox direct link). Output is MP4 (H.264 + AAC). YouTube/TikTok/Instagram pages are refused - this Actor edits files you own, it is not a downloader.

## `bulkText` (type: `string`):

One URL per line or a CSV with a `url` column. Videos are recognised by their file extension (.mp4, .mov, .webm…), everything else is treated as an image.

## `datasetId` (type: `string`):

Chain after a scraper or our Image Downloader. Sorted into images/videos by file extension.

## `datasetField` (type: `string`):

Arrays of URLs are accepted too.

## `watermarkText` (type: `string`):

E.g. `© 2026 Your Brand`, `yourshop.com`, `CONFIDENTIAL`. Any language (Unicode font). Ignored when a logo URL is set.

## `logoUrl` (type: `string`):

Direct link to your logo: PNG with a transparent background works best (WebP/JPG also OK). Transparent borders are trimmed automatically.

## `position` (type: `string`):

Where the watermark goes. `Tiled` repeats it diagonally over the whole picture - hardest to crop out.

## `opacity` (type: `integer`):

100 = solid, 30–50 = subtle. For tiled patterns 25–40 looks best.

## `size` (type: `number`):

Watermark width as a percentage of each image/video width, so it looks the same on small and large files. Height is capped (18% of the height for text, 50% for logos).

## `margin` (type: `number`):

Distance from the edges as % of the shorter side.

## `textColor` (type: `string`):

Hex color, e.g. `#FFFFFF` (white) or `#000000`.

## `boldText` (type: `boolean`):

Bold font (DejaVu Sans Bold).

## `textOutline` (type: `boolean`):

Thin contrasting outline so the text stays readable on light and dark areas.

## `textBackground` (type: `boolean`):

Semi-transparent rounded box behind the text.

## `textBackgroundColor` (type: `string`):

Hex color of the box.

## `tileAngle` (type: `integer`):

Rotation of the tiled pattern (position `tiled` only). 0 = horizontal rows.

## `imageFormat` (type: `string`):

Transparent images stay transparent in PNG/WebP.

## `imageQuality` (type: `integer`):

90 is visually lossless for most photos.

## `maxResolution` (type: `string`):

Downscale videos so the short side is at most this (never upscales). Billing depends on the OUTPUT resolution: ≤720p 1 unit, 1080p 2 units, above 1080p 8 units per minute.

## `videoQuality` (type: `string`):

H.264 quality. The watermark is burned in, so the video is always re-encoded; audio is copied when it is already AAC.

## `maxFileSizeMb` (type: `integer`):

Larger files are skipped with an error row (not charged). Images are limited to 100 MB.

## `maxDurationMinutes` (type: `integer`):

Cost cap: longer videos are skipped (not charged).

## `maxConcurrency` (type: `integer`):

Downloads in parallel; videos are processed one at a time (ffmpeg already uses every CPU core).

## `proxyConfiguration` (type: `object`):

Only for hosts that block datacenter IPs (HTTP 403).

## Actor input object example

```json
{
  "imageUrls": [
    "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg"
  ],
  "videoUrls": [
    "https://download.samplelib.com/mp4/sample-5s.mp4"
  ],
  "datasetField": "url",
  "watermarkText": "© Your Brand",
  "position": "bottom-right",
  "opacity": 70,
  "size": 25,
  "margin": 3,
  "textColor": "#FFFFFF",
  "boldText": true,
  "textOutline": true,
  "textBackground": false,
  "textBackgroundColor": "#000000",
  "tileAngle": 30,
  "imageFormat": "same",
  "imageQuality": 90,
  "maxResolution": "1080",
  "videoQuality": "balanced",
  "maxFileSizeMb": 500,
  "maxDurationMinutes": 30,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `all` (type: `string`):

No description

## `files` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "imageUrls": [
        "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg"
    ],
    "videoUrls": [
        "https://download.samplelib.com/mp4/sample-5s.mp4"
    ],
    "watermarkText": "© Your Brand",
    "position": "bottom-right",
    "opacity": 70,
    "size": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/watermark-adder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "imageUrls": ["https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg"],
    "videoUrls": ["https://download.samplelib.com/mp4/sample-5s.mp4"],
    "watermarkText": "© Your Brand",
    "position": "bottom-right",
    "opacity": 70,
    "size": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("gazidev/watermark-adder").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "imageUrls": [
    "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg"
  ],
  "videoUrls": [
    "https://download.samplelib.com/mp4/sample-5s.mp4"
  ],
  "watermarkText": "© Your Brand",
  "position": "bottom-right",
  "opacity": 70,
  "size": 25
}' |
apify call gazidev/watermark-adder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/watermark-adder"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7SzaRVtHlzSzUyGXZ/builds/EvOecNdeMgYKmUBpy/openapi.json
