# TikTok Scraper — Hashtag Date Filter, Profiles & Search (`funny_ground/tiktok-scraper`) Actor

Scrape TikTok profiles, hashtags, search results, and direct videos with optional MP4/cover downloads. HTTP-first extraction normally starts no browser or proxy and never uses residential IPs. $0.50 per 1,000 results, as low as $0.40 on volume tiers. A run with 0 valid rows is FAILED.

- **URL**: https://apify.com/funny\_ground/tiktok-scraper.md
- **Developed by:** [Coor Yu](https://apify.com/funny_ground) (community)
- **Categories:** Videos, Social media
- **Stats:** 12 total users, 3 monthly users, 99.9% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## TikTok Scraper — Profiles, Videos, Hashtags & Search

Use one Actor to export TikTok creator videos, hashtag feeds, keyword search results, or known video URLs. Every result includes metadata, engagement statistics, music, mentions, hashtags, and optional MP4 or cover downloads.

Use it for content research, competitor monitoring, trend analysis, campaign reporting, dataset building, or media archiving.

> **Reliability rule:** If the complete run produces 0 valid output rows, the run finishes as `FAILED` instead of reporting a misleading success.

### Ready-to-view example

- [Open the saved direct-video example](https://apify.com/funny_ground/tiktok-scraper/examples/scrape-one-tiktok-video)
- [View its real sample output without running the Actor](https://api.apify.com/v2/datasets/funny_ground~example-tiktok-scraper/items?clean=true)

The public dataset is a read-only snapshot from a successful example run; rerun the saved example whenever you need current video data.

### Why use this Actor

- Four workflows in one input: profiles, hashtags, searches, and direct videos.
- HTTP-first extraction normally starts **no browser and no proxy**.
- Only failed inputs enter a browser fallback restricted to non-residential egress.
- Optional MP4 and cover-image storage.
- One stable output schema across every input type.

### Low-cost tiered pricing

- **Free tier: $0.0005 per result — $0.50 per 1,000.**
- **Bronze: $0.45 per 1,000.**
- **Silver and above: $0.40 per 1,000.**
- A small start event and normal Apify platform usage may also apply; see the **Pricing** tab.
- Only rows written to the dataset incur the result fee.

Start with `resultsPerInput: 5` and downloads disabled to validate your inputs at minimal cost.

### Quick start

```json
{
  "profiles": ["mrbeast"],
  "hashtags": ["roblox"],
  "searchKeywords": ["skincare routine"],
  "videoUrls": [
    "https://www.tiktok.com/@dafeiju7/video/7648048288498863374"
  ],
  "resultsPerInput": 10,
  "dataSource": "auto",
  "shouldDownloadVideos": false,
  "shouldDownloadCovers": false,
  "maxConcurrency": 4
}
```

Provide at least one profile, hashtag, keyword, or video URL. You can mix all four in the same run.

#### Main options

| Field | Default | What it does |
|---|---:|---|
| `profiles` | `[]` | Usernames or full profile URLs. |
| `hashtags` | `[]` | Hashtags without `#`. |
| `searchKeywords` | `[]` | Free-text TikTok video searches. |
| `videoUrls` | `[]` | Known TikTok video URLs; fastest one-record path. |
| `resultsPerInput` | `50` | Maximum videos per profile, hashtag, or keyword; `0` requests all. |
| `dataSource` | `auto` | HTTP first, non-residential browser fallback only for failed inputs. |
| `shouldDownloadVideos` | `false` | Store MP4 files in the Key-Value Store. |
| `shouldDownloadCovers` | `false` | Store cover images in the Key-Value Store. |
| `maxConcurrency` | `4` | Parallel seed tasks; HTTP calls are rate-limited automatically. |
| `proxy` | DEFAULT | Browser fallback only; groups, custom URLs, and disabling are ignored. |

Use `dataSource: "tikwm"` to guarantee that no browser or proxy is started. Use `browser` only for troubleshooting.

### Hashtag date filter

`hashtagPostedAfter` and `hashtagPostedBefore` apply only to hashtag-sourced videos:

```json
{
  "hashtags": ["fashion"],
  "hashtagPostedAfter": "2026-07-01",
  "hashtagPostedBefore": "2026-08-01"
}
```

Date-only values are interpreted at midnight UTC. Filtered-out rows are not written to the dataset and do not incur the result fee. Hashtag feeds are not perfectly chronological, so broader result limits may be needed for narrow date windows.

### Output

One dataset row represents one TikTok video:

```json
{
  "videoId": "7648048288498863374",
  "webVideoUrl": "https://www.tiktok.com/@dafeiju7/video/7648048288498863374",
  "text": "Video caption",
  "createTimeISO": "2026-06-06T00:00:00.000Z",
  "duration": 18,
  "videoDownloadUrl": "https://...",
  "coverUrl": "https://...",
  "author": {
    "id": "123",
    "uniqueId": "creator",
    "nickname": "Creator",
    "avatar": "https://..."
  },
  "music": {
    "id": "456",
    "title": "Original Sound",
    "author": "creator",
    "playUrl": "https://..."
  },
  "stats": {
    "playCount": 100000,
    "diggCount": 9000,
    "commentCount": 300,
    "shareCount": 120,
    "collectCount": 500
  },
  "hashtags": ["example"],
  "mentions": [],
  "sourceType": "direct",
  "sourceInput": "https://www.tiktok.com/...",
  "scrapedAt": "2026-07-28T00:00:00.000Z"
}
```

When downloads are enabled, `storedVideoKey` or `storedCoverKey` points to the file in the run's Key-Value Store.

### How the low-cost mode works

The default path uses lightweight public HTTP endpoints for profile posts, hashtag feeds, searches, and video details. Successful inputs are pushed immediately. Playwright and Apify Proxy are initialized only for failed inputs in `auto` mode.

The fallback accepts only Apify's non-residential `DEFAULT` pool. Residential groups, custom URLs, proxy disabling, and the legacy `fallbackToResidential` setting are ignored to prevent accidental residential costs.

### 中文速览

一个 Actor 同时支持 TikTok 达人主页、话题、关键词搜索和单视频链接。默认轻量 HTTP 优先，不启动浏览器或代理；免费层 **每 1,000 条 $0.50**，批量层最低 **$0.40/1,000**。设置 `dataSource=tikwm` 可严格保证 HTTP-only。

### Limitations and responsible use

- Only public and currently available content can be returned.
- Counts and media URLs are snapshots; signed media URLs may expire.
- The HTTP path depends on an independent public upstream service; `auto` provides a non-residential browser fallback.
- Media downloads increase runtime, storage, and platform usage.
- Collect and use public data in accordance with applicable laws and platform terms.

### Zero-result policy

A run that produces no valid video rows finishes as `FAILED`. In `auto` mode, a zero-result HTTP response first receives the existing non-residential browser fallback before the run is failed.

# Actor input Schema

## `profiles` (type: `array`):

TikTok usernames (without @) or full profile URLs.

## `hashtags` (type: `array`):

Hashtags without #.

## `hashtagPostedAfter` (type: `string`):

Only return hashtag videos posted on or after this date/time (inclusive). Accepts ISO 8601 — date-only (e.g. 2026-05-01) is anchored to 00:00:00 UTC; date-time (e.g. 2026-05-01T00:00:00Z) is used as-is. Leave empty to disable. Only applies to videos from the `hashtags` input — profiles / search / direct URLs ignore this filter.

## `hashtagPostedBefore` (type: `string`):

Only return hashtag videos posted strictly before this date/time (exclusive). Same format as `hashtagPostedAfter`. Leave empty for no upper bound. Tip: hashtag feeds aren't strictly chronological, so set `resultsPerInput` ~3-5× the count you actually need — the rest will be filtered out.

## `searchKeywords` (type: `array`):

Free-text search queries.

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

Direct TikTok video URLs to scrape. Skips listing and goes straight to detail page — fastest path when you already know the video IDs.

## `dataSource` (type: `string`):

Auto (recommended) uses lightweight HTTP first and starts the non-residential browser fallback only for failed inputs. HTTP only guarantees no browser or proxy. Browser forces the legacy path.

## `resultsPerInput` (type: `integer`):

Max videos to return for each profile / hashtag / keyword. Set to 0 for unlimited (until end of feed).

## `shouldDownloadVideos` (type: `boolean`):

If true, store video MP4 files in Apify Key-Value Store. WARNING: significantly increases cost & runtime.

## `shouldDownloadCovers` (type: `boolean`):

If true, store video thumbnail JPGs in Apify Key-Value Store.

## `proxy` (type: `object`):

Country setting for the default non-residential Apify Proxy pool. Proxy groups, custom proxy URLs, and proxy disabling are ignored so this Actor never uses residential IPs.

## `fallbackToResidential` (type: `boolean`):

Ignored. The actor never uses or switches to residential IPs.

## `blockImages` (type: `boolean`):

Abort image requests to cut proxy bandwidth and speed up page loads. Turn off only if your target route starts returning 0 results.

## `headless` (type: `boolean`):

Run Playwright in headless mode. Set false only when debugging locally.

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

Max simultaneous browser pages. Higher = faster but more rate-limit risk.

## `requestTimeoutSecs` (type: `integer`):

Page load timeout in seconds.

## `maxAttemptsPerSeed` (type: `integer`):

Maximum browser attempts for each profile / hashtag / direct URL before giving up. Lower values improve speed when TikTok blocks non-residential IPs.

## `maxSearchAttempts` (type: `integer`):

Maximum browser attempts for each search keyword. Search is heavily blocked on non-residential proxies; the default fails fast for speed, and you can raise it for more retry coverage.

## Actor input object example

```json
{
  "profiles": [
    "khaby.lame",
    "https://www.tiktok.com/@charlidamelio"
  ],
  "hashtags": [
    "fyp",
    "comedy"
  ],
  "hashtagPostedAfter": "2026-05-01",
  "hashtagPostedBefore": "2026-05-19",
  "searchKeywords": [
    "summer fashion 2026",
    "AI tools"
  ],
  "videoUrls": [
    "https://www.tiktok.com/@khaby.lame/video/7321234567890123456"
  ],
  "dataSource": "auto",
  "resultsPerInput": 50,
  "shouldDownloadVideos": false,
  "shouldDownloadCovers": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  },
  "fallbackToResidential": false,
  "blockImages": true,
  "headless": true,
  "maxConcurrency": 4,
  "requestTimeoutSecs": 45,
  "maxAttemptsPerSeed": 2,
  "maxSearchAttempts": 1
}
```

# Actor output Schema

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

Scraped TikTok videos from the default dataset, displayed with the overview table view.

# 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 = {
    "dataSource": "auto",
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("funny_ground/tiktok-scraper").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 = {
    "dataSource": "auto",
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("funny_ground/tiktok-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "dataSource": "auto",
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}' |
apify call funny_ground/tiktok-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=funny_ground/tiktok-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/bgrvYRe7WFec4Osbq/builds/5v3Cdb1HeZikoSjpd/openapi.json
