# Truth Social Scraper: Posts & Profiles (`sz022/truth-social-posts-scraper`) Actor

Scrape Truth Social posts and profiles without a login. Paste usernames or post URLs and get every post with likes, reblogs, replies, upvotes and downvotes, edits, media, quotes and link cards, plus full profile details. Filter by date, skip replies or reblogs. Export to CSV, Excel or JSON.

- **URL**: https://apify.com/sz022/truth-social-posts-scraper.md
- **Developed by:** [CHEN-SHU-CHEN](https://apify.com/sz022) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 post scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Truth Social Scraper?

You want to know what an account posted on Truth Social, when, and how people reacted: for a news story, a research dataset, a monitoring sheet. The site has no export, and copying posts by hand loses the numbers and the edits.

Paste **Truth Social usernames or profile URLs** and get every post as a row: the text, the exact **replies, reblogs (ReTruths), likes, upvotes and downvotes**, when it was posted and **when it was last edited**, its images and videos, the post it quotes or reshares, link previews, mentions and hashtags. Each profile also comes back as a row of its own: followers, following, post count, bio, website, join date, and whether the account is **verified** or **premium**. You can also paste single post URLs.

No Truth Social account or login is needed.

Because it runs on Apify, you can **schedule** a daily pull of an account's new posts, call it through the **API**, connect it to Make, Zapier or Google Sheets, and export the table as **CSV, Excel or JSON**.

### How to scrape Truth Social posts

1. Open the **Input** tab and paste usernames or profile URLs into **Profiles**, one per line. `realDonaldTrump`, `@realDonaldTrump` and `https://truthsocial.com/@realDonaldTrump` all work.
2. Set **Maximum posts per profile**. Posts come newest first. Set it to 0 to get only the profile rows.
3. Optional: limit the posts to a date window with **Posts from** and **Posts until**, and skip replies, reblogs or posts without media.
4. Optional: paste single posts into **Post URLs**.
5. Click **Start**. A profile with 100 posts takes about five seconds.
6. Open the **Output** tab. The dataset has two tables, **Posts** and **Profiles**; export either one, or everything.

### How much does it cost to scrape Truth Social?

You pay per row delivered, and nothing for rows that failed.

| Event | Price | Per 1,000 |
|---|---|---|
| Post scraped (`post-scraped`) | $0.004 | $4 |
| Profile scraped (`profile-scraped`) | $0.004 | $4 |
| Run start (`apify-actor-start`, per GB of memory) | set by Apify | — |

So one profile with its 100 newest posts costs about $0.40. Usernames that do not exist, invalid input, posts that were deleted, and anything the run did not get to are free. Apify's free plan ($5 a month) covers about 1,250 rows.

A post is charged once per run. If a post URL you pasted was already returned from a profile's feed in the same run, it is not repeated and not charged again.

#### How do I cap what a run can spend?

Set a maximum cost per run in the run options. Each row is written and charged in one step, and the run stops on the first row the limit cannot pay for, so you never pay more than the limit and never pay for a row you did not get. A profile's own row is kept room for while its posts are read, so the posts never use up the money its profile row needs. The profiles and posts it did not reach still appear in the output with the status `not_processed`, free. The profile it stopped on says `charge_limit` in `postsStopReason`.

### Input

| Field | Description |
|---|---|
| `profiles` | Usernames or profile URLs. Each gives one profile row and up to `maxPostsPerProfile` post rows |
| `maxPostsPerProfile` | Newest first; 0 for profile rows only. Default 20 |
| `postsSince`, `postsUntil` | Optional date window, in UTC. `2026-10-01`, an ISO date-time, or a relative date such as `7 days` (meaning 7 days ago). A bare date in `postsUntil` includes that whole day |
| `excludeReplies` | Leave out replies, the account's own threads included |
| `excludeReblogs` | Leave out reblogs (ReTruths). Quote posts are kept |
| `onlyPostsWithMedia` | Only posts with at least one image or video |
| `postUrls` | Single posts: `https://truthsocial.com/@user/117461843705117717`, the `/posts/` form, or the bare numeric ID |
| `proxyConfiguration` | Apify datacenter proxy by default |
| `residentialFallback` | If Cloudflare keeps blocking the datacenter proxy, finish the run on Apify's US residential proxy. On by default; included in the price |

A post URL pasted into **Profiles** is read as a post, and a profile URL pasted into **Post URLs** as a profile.

Example input:

```json
{
  "profiles": ["realDonaldTrump", "https://truthsocial.com/@DonaldJTrumpJr"],
  "maxPostsPerProfile": 100,
  "postsSince": "2026-10-01",
  "excludeReblogs": true,
  "postUrls": ["https://truthsocial.com/@realDonaldTrump/117461843705117717"]
}
```

### Output

Every row has a `type` (`post` or `profile`), a `status`, an `error` message when something went wrong, and the `input` line it came from. Statuses: `ok`, `not_found`, `login_required`, `blocked`, `rate_limited`, `error`, `parse_error`, `invalid_input`, `not_processed`. One row per input at least, so 50 profiles in means 50 profile rows out, whatever happened to them.

A profile's posts are written before its profile row, so the profile row can say how many posts came with it.

#### Post rows

| Field | Notes |
|---|---|
| `id`, `url`, `uri` | |
| `createdAt`, `editedAt`, `version` | ISO 8601, UTC. `editedAt` is `null` for a post never edited; `version` counts the edits |
| `title` | Set on some posts (Truth Social's own field); usually `null` |
| `content`, `text` | The post as HTML, and as plain text with line breaks kept |
| `language`, `visibility`, `sensitive`, `spoilerText` | |
| `repliesCount`, `reblogsCount`, `favouritesCount` | Exact counts. `favouritesCount` is the like count |
| `upvotesCount`, `downvotesCount` | Truth Social's vote counts |
| `isReply`, `isReblog`, `isQuote` | |
| `inReplyToId`, `inReplyToAccountId`, `inReplyTo` | `inReplyTo` is the parent post, when the API includes it |
| `quoteId`, `quote` | The quoted post, with its own text, counts, media and author |
| `reblog` | For a reblog, the original post, with its own counts and author |
| `mediaCount`, `mediaAttachments` | Each with `type` (image, video, gifv, audio), `url`, `previewUrl`, `width`, `height`, `aspectRatio`, `durationSeconds`, `bitrate`, `frameRate`, `description` (alt text), `blurhash` |
| `card` | The link preview: `url`, `title`, `description`, `imageUrl`, `providerName`, `authorName`, embed `html` |
| `poll` | Options with vote counts, `expiresAt`, `votersCount` |
| `mentions`, `tags`, `hashtags`, `emojis` | |
| `group` | For posts made in a Truth Social group |
| `sponsored` | `true` for a promoted post |
| `authorUsername`, `authorDisplayName`, `authorVerified`, `authorPremium`, `authorFollowersCount` | Shortcuts for the table view |
| `account` | The author's full profile, the same fields as a profile row |
| `scrapedAt` | When the row was read |

Example post row (shortened):

```json
{
  "type": "post",
  "status": "ok",
  "input": "realDonaldTrump",
  "id": "117461843705117717",
  "url": "https://truthsocial.com/@realDonaldTrump/117461843705117717",
  "createdAt": "2026-10-18T12:11:35.989Z",
  "editedAt": null,
  "text": "France is burning! The immigration of people from an entirely different world…",
  "repliesCount": 1123,
  "reblogsCount": 2734,
  "favouritesCount": 11720,
  "upvotesCount": 11720,
  "downvotesCount": 0,
  "isReply": false,
  "isReblog": false,
  "isQuote": false,
  "mediaAttachments": [],
  "authorUsername": "realDonaldTrump",
  "authorVerified": true,
  "authorPremium": true,
  "authorFollowersCount": 13121258
}
```

#### Profile rows

| Field | Notes |
|---|---|
| `id`, `username`, `acct`, `displayName`, `url` | |
| `bio`, `bioText` | HTML and plain text |
| `website`, `location`, `fields` | `fields` are the extra name/value lines some profiles show |
| `avatarUrl`, `avatarStaticUrl`, `headerUrl`, `headerStaticUrl` | |
| `followersCount`, `followingCount`, `statusesCount` | Exact. `statusesCount` is the account's total posts |
| `createdAt`, `lastStatusAt` | Join date-time, and the date of the latest post |
| `verified`, `premium` | Blue check, and Truth+ |
| `locked`, `bot`, `isGroup`, `discoverable`, `acceptingMessages`, `acceptsChatMessages`, `tvAccount` | |
| `postsScraped` | How many post rows this run returned for the profile |
| `postsStopReason` | Why it stopped: `max_posts`, `date_range`, `end_of_feed`, `charge_limit`, `charge_failed`, `time_budget`, `spend_guard` (the run was costing more than it earned), or the status of a feed page that failed |
| `postsError` | The error, when a feed page failed |

The key-value store also holds `RUN_SUMMARY`: rows delivered, failed, skipped as duplicates and not processed, the number of requests, and why the run stopped if it stopped early.

### FAQ

#### Do I need a Truth Social account?

No. Everything comes from what Truth Social shows any logged-out visitor, through the same public API its website uses.

#### What can it not get?

These need a logged-in account on Truth Social, so they are not offered:

- **Search** for posts or accounts by keyword or hashtag, and trending posts
- **Followers and following lists**
- **Who liked or reblogged** a post
- **Reply threads** under a post. Each post's reply count is included, and a reply you scrape carries the post it answers
- **Groups' own feeds**. Group posts that appear on a profile are included, with their `group`

Private (locked) accounts show their profile but not their posts. Their profile row is returned with `postsScraped: 0`, and `postsStopReason` says what the feed answered. Deleted and suspended posts or accounts come back `not_found`.

#### Is it legal to scrape Truth Social?

It reads only public posts and profiles, without logging in. Posts are personal data under laws such as the GDPR when they identify a person, so check what applies to your use. This is not legal advice.

#### Why is a username not found?

Check the spelling; case does not matter. An account that was deleted or suspended also comes back `not_found`. These rows are free.

#### How fast is it, and are there limits?

A feed page holds 20 posts and takes well under a second, so 1,000 posts take about a minute. Truth Social allows 300 profile lookups per five minutes; the run waits for the reset rather than hitting the limit, so very long lists of profiles slow down after the first 300.

#### What happens when Cloudflare blocks the run?

Truth Social is behind Cloudflare. The run reads the API with a browser's network fingerprint, so it is normally let through. If a request is blocked anyway, it is retried on a fresh IP; after repeated blocks the run moves to Apify's US residential proxy for the rest of the run, at no extra charge to you. If it is still blocked, the affected rows come back `blocked`, and after four inputs in a row fail this way the run stops and marks the rest `not_processed`, free.

#### Why are the rows for one profile split into two tables?

Apify shows a dataset through views. **Posts** and **Profiles** are two views of the same dataset; each shows its own columns, and rows of the other type appear in it with those columns empty. Filter on `type` in your spreadsheet, or export a view as CSV.

#### Something broke or a number looks wrong

Open an issue on the **Issues** tab with the input and the run link. Reports are answered there.

# Actor input Schema

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

Truth Social usernames or profile URLs, one per line: realDonaldTrump, @realDonaldTrump or https://truthsocial.com/@realDonaldTrump. Each gives one profile row and up to the maximum number of posts below. A post URL pasted here is read as a post.

## `maxPostsPerProfile` (type: `integer`):

Newest first. Set to 0 to get only the profile rows. Each post row is charged, so this also caps what a profile can cost.

## `postsSince` (type: `string`):

Optional. Only posts created on or after this date (UTC). A date such as 2026-10-01, or a relative one such as 7 days. Reading stops at the first older post, so an early date never costs more requests than it needs.

## `postsUntil` (type: `string`):

Optional. Only posts created on or before this date (UTC; a bare date includes that whole day). The feed is opened at this date, so older periods are reached without paging through newer posts.

## `excludeReplies` (type: `boolean`):

Leave out posts that reply to another post, the account's own threads included.

## `excludeReblogs` (type: `boolean`):

Leave out posts the account only reshared. Quote posts, which add the account's own text, are kept.

## `onlyPostsWithMedia` (type: `boolean`):

Keep only posts that carry at least one image, video or other attachment.

## `postUrls` (type: `array`):

Single posts, one per line: https://truthsocial.com/@realDonaldTrump/117461843705117717, the /posts/ form, or just the numeric post ID. Each gives one post row. A post already returned from a profile's feed in the same run is not repeated or charged twice.

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

Apify's datacenter proxy by default. If Cloudflare blocks it, the run moves to Apify's US residential proxy on its own (see the option below). Proxy URLs of your own are used as given.

## `residentialFallback` (type: `boolean`):

When Cloudflare keeps blocking the datacenter proxy, read the rest of the run through Apify's US residential proxy. The residential traffic is included in the per-row price. Turn off to fail fast instead.

## Actor input object example

```json
{
  "profiles": [
    "realDonaldTrump"
  ],
  "maxPostsPerProfile": 10,
  "excludeReplies": false,
  "excludeReblogs": false,
  "onlyPostsWithMedia": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "residentialFallback": true
}
```

# Actor output Schema

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

Every row: posts with counts, media, quotes and cards; profiles with followers, bio and flags.

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

The dataset in the posts view.

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

The dataset in the profiles view.

## `summary` (type: `string`):

Counts of rows delivered, failed, skipped as duplicates and not processed, and why the run stopped if it stopped early.

# 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 = {
    "profiles": [
        "realDonaldTrump"
    ],
    "maxPostsPerProfile": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("sz022/truth-social-posts-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 = {
    "profiles": ["realDonaldTrump"],
    "maxPostsPerProfile": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("sz022/truth-social-posts-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 '{
  "profiles": [
    "realDonaldTrump"
  ],
  "maxPostsPerProfile": 10
}' |
apify call sz022/truth-social-posts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sz022/truth-social-posts-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/HhVMIVzkfFUf92wco/builds/jpWuNTBdAkb93dqMo/openapi.json
