# TikTok Photo Scraper – Images, Captions & Stats (`scrapingmonkey/tiktok-photo-details-scraper`) Actor

Extract public TikTok photo posts from URLs. Get ordered image links and dimensions, captions, engagement, creator details, music, hashtags, and mentions in one row per post.

- **URL**: https://apify.com/scrapingmonkey/tiktok-photo-details-scraper.md
- **Developed by:** [ScrapingMonkey](https://apify.com/scrapingmonkey) (community)
- **Categories:**
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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?

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

Collect the images and context behind a public TikTok photo post. **TikTok Photo Scraper** returns the ordered photo URLs, dimensions, caption, music, creator details, and available engagement figures from a full /photo/ link.

A photo post stays in one dataset row, with image lists kept in their original order. No TikTok login or cookies are required, and each result includes its source input and status.

| At a glance | Details |
| --- | --- |
| 📥 Input | Full TikTok /@username/photo/ID URLs |
| 📤 Output | Ordered image URLs, dimensions, caption, engagement, creator, music, and tags |
| 🔐 Login required | No |
| ⚡ Processing | Up to 5 photo posts concurrently with automatic retries |
| 💾 Delivery | One dataset row per unique valid photo-post ID |

### What the TikTok photo post scraper extracts 🔎

The Actor retrieves the photo collection exposed for each requested post and keeps its images together. It also captures the text, sound, and creator context needed to understand the collection.

- Post ID, canonical URL, caption, publication time, and reported creation region
- Ordered image URLs, available alternative URLs, width, and height
- The first photo as the image cover
- Views, likes, comments, shares, and saves when exposed
- Creator profile details and available audience counts
- Music metadata, hashtags, mentions, and source-provided content or interaction flags

The result contains links to images. It does not download image files or convert a photo post into a video.

### How to scrape TikTok photo post details 🚀

1. Copy the full URL of a public TikTok photo post.
2. Paste it into `inputList`; add other photo links as separate items.
3. Start the Actor and check the dataset's `status` column.
4. Export the photo URLs and the accompanying caption or metrics.

```json
{
  "inputList": [
    "https://www.tiktok.com/@qq.mm.pp/photo/7479037796326362385"
  ]
}
```

The address must contain /photo/. Short share links, /video/ links, profile URLs, and bare numeric IDs are not supported.

### TikTok photo post data fields and output 📦

| Field group | Included data |
| --- | --- |
| Post | ID, canonical URL, caption, type, creation date, and region |
| Photo collection | Ordered main and alternative image URLs with dimensions |
| Cover | The first photo's URL, alternatives, width, and height |
| Engagement and creator | Available post metrics, creator identity, bio, account flags, and counts |
| Music and text tags | Sound details, hashtags, mentions, labels, and interaction flags |

Complete illustrative success item; these fictional values show the output structure and are not a live result:

```json
{
  "input": "https://www.tiktok.com/@trailjournal/photo/7523456789012345678",
  "status": "success",
  "image_cover.url": "https://example.com/trail-view.jpg",
  "url": "https://www.tiktok.com/@trailjournal/photo/7523456789012345678",
  "id": "7523456789012345678",
  "type": "photo",
  "description": "Morning trail with @trailguide #hiking",
  "created_at": "2023-11-14T22:13:20Z",
  "create_time": 1700000000,
  "location_created": "US",
  "statistics.views": 42700,
  "statistics.likes": 2860,
  "statistics.comments": 64,
  "statistics.shares": 118,
  "statistics.saves": 390,
  "author.avatar_large": "https://example.com/trail-journal-large.jpg",
  "author.avatar_medium": "https://example.com/trail-journal-medium.jpg",
  "author.avatar_thumb": "https://example.com/trail-journal-thumb.jpg",
  "author.username": "trailjournal",
  "author.name": "Trail Journal",
  "author.profile_url": "https://www.tiktok.com/@trailjournal",
  "author.id": "7123456789012345678",
  "author.sec_uid": "",
  "author.bio": "Weekend walks and practical trail notes.",
  "author.region": "US",
  "author.is_verified": false,
  "author.is_private": false,
  "author.followers": 18400,
  "author.following": 126,
  "author.likes": 286000,
  "author.videos_count": 94,
  "author.friends_count": 28,
  "music.id": "7423456789012345678",
  "music.title": "Morning Walk",
  "music.author_name": "Trail Journal",
  "music.author_username": "trailjournal",
  "music.album": "",
  "music.duration": 24,
  "music.is_original": true,
  "music.is_commerce": false,
  "music.is_author_artist": false,
  "music.cover_large": "https://example.com/morning-walk-large.jpg",
  "music.cover_medium": "https://example.com/morning-walk-medium.jpg",
  "music.cover_thumb": "https://example.com/morning-walk-thumb.jpg",
  "music.play_url": "https://example.com/morning-walk.mp3",
  "images.url": [
    "https://example.com/trail-view.jpg",
    "https://example.com/trail-map.jpg"
  ],
  "images.urls": [
    [
      "https://example.com/trail-view.jpg",
      "https://example.com/trail-view-alt.jpg"
    ],
    [
      "https://example.com/trail-map.jpg"
    ]
  ],
  "images.width": [
    1200,
    1200
  ],
  "images.height": [
    800,
    800
  ],
  "image_cover.urls": [
    "https://example.com/trail-view.jpg",
    "https://example.com/trail-view-alt.jpg"
  ],
  "image_cover.width": 1200,
  "image_cover.height": 800,
  "video.id": "",
  "video.cover": "",
  "video.origin_cover": "",
  "video.dynamic_cover": "",
  "video.play_url": "",
  "video.download_url": "",
  "video.width": 0,
  "video.height": 0,
  "video.duration": 0,
  "video.ratio": "",
  "video.format": "",
  "video.bitrate": 0,
  "video.encoded_type": "",
  "video.video_quality": "",
  "video.volume_info.loudness": 0,
  "video.volume_info.peak": 0,
  "video.volume_info.loudness_range": 0,
  "video.bitrates": [],
  "subtitles": [],
  "hashtags.id": [
    "54321"
  ],
  "hashtags.name": [
    "hiking"
  ],
  "hashtags.description": [
    ""
  ],
  "hashtags.profile_url": [
    "https://www.tiktok.com/tag/hiking"
  ],
  "hashtags.cover_large": [
    ""
  ],
  "hashtags.cover_medium": [
    ""
  ],
  "hashtags.cover_thumb": [
    ""
  ],
  "mentions.user_id": [
    "7223456789012345678"
  ],
  "mentions.sec_uid": [
    ""
  ],
  "mentions.username": [
    "trailguide"
  ],
  "mentions.profile_url": [
    "https://www.tiktok.com/@trailguide"
  ],
  "mentions.start": [
    19
  ],
  "mentions.end": [
    30
  ],
  "is_photo": true,
  "is_ad": false,
  "is_aigc": false,
  "aigc_label_type": 0,
  "is_original_item": true,
  "is_official_item": false,
  "is_secret": false,
  "for_friends": false,
  "duet_enabled": false,
  "stitch_enabled": false,
  "share_enabled": true,
  "comments_enabled": true,
  "comment_status": 0,
  "collected": false,
  "diversification_labels": [
    "Travel",
    "Outdoors"
  ],
  "suggested_words": [
    "Hiking"
  ]
}
```

The table and JSON share the same flat keys. `images.url`, `images.width`, and `images.height` are aligned lists: their first values describe the first image, and so on. `images.urls` keeps an inner list of alternative URLs for each image. Hashtag and mention lists follow the same positional rule within their own group.

Success rows use empty strings, `0`, `false`, or empty lists for unavailable values. A missing metric can therefore appear as zero. Video fields may be empty for photo posts; `video.download_url` is empty, and `subtitles` and `video.bitrates` remain empty lists in the current output. A failed result keeps `input` and `status` and sets the photo-post data fields to `null`.

### Input requirements and coverage ⚙️

| Parameter | Type | Required | Rules |
|---|---|---|---|
| `inputList` | array of strings | Yes | At least one full TikTok /@username/photo/numeric-ID URL; no fixed batch-size maximum |

HTTP and HTTPS links on tiktok.com or its subdomains are accepted. The username segment allows 1–24 letters, digits, periods, or underscores. Trailing slashes and query parameters are allowed; output uses a canonical URL.

The returned content must contain photos. A video behind a photo URL is treated as a failed result. Valid requests are deduplicated by post ID, while invalid input entries are saved individually. There are no image-count or pagination settings.

### TikTok photo post use cases 🎯

#### Carousel creative research

Keep each post's image sequence and caption together when comparing storytelling, layout, and calls to action.

#### Campaign reporting

Attach engagement figures and creator details to submitted photo-post links without manually copying each metric.

#### Visual content catalogues

Index image URLs and dimensions alongside the post ID so a review system can retrieve the correct image in sequence.

#### Caption and sound analysis

Compare the descriptions, hashtags, mentions, and music used across a selected set of photo posts.

### Pricing and billable photo post rows 💰

Check the Actor's **Pricing** tab for the current pricing model and rate.

Under dataset-item pricing, each saved success or failed row can be billable. Valid duplicate requests are processed once; invalid entries can each save a failed row. Automatic retries do not independently create extra dataset rows.

A collection with several images remains one result row. Image alternatives, tags, and mentions do not add separate rows.

### TikTok Photo Details Scraper API and integrations 🔌

Use the Actor ID from the **API** tab as `$ACTOR_ID` and your Apify token as `$APIFY_TOKEN`.

```bash
curl -X POST "https://api.apify.com/v2/acts/$ACTOR_ID/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"inputList":["https://www.tiktok.com/@qq.mm.pp/photo/7479037796326362385"]}'
```

The request starts a run. Retrieve its dataset after completion, or connect an Apify schedule and webhook to repeat collection and trigger downstream work. Export to JSON, CSV, or Excel, or send selected fields to Google Sheets, Make, Zapier, or your own application.

### Reliability and limits ⚠️

Up to five photo posts are processed concurrently. Temporary request failures are retried; unavailable posts and unresolved requests become failed rows so other valid inputs can continue.

The image list reflects what TikTok exposes to the Actor. Removed, private, region-restricted, or embed-disabled posts may be inaccessible. Image and music URLs can expire. Missing metrics or settings use defaults, so interpret them alongside the post's availability rather than treating every zero as a measured count.

### Frequently asked questions ❓

#### Will each photo become a separate row?

No. One photo post produces one row, with ordered lists for its images and dimensions.

#### Can I submit a video link?

No. This Actor requires a full /photo/ URL and verifies that the returned post contains photos.

#### Are alternative image URLs preserved?

Yes. `images.urls` keeps the available alternatives grouped by image; `images.url` contains the first available URL for each image.

#### Does it download the images?

No. It returns image references and dimensions. Download or archive permitted assets separately if your workflow needs files.

#### Is the soundtrack included?

Music metadata and a playback URL are returned when exposed. The Actor does not create a slideshow video or generate a transcript.

### Support, responsible use, and related actors 🛟

For help with a result, share the run ID and a safe public example input through the Actor's support channel. Keep tokens and other credentials out of support messages.

Use public data and media within the permissions and obligations that apply to your project, including privacy and copyright requirements.

# Actor input Schema

## `inputList` (type: `array`):

Add one full TikTok /photo/ link per item. Numeric post IDs, short links and video links are not accepted.

## Actor input object example

```json
{
  "inputList": [
    "https://www.tiktok.com/@qq.mm.pp/photo/7479037796326362385"
  ]
}
```

# Actor output Schema

## `posts` (type: `string`):

One dataset row for every submitted TikTok photo-post link.

# 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 = {
    "inputList": [
        "https://www.tiktok.com/@qq.mm.pp/photo/7479037796326362385"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapingmonkey/tiktok-photo-details-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 = { "inputList": ["https://www.tiktok.com/@qq.mm.pp/photo/7479037796326362385"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapingmonkey/tiktok-photo-details-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 '{
  "inputList": [
    "https://www.tiktok.com/@qq.mm.pp/photo/7479037796326362385"
  ]
}' |
apify call scrapingmonkey/tiktok-photo-details-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapingmonkey/tiktok-photo-details-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/XVz70qfEneeXL2Ucu/builds/etJxIUD5argfU4vAz/openapi.json
