# TikTok Profile, Hashtag, Search, Video & Trending Scraper (`abotapi/tiktok-scraper`) Actor

Scrape TikTok without login. Extract profiles, bios, follower stats and videos; search by hashtag or keyword; scrape individual videos with full engagement stats; or collect posts from the current trending feed.

- **URL**: https://apify.com/abotapi/tiktok-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Social media, Videos, Automation
- **Features:** Limited permissions
- **Stats:** 105 total users, 25 monthly users, 89.6% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.20 / 1,000 results

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

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 Scraper: Profiles, Videos, Hashtag Search & Trending Feed

TikTok turns into a clean, structured feed with this scraper. Give it usernames to get a public profile plus its videos, hashtags or keywords to get matching videos, direct video links to get full stats for a single video, or run it with no input at all to get the current trending feed. No login is ever required. Export the result to JSON, CSV or Excel, or pull it straight into your app through the API.

### Why This Scraper?

- **Five ways in.** Profile, hashtag/keyword search, video, trending, and paste-any-link URL mode in one actor, so you do not need separate tools for each job.
- **Profiles.** Profile mode returns the full public profile (bio, avatar, follower/following/heart/video counts, verified flag) plus a paginated list of that account's videos.
- **Search.** Search mode accepts a plain keyword or a hashtag (start the term with #) and returns matching videos either way.
- **Video stats.** Video mode returns full stats for a single video: plays, likes, comments, shares and saves, plus music info, hashtags, duration and cover images.
- **Trending.** Trending mode needs no input at all beyond the mode itself; run it as-is to pull the current trending feed.
- **Public data only.** Public data only: the actor never logs in, so it can only read what is publicly visible; private accounts and their videos return no data.
- **Honest run reporting.** Clear, honest run reporting: a run that could not read anything fails loudly, so an empty dataset always means "nothing matched", never "something broke".

### Use Cases

- **Social listening and brand monitoring:** track a hashtag or keyword and pull matching videos as they post.
- **Creator and competitor research:** collect a profile's stats and video history to benchmark growth and content.
- **Trend and sound discovery:** scan the trending feed or a hashtag to see what is gaining traction right now.
- **Dataset building for analytics or ML:** collect video metadata (captions, stats, hashtags, music) at scale for research.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live profile or video.

| Field | Example |
| --- | --- |
| `recordType` | `"video"` (also `"profile"`) |
| `sourceMode` | `"search"` (also `"profile"`, `"video"`, `"trending"`) |
| `id` | `"7000000000000000000"` (video only) |
| `url` | link to the video or profile |
| `uniqueId` | `"samplecreator"` (profile only) |
| `nickname` | `"Sample Creator"` (profile only) |
| `authorUsername` | `"samplecreator"` (video only) |
| `desc` | `"Sample video caption #sample"` (video only) |
| `playCount` | `500000` (video only) |
| `diggCount` | `42000` (likes; video and profile both carry this field) |
| `commentCount` | `1200` (video only) |
| `shareCount` | `1200` (video only) |
| `collectCount` | `3300` (saves; video only) |
| `followerCount` | `125000` (profile only) |
| `followingCount` | `340` (profile only) |
| `heartCount` | `2400000` (total likes received; profile only) |
| `videoCount` | `210` (profile only) |
| `verified` | `false` (profile only) |
| `createTimeIso` | `"2026-01-01T00:00:00.000Z"` (video only) |
| `scrapedAt` | `"2026-01-01T00:00:00.000Z"` |

Video records also carry `authorNickname`, `authorSecUid`, `authorAvatarUrl`, `duration`, `width`, `height`, `musicId`, `musicTitle`, `musicAuthor`, `musicUrl`, `hashtags`, `coverUrl`, `dynamicCoverUrl`, `originCoverUrl` and `region`. Profile records also carry `secUid`, `signature` (bio), `avatarUrl`, `privateAccount` and `bioLink`.

### How to Use

1. Pick a **mode**: `profile` (usernames or profile URLs), `search` (hashtags or keywords), `video` (direct video URLs), `trending` (no input needed), or `url` (paste any TikTok link, see URL mode below).
2. Fill in the field for that mode; leave the other mode fields blank.
3. Set **Max results per unit** to control how many videos come back per username, per query, or from the feed, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Profile mode:**

```json
{
  "mode": "profile",
  "usernames": ["samplecreator", "@anothercreator"],
  "maxItems": 20
}
```

**Hashtag or keyword search:**

```json
{
  "mode": "search",
  "queries": ["#fyp", "cooking tutorial"],
  "maxItems": 20
}
```

**Single video, full stats:**

```json
{
  "mode": "video",
  "videoUrls": ["https://www.tiktok.com/@samplecreator/video/7000000000000000000"]
}
```

**Any TikTok link (URL mode):**

```json
{
  "mode": "url",
  "startUrls": ["https://www.tiktok.com/@tiktok", "https://www.tiktok.com/@tiktok/video/7673909736131038495"],
  "maxItems": 20
}
```

Trending mode needs only `{"mode": "trending", "maxItems": 20}`.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/tiktok-scraper").call(run_input={"mode": "search", "queries": ["#fyp"], "maxItems": 20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get("desc"), item.get("playCount"))
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/tiktok-scraper').call({ mode: 'search', queries: ['#fyp'], maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### URL mode

Set **Mode** to `url` and paste links; each is routed by shape. Anything else gets a warning in the run log and is counted as unreachable, never silently skipped.

| URL shape | Example | Routed to |
|---|---|---|
| Profile | `https://www.tiktok.com/@tiktok` | profile mode |
| Video | `https://www.tiktok.com/@tiktok/video/7673909736131038495` | video mode |
| Hashtag | `https://www.tiktok.com/tag/fyp` | hashtag search |
| Keyword search | `https://www.tiktok.com/search?q=cats` | keyword search |
| Explore | `https://www.tiktok.com/explore` | trending feed |
| Mobile host | `https://m.tiktok.com/@tiktok` | rewritten to www.tiktok.com |
| Short link | `https://vm.tiktok.com/ZMabc/` | resolved to the profile or video it points to |

URL mode is new. Left untouched, the TikTok URLs field scrapes the two example links (the @tiktok profile and one video). Hashtag, search, `/explore` and short links are accepted and routed, but are not part of the default. Plain names and `#tags` already work in search mode.

#### Video links must be the full link

Short share links (`vm.tiktok.com`, `vt.tiktok.com`) are not resolved in video mode. Open the share link once in a browser, copy the full address it redirects to (it looks like `https://www.tiktok.com/@username/video/1234567890`), and paste that instead.

#### Post filters

The date and likes filters are applied to the videos the actor reads, after they are read; **Max results per unit** still caps what is returned. They apply in every mode, including trending, video links and `/explore` in URL mode. A run where every video is filtered out ends as an honest empty result, not a failure.

#### Output views

The dataset has four views in the Console: **Results** (all records), **Profiles** (profile records only), **Videos** (video records only) and **Changes** (rows tagged by incremental mode).

#### How Max results per unit adds up

The cap applies per unit, not to the whole run: per username in profile mode, per hashtag or keyword in search mode, or to the whole feed in trending mode. Three usernames with a cap of 20 can return up to 60 video records plus the 3 profile records themselves, since a profile record does not count against the video cap. Set it to `0` to walk a unit until it runs out on its own. Only rows actually written count toward the cap: rows skipped because an earlier run already delivered them (resume) or because they are `UNCHANGED` do not use it up, so a resumed run delivers up to the cap of new rows. Every run also saves an `OUTPUT` record in the run's key-value store listing, per username, query or link, its status and row count.

### Send results into your apps (MCP connectors)

The input form includes an optional **Pipe results into your apps** field for Model Context Protocol connectors, but connector export is not active in the current build of this actor: selecting a connector has no effect, and the run log notes that nothing was sent. Your results are always in the dataset; to move them into Notion, Airtable, Google Sheets or similar apps today, use the **Integrations** tab (Make, Zapier, n8n, webhooks) or the API.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `profile` | `profile`, `search`, `video`, `trending`, or `url`. |
| `usernames` | array | `["tiktok"]` | Usernames or profile URLs, with or without @. Profile mode only. |
| `queries` | array | `["#fyp"]` | Hashtags (start with #) or plain keywords. Search mode only. |
| `videoUrls` | array | one sample video link | Direct TikTok video URLs. Video mode only. |
| `startUrls` | array | the @tiktok profile and one example video | TikTok links for `url` mode, routed by shape (see URL mode). |
| `oldestPostDate` | string | (none) | Keep only videos posted on or after this date: `YYYY-MM-DD`, an ISO timestamp, or a relative value such as `30 days`. |
| `newestPostDate` | string | (none) | Keep only videos posted on or before this date, same formats. |
| `leastDiggs` | integer | (none) | Keep only videos with at least this many likes. |
| `mostDiggs` | integer | (none) | Keep only videos with at most this many likes. |
| `maxItems` | integer | `3` | Max results per unit: videos per username, per query, or from the feed. `0` = unlimited; values above 1,000,000 are rejected. Not used in video mode: every video URL is fetched once. |
| `resumeFromRunId` | string | (none) | Run or dataset ID of an earlier run; records it already holds are skipped. |
| `incrementalMode` | boolean | `false` | Tag rows `NEW` / `UPDATED` / `UNCHANGED` / `REAPPEARED` against the remembered previous run of the same targets; unchanged rows are not pushed or charged. Signed link fields that change on every fetch (`coverUrl`, `dynamicCoverUrl`, `originCoverUrl`, `authorAvatarUrl`, `avatarUrl`, `musicUrl`, `musicCoverMediumUrl`, `subtitleLinks`, `slideshowImageLinks`) and `scrapedAt` never count as a change. |
| `stateKey` | string | (automatic) | Name of the remembered baseline. Empty keys it on mode, targets and filters. |
| `emitUnchanged` | boolean | `false` | Incremental mode: also push `UNCHANGED` rows. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode: field names (for example `playCount`) not counted as a change. |
| `proxyTier` | string | `residential` | `residential` (default) or `datacenter`. See Plan Requirement below. |
| `proxy` | object | (none; prefilled with Apify Proxy on) | Advanced connection settings. Leave `apifyProxyGroups` unset so `proxyTier` above decides Residential vs Datacenter. |
| `mcpConnectors` | array | (none) | Reserved for connector export, which is not active in the current build; leave empty. |
| `notionParentPageUrl` | string | (none) | Only used by connector export (not active in the current build). |
| `maxNotifyListings` | integer | `50` | Only used by connector export (not active in the current build). |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "recordType": "video",
  "sourceMode": "search",
  "id": "7000000000000000000",
  "url": "https://www.tiktok.com/@samplecreator/video/7000000000000000000",
  "desc": "Sample video caption #sample",
  "createTime": 1767225600,
  "createTimeIso": "2026-01-01T00:00:00.000Z",
  "authorUsername": "samplecreator",
  "authorNickname": "Sample Creator",
  "authorSecUid": "MS4wSample0000000000000000000000000000000000000000000",
  "authorAvatarUrl": "https://p16-sign-va.tiktokcdn.com/sample-avatar.jpeg",
  "duration": 18,
  "width": 1080,
  "height": 1920,
  "playCount": 500000,
  "diggCount": 42000,
  "commentCount": 1200,
  "shareCount": 1200,
  "collectCount": 3300,
  "musicId": "7000000000000000001",
  "musicTitle": "Sample Sound",
  "musicAuthor": "Sample Artist",
  "musicUrl": "https://sf16-ies-music.tiktokcdn.com/sample-music.mp3",
  "hashtags": ["sample", "fyp"],
  "coverUrl": "https://p16-sign-va.tiktokcdn.com/sample-cover.jpeg",
  "dynamicCoverUrl": "https://p16-sign-va.tiktokcdn.com/sample-dyncover.jpeg",
  "originCoverUrl": "https://p16-sign-va.tiktokcdn.com/sample-origcover.jpeg",
  "region": "US",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan Requirement

The default **Proxy tier** is Residential. Datacenter was measured to work too: profile and video pages loaded with full data through the datacenter tier in our checks, so it is a valid cheaper choice, though TikTok can answer some connections with a short "Please wait" challenge page (HTTP 200, no data) and results can vary by run. The actor treats that page as a failed load, never as an empty result, and tries the next connection.

Apify free plans do not include Residential proxies. On a free plan the run logs that the selected connection is unavailable and continues on a backup connection; some pages may still be unavailable. If nothing can be loaded the run fails with one of these messages:

- "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required."
- "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested TikTok pages."
- "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."

A paid plan with Residential access, or your own proxy URLs, avoids all three.

### FAQ

#### How much does it cost?

This actor is pay per event, and two events are billed: **Actor start** (once per run, scaled by the memory the run uses, per GB) and **Result** (once per record written to the dataset: each profile, video or search hit). Nothing else is charged; video mode has no separate per-video fee. The **Pricing** tab shows the current rates. Use **Max results per unit** to cap the cost of any run.

#### Is it legal to scrape TikTok?

This actor collects only publicly available profile and video data; login is never required. You are responsible for how you use it: follow TikTok's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Video, cover and music files themselves may belong to their creators or rights holders even though the metadata is public.

#### Can I run this on a schedule to catch new videos?

Yes, schedule it from the **Schedules** tab. Turn on **Incremental mode** to tag each row `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` or `REAPPEARED` against the previous run of the same targets (`firstSeenAt` / `lastSeenAt` are added too). Unchanged rows are not pushed and so are not billed unless you enable **Emit unchanged**. Link fields that TikTok re-signs on every fetch (`coverUrl`, `dynamicCoverUrl`, `originCoverUrl`, `authorAvatarUrl`, `avatarUrl`, `musicUrl`, `musicCoverMediumUrl`, `subtitleLinks`, `slideshowImageLinks`) and `scrapedAt` are ignored when deciding whether a row changed, so a changed cover image alone does not make a row `UPDATED`. Engagement counters change constantly, so list the ones you do not care about in **Ignore fields when detecting changes**. Videos that disappear are not reported, because TikTok feeds have no end marker. With it off, output is exactly as before. To continue an interrupted run, paste its run ID into **Resume from run or dataset ID**.

#### Why does video mode say it could not find a video?

Video mode needs the full link, `https://www.tiktok.com/@username/video/1234567890`. Short share links (`vm.tiktok.com`, `vt.tiktok.com`) are not resolved; open one in a browser first and copy the full address it redirects to.

#### Why did my run fail instead of returning an empty dataset?

A run fails only when it collected 0 records and at least one target could not be read at all, or loaded without carrying any usable data; the status message says which. A keyword or hashtag that genuinely matches nothing still ends as a successful, honestly empty run. If some targets were read and others were not, the run stays green with a warning: the dataset is just incomplete for the targets that failed. For trending mode specifically, a run that fails outright most often means the feed's underlying request shape changed; please report it so the mapping can be refreshed.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client like any other actor. Sending results to connected apps through the connector field is not active in the current build; use the Integrations tab instead.

### 🔗 Want more TikTok data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🧩 <a href="https://apify.com/abotapi/tiktok-comments-scraper"><b>TikTok Comments Scraper</b></a><br>Scrape comments from TikTok videos using one or more video URLs or IDs. Extract comment...</td><td>📱 <a href="https://apify.com/abotapi/tiktok-live-recorder"><b>Tiktok Live Recorder</b></a><br>Record TikTok live streams to MP4 with full metadata, all stream quality URLs, and...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/tiktok-shop-scraper"><b>TikTok Shop Scraper</b></a><br>Scrape TikTok Shop by keyword, category or product URL. Extract prices, discounts, sales...</td><td>📱 <a href="https://apify.com/abotapi/mastodon-social-scraper"><b>Mastodon</b></a><br>From $1/1K. Scrape trending Mastodon profiles and related posts from any Mastodon...</td></tr>
<tr><td>📱 <a href="https://apify.com/abotapi/lemon8-profile-scraper"><b>Lemon8 Profile Scraper</b></a><br>Scrape Lemon8 user profiles with automated multi-profile discovery. Extract profile data...</td><td>📱 <a href="https://apify.com/abotapi/lemon8-discover-scraper"><b>Lemon8 Search Scraper</b></a><br>Scrape Lemon8 posts by keyword across multiple regions. Extract posts, images, videos...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/tiktok-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

profile: usernames or profile URLs -> profile info + their videos. search: hashtags or keywords -> matching videos. video: direct video URLs -> full video stats. url: paste TikTok links of any supported shape (routed automatically). trending: current trending feed, no input needed.

## `startUrls` (type: `array`):

Profile (/@user) and video (/@user/video/id) links are confirmed; hashtag (/tag/x), search (/search?q=), /explore and vm./vt. short links are accepted and routed to the matching lane. Only used when mode = url. Left untouched, it scrapes one example profile and one example video.

## `usernames` (type: `array`):

One or more TikTok usernames (with or without @) or full profile URLs. Only used when mode = profile.

## `queries` (type: `array`):

One or more search terms. Start a term with # to search that hashtag (e.g. "#fyp"); anything else is treated as a keyword search. Only used when mode = search.

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

One or more direct TikTok video URLs (https://www.tiktok.com/@user/video/1234567890). Only used when mode = video.

## `maxItems` (type: `integer`):

profile mode: max videos per username. search mode: max videos per hashtag/keyword. video mode: max videos from the URL list. trending mode: max videos from the feed. 0 = unlimited (walks until the feed itself ends).

## `oldestPostDate` (type: `string`):

Keep only videos posted on or after this date. Use YYYY-MM-DD, an ISO timestamp, or a relative value such as "30 days". Empty = no limit.

## `newestPostDate` (type: `string`):

Keep only videos posted on or before this date. Use YYYY-MM-DD, an ISO timestamp, or a relative value such as "7 days". Empty = no limit.

## `mostDiggs` (type: `integer`):

Keep only videos with at most this many likes. Empty = no limit.

## `leastDiggs` (type: `integer`):

Keep only videos with at least this many likes. Empty = no limit.

## `proxyTier` (type: `string`):

Residential is the default. In our checks both tiers loaded video pages, and profile pages loaded on the datacenter tier while some residential exits were answered with a short "Please wait" challenge page instead of data (treated as a failed load, never as an empty result). Datacenter is cheaper; results vary by exit and page type. When a connection is refused the run moves on to the next one automatically and fails with a clear message if none works.

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

Leave apifyProxyGroups unset (the default) so the 'Proxy tier' field above decides Residential vs Datacenter. Set apifyProxyGroups here only to pin some other, non-standard group. Leave the country empty for best results; the actor rotates across exits automatically.

## `resumeFromRunId` (type: `string`):

Paste the ID of an earlier run (or its dataset) to skip records it already collected. Leave empty for a fresh run.

## `incrementalMode` (type: `boolean`):

Remember what previous runs of the same targets returned and tag each row with changeType (NEW, UPDATED, UNCHANGED, REAPPEARED), changedFields, firstSeenAt and lastSeenAt. Unchanged rows are not pushed (and not charged) unless 'Emit unchanged' is on. Off = output is identical to a normal run. Removed or deleted items are not reported, because TikTok feeds have no end-of-feed marker. Link fields TikTok re-signs on every fetch (coverUrl, dynamicCoverUrl, originCoverUrl, authorAvatarUrl, avatarUrl, musicUrl, musicCoverMediumUrl, subtitleLinks, slideshowImageLinks) and scrapedAt are ignored when deciding whether a row changed.

## `stateKey` (type: `string`):

Optional name for the remembered baseline. Leave empty to key it automatically on mode, targets and filters (not on Max items or proxy settings).

## `emitUnchanged` (type: `boolean`):

Also push rows whose content did not change, tagged UNCHANGED. Off by default.

## `ignoreFieldsForChanges` (type: `array`):

Field names to leave out when deciding whether a row changed, e.g. playCount, diggCount, followerCount for counters that move constantly. Applies to the previous baseline too.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify → Settings → API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "profile",
  "startUrls": [
    "https://www.tiktok.com/@tiktok",
    "https://www.tiktok.com/@tiktok/video/7673909736131038495"
  ],
  "usernames": [
    "tiktok"
  ],
  "queries": [
    "#fyp"
  ],
  "videoUrls": [
    "https://www.tiktok.com/@tiktok/video/7673909736131038495"
  ],
  "maxItems": 3,
  "proxyTier": "residential",
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `profiles` (type: `string`):

No description

## `videos` (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 = {
    "mode": "profile",
    "startUrls": [
        "https://www.tiktok.com/@tiktok",
        "https://www.tiktok.com/@tiktok/video/7673909736131038495"
    ],
    "usernames": [
        "tiktok"
    ],
    "queries": [
        "#fyp"
    ],
    "videoUrls": [
        "https://www.tiktok.com/@tiktok/video/7673909736131038495"
    ],
    "maxItems": 3,
    "proxy": {
        "useApifyProxy": true
    },
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/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 = {
    "mode": "profile",
    "startUrls": [
        "https://www.tiktok.com/@tiktok",
        "https://www.tiktok.com/@tiktok/video/7673909736131038495",
    ],
    "usernames": ["tiktok"],
    "queries": ["#fyp"],
    "videoUrls": ["https://www.tiktok.com/@tiktok/video/7673909736131038495"],
    "maxItems": 3,
    "proxy": { "useApifyProxy": True },
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/tiktok-scraper").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 '{
  "mode": "profile",
  "startUrls": [
    "https://www.tiktok.com/@tiktok",
    "https://www.tiktok.com/@tiktok/video/7673909736131038495"
  ],
  "usernames": [
    "tiktok"
  ],
  "queries": [
    "#fyp"
  ],
  "videoUrls": [
    "https://www.tiktok.com/@tiktok/video/7673909736131038495"
  ],
  "maxItems": 3,
  "proxy": {
    "useApifyProxy": true
  },
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/tiktok-scraper --silent --output-dataset

```

## MCP server setup

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

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/uVUt4J0Dc5jIxLokz/builds/oGRIvzoW4Swuvpvmd/openapi.json
