# TikTok Slideshow Downloader (`khadinakbar/tiktok-slideshow-downloader`) Actor

Save public TikTok slideshow photos with custom filenames, labels, SHA-256 checksums and an archive manifest. This independent Actor is not affiliated with, associated with, or endorsed by TikTok.

- **URL**: https://apify.com/khadinakbar/tiktok-slideshow-downloader.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Social media, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / 1,000 photo downloadeds

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

### What is TikTok Slideshow Downloader?

Save public TikTok slideshow photos as ordered image files with custom filenames, SHA-256 checksums, an archive manifest and your own per-post labels. Paste carousel links or numeric post IDs, choose a total photo limit, and get one Dataset row per delivered slide with image URL, caption, creator, available engagement and sound metadata, and collection time.

Save public posts you are authorized to collect. Run costs include the photo and startup events plus platform usage. Blocked pages, larger files and retention can raise total costs; inspect the recovery budget and run diagnostics before expanding a batch.

### Best fit and workflow

Use it when you have known public photo posts and need an archive another process can verify. For example, a creative researcher can attach a campaign label to each post, save ten slides across the batch, export the photo rows, and verify downloaded bytes against each row's checksum.

For MP4 downloads, continue with [TikTok No Watermark Downloader](https://apify.com/khadinakbar/tiktok-no-watermark-downloader). This job works from supplied references rather than discovering profiles or hashtags. It saves available image bytes without editing or watermark removal.

### How it works

1. Normalize links and IDs; deduplicate by post ID while keeping the first labels.
2. Read the public photo list through the run’s metered Apify proxy, with direct access first, up to six recovery connections per post and bounded redirects.
3. Check the total photo selection and event allowance before further work.
4. Copy recognized image files, calculate SHA-256, and write each photo with its matching event.
5. Save run-local progress and write OUTPUT, RUN_SUMMARY and MANIFEST.

### Input example

```json
{
  "postRequests": [
    {
      "url": "https://www.tiktok.com/@anamiriamyepez/photo/7498924643227536645",
      "userData": { "campaign": "creative-archive" }
    }
  ],
  "maxSlideshows": 1,
  "maxPhotos": 3,
  "maxItems": 3,
  "fileNamePattern": "campaign-{postId}-{photoIndex}.{extension}",
  "saveFiles": true
}
```

| Input | What it does |
|---|---|
| slideshowUrls | Plain list of public links or numeric post IDs |
| postRequests | Optional labeled posts with url and a userData object |
| maxSlideshows | Select up to 500 distinct posts; default 10 |
| maxPhotos | Select up to 35 slides per post; default 35 |
| maxItems | Total photo selection across the batch, up to 17,500 |
| fileNamePattern | Name files with postId, photoIndex and extension placeholders |
| saveFiles | Copy files by default; false returns expiring source links only |
| connectionMode | Automatic uses direct pages first; extra-recovery starts with metered connections |
| maxLookupCostUsd | Limit the reserved recovery-connection cost, default $0.12; other usage is additional |

Use either post list or both. Plain URLs come first, followed by labeled posts. inputIndex is the zero-based position in that combined order. Duplicate IDs keep the first labels. Labels are limited to 4,096 JSON bytes per post. Accepted references include full photo links, vm.tiktok.com, vt.tiktok.com, /t/ share links, legacy m.tiktok.com/v/ID.html links and numeric IDs.

The 500-post input bound is separate from a completed-volume claim. The bounded lookup budget, event allowance and run deadline may stop a larger job early; inspect delivered counts. Image bodies are capped at 15 MiB and lookup responses at 8 MiB. Reaching a configured maxItems selection completes that selection without looking up later posts. Unexpected stops and unavailable selected work are reported separately.

Filename patterns cannot contain paths or control characters. Characters outside letters, numbers, dot, underscore and hyphen become underscores. A stable post/slide suffix prevents collisions when placeholders are omitted or the pattern expands too far.

### Output data

Each row represents one delivered photo. These values illustrate the shape; real URLs and checksums come from your run.

```json
{
  "postId": "1234567890123456789",
  "photoIndex": 1,
  "photoCount": 3,
  "totalPhotoCount": 3,
  "fileName": "campaign-1234567890123456789-1.jpg",
  "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "bytes": 98253,
  "contentType": "image/jpeg",
  "downloadUrl": "https://api.apify.com/v2/key-value-stores/STORE/records/PHOTO_KEY",
  "sourceImageUrl": "https://p16.tiktokcdn.com/illustrative-photo.jpeg",
  "authorUsername": "creator",
  "caption": "Example photo caption",
  "inputIndex": 0,
  "requestedUrl": "1234567890123456789",
  "userData": { "campaign": "creative-archive" },
  "scrapedAt": "2026-10-15T00:00:00.000Z"
}
```

sourceImageUrl expires according to the source CDN. downloadUrl follows the run storage's access and retention settings. Download files before retention expires. sha256 describes copied bytes and is absent in source-link-only mode. Optional metadata is included when available; an empty caption is preserved. Export Dataset rows as JSON, CSV, Excel or HTML.

MANIFEST contains file count, total bytes and a SHA-256 digest of the ordered file identity/checksum index. It includes up to 1,000 file entries and links to the complete Dataset. Above that, fileIndexTruncated is true and nextFileIndexOffset identifies the next Dataset offset. A checksum does not establish copyright ownership or source authenticity.

### Pricing and spending control

This Actor uses **Pay per event plus Apify platform usage**. Check the [Pricing tab](https://apify.com/khadinakbar/tiktok-slideshow-downloader/pricing) for the effective prices before running it. This version charges $0.0001 per photo ($0.10 per 1,000) plus $0.00005 per startup at its default memory. Metered proxy, compute, transfer and storage usage are additional. They vary with the job and retries, so the event price is not a total-price quote.

One photo-downloaded event corresponds to one delivered photo row, including source-link-only rows. Files, filenames, checksums, labels and the manifest have no extra photo event. Startup and platform usage also apply, including to runs without photos. Budget a job as startup + accepted photo events + usage. Set the spending ceiling through Console run options, REST maxTotalChargeUsd, or MCP callOptions; it is not an input field.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~tiktok-slideshow-downloader/runs?maxTotalChargeUsd=0.02" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"slideshowUrls":["7498924643227536645"],"maxItems":3,"saveFiles":true}'
```

Poll the run to a terminal status. Read its Dataset, then OUTPUT, RUN_SUMMARY and MANIFEST from its key-value store. Verify useful rows and the named event counts before continuing an automation.

### Agent prompt and integrations

> Archive the first three images from these public TikTok photo posts. Attach my campaign labels, save images with post/slide filenames, and return each download URL and SHA-256. Read the run summary and archive manifest; report incomplete work and total charges.

In Apify MCP, inspect this Actor's current schema using your authorized account. Use call-actor with the exact Actor identity and validated input, poll with get-actor-run, then use get-dataset-items and the key-value record tool. Put the spending cap in callOptions. Use your Apify account to run the Actor and retrieve its outputs.

Structured rows and labels work with Make, Zapier, API clients and webhooks. Schedules can repeat an archive when configured by the account owner. A downstream workflow can compare checksums across runs; this Actor does not maintain a cross-run customer database.

### Best results and troubleshooting

- Start with canonical links or exact IDs and a small total photo limit.
- COMPLETE means the configured selection completed. PARTIAL preserves useful rows when selected work or the manifest is incomplete.
- VALID_EMPTY reports no available photos, including regular video inputs. A missing-post lookup alone does not prove deletion or privacy.
- INVALID_INPUT describes starting information to correct. UPSTREAM_FAILED and CONFIG_ERROR fail honestly when no useful photos can be delivered.
- itemLimitReached identifies the configured photo selection; limitReached identifies a spending, lookup-budget or execution stop.
- If reconciliationRequired is true, inspect rows and event counts before resuming. Run-local checkpoints do not make storage and charging atomic.

### Builder's note

An archive needs a relationship between a source post, an ordered photo and its saved bytes. The filename is convenient for people; post/slide identity, checksums and structured outcomes help automations check their work. Those checks are separate from lookup availability and pricing validation.

### Authorized public data

Supply posts you are authorized to collect. The Actor reads public photo posts and public metadata without a TikTok login or private feed. Images may contain personal data and copyrighted material. Respect creators' rights, source terms and privacy obligations. Public accessibility is not blanket reuse permission. Files and caller-supplied labels stay in the caller's Apify storage under its access and retention settings.

# Actor input Schema

## `slideshowUrls` (type: `array`):

Paste public TikTok photo-post links, short-share links, legacy mobile links or numeric post IDs. For per-post campaign labels, use Labeled posts. Videos are detected and skipped.

## `maxSlideshows` (type: `integer`):

Process up to this many distinct posts, from 1 to 500; default 10. Duplicate IDs are processed once. Provider budgets, photo limits and the run deadline may stop a larger job early; check the run summary.

## `maxPhotos` (type: `integer`):

Maximum photos returned per post. Defaults to 35; range 1–35. Later slides are omitted. Each delivered row is one photo event; totalPhotoCount records source slides before this cap.

## `saveFiles` (type: `boolean`):

When true, store each slide in the key-value store and return downloadUrl. Defaults to true. When false, return the source CDN URL only. CDN links expire, so leave this on for a durable file.

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

Stop after this many photos across all posts, from 1 to 17500. Omit to use the per-post and post-count limits. Reaching this configured selection limit completes the selected job without looking up later posts.

## `fileNamePattern` (type: `string`):

Name saved images using {postId}, {photoIndex} and {extension}. Paths are not allowed. A unique post and slide suffix is added when needed; characters outside letters, numbers, dot, underscore and hyphen become underscores.

## `postRequests` (type: `array`):

Optionally attach a small campaign or archive label object to each post. Each delivered photo includes that userData and its original input position. Duplicate references keep the first labels. Can be used instead of the plain URL list.

## `connectionMode` (type: `string`):

Use automatic for direct public-page access with connection recovery. Extra recovery starts with metered proxy access and can cost more.

## `maxLookupCostUsd` (type: `number`):

Limit the reserved cost of paid connection recovery. Compute, file transfer and storage are additional. Lower budgets can stop recovery before all posts finish.

## Actor input object example

```json
{
  "slideshowUrls": [
    "https://www.tiktok.com/@anamiriamyepez/photo/7498924643227536645"
  ],
  "maxSlideshows": 1,
  "maxPhotos": 3,
  "saveFiles": true,
  "maxItems": 3,
  "fileNamePattern": "campaign-{postId}-{photoIndex}.{extension}",
  "connectionMode": "automatic",
  "maxLookupCostUsd": 0.12
}
```

# Actor output Schema

## `photos` (type: `string`):

Dataset rows with downloadUrl, sourceImageUrl, caption, author, and sound.

## `runSummary` (type: `string`):

Outcome, itemsPushed, chargedEventCounts, and per-post results.

## `output` (type: `string`):

Read outcome, itemsPushed, actual photo event counts, budget stops and reconciliation diagnostics.

## `manifest` (type: `string`):

Saved-file count, total bytes, SHA-256 index digest and up to 1000 file entries. Larger complete indexes are in the Dataset.

# 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 = {
    "slideshowUrls": [
        "https://www.tiktok.com/@anamiriamyepez/photo/7498924643227536645"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/tiktok-slideshow-downloader").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 = { "slideshowUrls": ["https://www.tiktok.com/@anamiriamyepez/photo/7498924643227536645"] }

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/tiktok-slideshow-downloader").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 '{
  "slideshowUrls": [
    "https://www.tiktok.com/@anamiriamyepez/photo/7498924643227536645"
  ]
}' |
apify call khadinakbar/tiktok-slideshow-downloader --silent --output-dataset

```

## MCP server setup

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

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/KkOCBAueYIiAv5P7S/builds/rSaLycsqac0fGygQk/openapi.json
