# TikTok Video Downloader & Metadata API (`alex_fodo/tiktok-scraper-no-watermark`) Actor

Download public TikTok videos as MP4 files stored in Apify. Process up to 50 links per run and extract authors, views, likes, comments and video metadata. Get saved file URLs and structured JSON for archives, analytics and automated workflows.

- **URL**: https://apify.com/alex_fodo/tiktok-scraper-no-watermark.md
- **Developed by:** [AGENT DOJO](https://apify.com/alex_fodo) (community)
- **Stats:** 3 total users, 2 monthly users, 92.9% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $5.00 / 1,000 saved mp4 with metadata

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## TikTok Video Downloader & Metadata API

Save public TikTok videos as MP4 files in Apify and collect their titles, authors and engagement metrics. Use saved files for your video archive, or choose metadata-only mode for data workflows. Full video URLs and TikTok short links are supported.

Formats explicitly flagged as watermarked are skipped. Other formats may have unknown watermark status; this Actor does not guarantee watermark-free output or remove logos.

### Quick start

```json
{
  "urls": ["https://www.tiktok.com/@waffleskkw/video/7678161526284602644"],
  "downloadVideo": true,
  "requireUnmarkedFormat": false,
  "maxVideoSizeMb": 25,
  "proxyConfiguration": {"useApifyProxy": false}
}
```

Provide 1–50 links per run. Profiles, photo posts and other platforms are unsupported. Duplicate input URLs and duplicate video IDs produce at most one result per run. MP4 access verification is always enabled; there is no separate switch to configure. Saved files are limited to 25 MiB by default, configurable up to 50 MiB. Downloads are processed one at a time with a 3-second pause between video attempts. The extractor also pauses 1 second between supported metadata requests. These are conservative defaults, not a guaranteed safe TikTok rate.

### What you receive

Successful dataset rows include `status`, `video_id`, `title`, `author`, `views`, `likes`, `comments`, `duration_seconds`, `cover_image`, `direct_download_url`, `media_headers`, `media_url_verified`, `watermark_status`, `watermark_checked_visually` and `scraped_at`. Saved videos also include `video_file_url`, `video_file_key` and `video_size_bytes`. Error rows contain the input URL, status, error code/message and collection timestamp instead of full video metadata.

Use `video_file_url` for a saved MP4. Apify storage retention depends on the account and storage lifecycle; copy files to your own storage if you need long-term retention. The URL returned by Apify can be accessed by anyone who knows it. Temporary `direct_download_url` links can expire or depend on the original session/IP. Access verification is a check at collection time, not a guarantee that a TikTok URL works from another client.

- `ok`: metadata and verified MP4 access; in saved-file mode the MP4 was also downloaded and saved before the result is delivered.
- `partial`: metadata is available, but a usable media result was not completed.
- `error`: the input was invalid or TikTok could not be accessed.

Error codes include `INVALID_URL`, `RATE_LIMITED`, `STORAGE_FAILED`, `RUN_SIZE_LIMIT`, `BLOCKED`, `TIMEOUT`, `UNAVAILABLE`, `EXTRACTION_FAILED`, `NO_DIRECT_MP4`, `MEDIA_ACCESS_FAILED`, `MEDIA_DOWNLOAD_FAILED`, `VIDEO_TOO_LARGE`, `MEDIA_DOWNLOAD_TIMEOUT`, `INVALID_MP4`, `ONLY_WATERMARKED_FORMATS` and `WATERMARK_NOT_CONFIRMED`. Missing metrics remain null. A run stops on a rate-limit response (`RATE_LIMITED`), or after three consecutive access, network or storage failures to limit wasted work. A storage failure produces a partial row without a result fee. `OUTPUT` contains result counts and the stop reason. Zero complete results fail the run, except graceful event-budget, processing-time or total-size stops. Mixed or early-stopped batches have PARTIAL in OUTPUT.

### Can I download without the TikTok watermark?

The Actor retrieves available source MP4 formats; it does not erase logos, crop frames or remove text embedded by the creator. Formats explicitly marked as watermarked are never returned or saved. An explicit unmarked flag/format label is preferred over an unknown format, even when the unknown format has a higher resolution.

`requireUnmarkedFormat` controls how uncertainty is handled:

| Setting | Result |
| --- | --- |
| `false` (default) | Unknown formats are allowed and remain `watermark_status: "unknown"`. A complete delivered result can be billed; this is not a guarantee of a watermark-free video. |
| `true` | Only formats described as unmarked by the extractor are allowed. Unknown formats return `partial` / `WATERMARK_NOT_CONFIRMED`, with no result fee or delivered media link. |

When only marked MP4 formats are available, the result is `partial` / `ONLY_WATERMARKED_FORMATS`; no file is saved and no result fee is charged. Apify platform/proxy usage can still apply to these attempts.

`watermark_status` is `present`, `not_reported` or `unknown`. `not_reported` means extractor metadata explicitly indicates an unmarked format; it does not prove the absence of a logo in the pixels. `watermark_checked_visually` is always false: this Actor has no automatic visual watermark detector.

For workflows that cannot accept unknown formats, use:

```json
{
  "urls": ["https://www.tiktok.com/@waffleskkw/video/7678161526284602644"],
  "downloadVideo": true,
  "requireUnmarkedFormat": true
}
```

Strict mode may return no complete results when watermark metadata is unknown, including for the example URL above. It relies on format metadata, not visual detection; original captions and embedded creator branding remain.

### Pricing

The Actor uses pay per event + platform usage. Result fees are:

| Mode | Per complete result | Per 1,000 complete results |
| --- | --- | --- |
| Metadata + verified temporary MP4 link | $0.001 | $1 |
| Saved MP4 + metadata | $0.005 | $5 |

One result triggers only the fee for the selected mode. Failed, partial and duplicate results have no result fee. The **pay per event + usage** model also charges Apify platform usage separately, including resources consumed by failed requests and configured proxies. These result prices are not the total run cost. Check the active pricing in Apify Console/Store before starting a run.

The Actor stops processing new paid results when the result-event spending limit is reached. Under a usage-billed configuration, platform usage is separate from that limit.

### Limits

TikTok can block requests or restrict availability by region. Optional proxies may improve access but incur costs and do not guarantee success. The Actor does not remove watermarks. Watermark handling and strict-mode limitations are described above. No account cookies or login are required from users.

Socket timeout is per network operation, not a total run deadline. Each file download also has a 60-second processing bound, checked between reads. Use 512 MB. For the default 600-second processing limit, set the Apify run timeout to at least 900 seconds so the last operation and output persistence can finish. An external timeout or forced abort can prevent the final summary from being written.

### Batch controls and continuation

| Input | Default | Allowed | Purpose |
| --- | --- | --- | --- |
| `requestIntervalSecs` | 3 | 1–30 | Minimum pause after a video attempt, before the next valid video. |
| `maxRunSecs` | 600 | 30–3,600 | Soft processing limit checked before starting another video; an active network operation can overrun it. |
| `maxTotalVideoSizeMb` | 250 | 1–1,000 | Total successful downloaded payload limit in MiB, including files whose storage delivery fails and duplicate short-link downloads. |

The size limit excludes probes, metadata, unsuccessful/partial transfers and platform overhead; it is not a total bandwidth or money cap. A file that exceeds the remaining batch allowance returns `partial` / `RUN_SIZE_LIMIT`, without a result fee, and stops the run. A file larger than its per-video limit returns `VIDEO_TOO_LARGE`.

The `OUTPUT` record includes `schema_version`, `input_count`, `processed_count`, `duplicate_count`, `successful`, `partial`, `failed`, `charged_count`, `downloaded_bytes`, `elapsed_seconds`, `stop_reason`, `remaining_urls` and `retry_urls`. `processed_count` counts dataset rows; skipped duplicates have no row.

To continue, create a new run with `remaining_urls` and the same options. `retry_urls` contains valid inputs that produced incomplete results; review their error codes before retrying. For `RATE_LIMITED`, defer the next run rather than immediately retrying. Unavailable, oversized or strict-watermark-rejected videos may remain incomplete on retry. Deduplication applies within a run only; resubmitting successful videos in another run can incur a new fee.

Stop reasons: `BUDGET_LIMIT`, `RUN_TIME_LIMIT`, `RUN_SIZE_LIMIT`, `RATE_LIMITED`, `ACCESS_FAILURE_LIMIT`, or null when the batch finishes. There is no automatic cross-run continuation or guaranteed exactly-once billing across crashes/resurrections. Each run has its own request pacing; concurrent runs can still encounter shared network limits.

### Calling the API

Use your Apify token from an environment variable. Submit a run asynchronously, poll its status, then retrieve its dataset and summary. Avoid synchronous requests for large batches.

```bash
curl --fail-with-body -X POST \
  'https://api.apify.com/v2/acts/fyHpwU1X693apdSwT/runs?memory=512&timeout=900' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary @input.json
```

This request uses the Actor's configured default build. To pin a specific build, add the `build` query parameter. The response contains the run ID, `defaultDatasetId` and `defaultKeyValueStoreId`. Use `GET /v2/actor-runs/{runId}` to poll, `GET /v2/datasets/{defaultDatasetId}/items?format=json` for results, and `GET /v2/key-value-stores/{defaultKeyValueStoreId}/records/OUTPUT` for the summary, with the same authorization header. Check every dataset row: a successful platform run may still contain partial or error results.

# Actor input Schema

## `urls` (type: `array`):

1–50 HTTPS TikTok video or short links. Other platforms, profiles and photo posts are unsupported.

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

Optional Apify/custom proxy. Different sessions are used per item; access is not guaranteed.

## `socketTimeoutSecs` (type: `integer`):

Per network operation timeout, not a total per-video deadline.

## `retries` (type: `integer`):

Maximum network and extraction retry counts.

## `downloadVideo` (type: `boolean`):

Save a bounded MP4 with metadata. False returns metadata and a temporary TikTok URL.

## `maxVideoSizeMb` (type: `integer`):

Larger videos return VIDEO_TOO_LARGE without a result fee.

## `requireUnmarkedFormat` (type: `boolean`):

Always skip formats flagged with a watermark. True also rejects unknown watermark metadata with WATERMARK_NOT_CONFIRMED and no result fee. This uses extractor metadata, not visual watermark detection.

## `requestIntervalSecs` (type: `integer`):

Minimum pause after each attempted video. Extraction also uses a 1-second pause between supported metadata requests. No guaranteed TikTok-safe rate.

## `maxRunSecs` (type: `integer`):

Soft limit: stop starting videos after this time. An in-flight operation may exceed it; set the platform timeout higher.

## `maxTotalVideoSizeMb` (type: `integer`):

Bounds successful downloaded payloads in one run; excludes metadata, probes, failed transfers and platform overhead. Remaining URLs are returned in OUTPUT.

## Actor input object example

```json
{
  "urls": [
    "https://www.tiktok.com/@waffleskkw/video/7678161526284602644"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "socketTimeoutSecs": 15,
  "retries": 1,
  "downloadVideo": true,
  "maxVideoSizeMb": 25,
  "requireUnmarkedFormat": false,
  "requestIntervalSecs": 3,
  "maxRunSecs": 600,
  "maxTotalVideoSizeMb": 250
}
```

# Actor output Schema

## `results` (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 = {
    "urls": [
        "https://www.tiktok.com/@waffleskkw/video/7678161526284602644"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("alex_fodo/tiktok-scraper-no-watermark").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 = { "urls": ["https://www.tiktok.com/@waffleskkw/video/7678161526284602644"] }

# Run the Actor and wait for it to finish
run = client.actor("alex_fodo/tiktok-scraper-no-watermark").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 '{
  "urls": [
    "https://www.tiktok.com/@waffleskkw/video/7678161526284602644"
  ]
}' |
apify call alex_fodo/tiktok-scraper-no-watermark --silent --output-dataset

```

## MCP server setup

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

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/fyHpwU1X693apdSwT/builds/jkueFeIbyVHLwo8tC/openapi.json
