Truth Social Scraper With Photos & Videos avatar

Truth Social Scraper With Photos & Videos

Pricing

from $4.99 / 1,000 results

Go to Apify Store
Truth Social Scraper With Photos & Videos

Truth Social Scraper With Photos & Videos

πŸ“° Truth Social Scraper extracts posts, profiles & engagement from Truth Social β€” including text, timestamps, hashtags, mentions, likes & reposts. πŸ”Ž Perfect for social listening, competitor tracking, sentiment & trend analysis. πŸš€ Fast, reliable data for research & analytics.

Pricing

from $4.99 / 1,000 results

Rating

0.0

(0)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

1

Monthly active users

9 days ago

Last modified

Share

Truth Social Scraper β€” Photo, Video and GIF URLs per Post

Truth Social Scraper With Photos & Videos collects public posts from any Truth Social handle and turns their attachments into a normalized media catalog: direct file URL, preview URL, media type, alt-text description, pixel dimensions and a suggested download filename, alongside the post text, permalink, timestamp and author block. Rows stream into the dataset as each post is fetched. Add a handle, pick a media type, press Start.

⚠️ Two things to know before your first run. This Actor has no required input β€” start it with an empty input and it silently falls back to the handle realDonaldTrump, so always set Who should we scrape? yourself. And flattenMedia does not just reshape the output: it changes the row from a post to a single attachment, which changes both the fields you get and the number of rows a run produces.

What is Truth Social Scraper With Photos & Videos?

Truth Social Scraper With Photos & Videos is an Apify Actor that reads Truth Social's public, Mastodon-compatible HTTP API β€” account lookup, paginated profile timelines and single statuses β€” and writes one typed JSON row per post, or per media attachment when you flatten. Every media value it reports is computed from the post's real media_attachments array; nothing is inferred or filled in.

No Truth Social account, login, cookie, API token or app registration is used anywhere in the run. The only credential involved is your Apify token.

It is built for OSINT and media researchers building attachment archives, journalists and analysts monitoring public accounts, and developers and AI engineers piping post text and media metadata into indexes, dashboards and agent tools.

What Truth Social post and media data is publicly available to scrape?

Truth Social serves public posts and their attachments to unauthenticated clients through its Mastodon-compatible API β€” post text, timestamps, permalinks, engagement counters, the author block and the full attachment array are all readable without signing in. What is not readable that way is anything tied to an account relationship.

Data CategoryPublic without a loginBehind a Truth Social login
Post text, post ID, permalink and creation timeβœ… Publicβ€”
Media attachments β€” file URL, preview URL, type, alt description, dimensionsβœ… Publicβ€”
Author handle, display name, avatar, header image and profile URLβœ… Publicβ€”
Reply, repost and favourite counters on a postβœ… Publicβ€”
A reposted or quoted post nested inside a postβœ… Publicβ€”
A handle's own replies to other accountsβœ… Publicβ€”
Identities of the accounts that liked or reposted a post❌Logged-in account β€” and not requested by this Actor
Posts from a locked account❌Logged-in account the owner has approved
Direct messages, bookmarks and notifications❌Logged-in account

Truth Social Scraper With Photos & Videos only returns publicly visible data β€” what any logged-out visitor sees. Nothing behind a login wall.

What data can I extract with Truth Social Scraper With Photos & Videos?

Each row carries three things: the post itself as Truth Social publishes it, a media summary computed from that post's attachments, and a normalized asset record for every attachment on it.

Fields the Actor writes itself

These seven keys are added by the Actor and are present on every post row, regardless of what Truth Social returns.

Field NameDescription
recordType"post" in post mode, "media_asset" in flatten mode β€” the discriminator to switch on
mediaCountNumber of entries in the post's media_attachments array. 0 on a text-only post
mediaTypesSorted, de-duplicated list of the attachment types present, e.g. ["image", "video"]. [] when there are none
hasImagetrue when "image" appears in mediaTypes
hasVideotrue when "video" appears in mediaTypes
hasGiftrue when "gifv" appears in mediaTypes
mediaAssetsArray of normalized asset objects, one per attachment β€” the eight keys below. [] on a text-only post

Each entry inside mediaAssets β€” and, in flatten mode, each row β€” carries these eight asset keys:

Field NameDescription
mediaIdTruth Social's own identifier for the attachment
typeAttachment type as Truth Social labels it: image, video, gifv, audio or anything else it returns
urlDirect media file URL, taken from the attachment's url, falling back to remote_url. null when neither is present
previewUrlThumbnail / poster URL from the attachment's preview_url. null when absent
descriptionThe attachment's alt-text description as written by the poster. Often null
widthPixel width from meta.original, falling back to meta.small. null when the attachment carries no dimensions
heightPixel height from the same source as width
filenameSuggestionA ready-to-use filename built as <username>_<postId>_media<N><ext>, where <ext> comes from the real asset URL and falls back to a per-type default (.jpg for image, .mp4 for video and gifv, .mp3 for audio). null when the asset has no URL

Post fields read straight from Truth Social

In post mode the row is the Truth Social status object with the seven keys above merged in. These are the status keys the Actor itself reads, resolves or rewrites:

Field NameDescription
idTruth Social's post ID (an 18-digit snowflake). The pagination key and the value stored as a resume bookmark
urlPublic permalink to the post on truthsocial.com
created_atPublication timestamp as the API returns it
contentPost body. HTML by default; converted to plain, whitespace-collapsed text when cleanContent is on β€” including inside nested reblog and quote objects
accountThe author object as Truth Social returns it β€” handle, display name, avatar, profile URL and the account's own counters
media_attachmentsThe raw attachment array, kept untouched next to the normalized mediaAssets list
in_reply_to_idNon-null marks the post as a reply. This is the field the replies filter switches on
reblogThe reposted status, nested, when the row is a repost. Its content is cleaned too when cleanContent is on
quoteThe quoted status, nested, when the row quotes another post. Cleaned the same way

Every other key Truth Social puts on a status β€” engagement counters, visibility, language, sensitivity flags, tags, mentions, card and poll blocks β€” is passed through to your row exactly as received. The Actor adds nothing else and strips nothing.

Flatten-mode rows

With flattenMedia on, a row is one attachment. It carries six parent-post keys plus the eight asset keys above, and nothing else β€” there is no post text and no account object in a flattened row.

Field NameDescription
recordTypeAlways "media_asset"
postIdThe parent post's id β€” join key back to a post-mode row
postUrlThe parent post's permalink
postCreatedAtThe parent post's created_at
accountUsernameAuthor handle, read from the parent post's account.username, falling back to account.acct
assetIndexZero-based position of this attachment in the parent post's media_attachments array

πŸ€– Add-on: Need additional Truth Social data?

This is the media-cataloging Actor in the Scraper-Engine line β€” there is no separate companion Actor for Truth Social comments or profiles, so post rows plus their account block are the full picture available here. If you track the same accounts across platforms, Twitter/X Posts Scraper and Threads User Posts Scraper cover the equivalent short-form feeds, and Facebook Posts Scraper covers public Pages and profiles with the same one-row-per-post shape.

How to use Truth Social Scraper With Photos & Videos

The Actor runs on Apify. Start it from the Apify Console or call it through the Apify API β€” there is no separate signup, no API key of your own and no local install.

  1. Open Truth Social Scraper With Photos & Videos on Apify and click Try for free
  2. Put one or more handles into Who should we scrape? (truthSocialUsername), one per line. The leading @ is optional, and a full profile URL such as https://truthsocial.com/@handle is accepted too. Set this yourself β€” leaving it empty does not stop the run, it falls back to realDonaldTrump
  3. Set How many posts? (maxPosts) β€” this is a ceiling per handle, not per run. Default 20, schema range 5–5000
  4. Decide the media rules: Posts with photos / video only (onlyFetchPostsWithMedia) drops text-only posts, and Media type filter (mediaType) narrows to image, video or gifv
  5. Turn on Flatten media (flattenMedia) if you want one row per attachment instead of one row per post
  6. Click Start, then export the dataset as JSON, CSV, Excel or XML

Handles are processed one after another, and each row is written to the dataset the moment it is built β€” you can read results while the run is still going.

How to scale to bulk post extraction

truthSocialUsername is a list, so one run can sweep as many handles as you like. maxPosts then applies to each one independently: five handles at maxPosts: 200 is a ceiling of 1,000 posts, not 200. fetchSinglePostByIdOrUrl is also a list, for pulling a known set of posts by ID or permalink β€” but the two are mutually exclusive in effect, because a non-empty single-post list makes the Actor skip profile mode entirely and ignore maxPosts. For a recurring sweep, put the same input on an Apify schedule and attach a webhook so each finished dataset lands in your own store.

What can you do with Truth Social post and media data?

  • πŸ—‚οΈ An OSINT researcher building an image archive turns on flattenMedia with mediaType: "image", then feeds url and filenameSuggestion straight into a download queue β€” the filename already carries the handle, post ID and attachment index, so the archive is self-describing without a rename pass.
  • 🎬 A video monitoring analyst filters on hasVideo and reads mediaCount, width and height to spot which posts carry vertical clips versus landscape uploads before deciding what to pull for review.
  • πŸ“° A journalist tracking a public account runs with onlyFetchReplies on to isolate that handle's replies, then reads content, created_at and url to build a sourced timeline with a permalink behind every line.
  • β™Ώ An accessibility auditor compares description against mediaCount across a handle's recent posts to measure how often attachments ship without alt text at all.
  • πŸ€– An AI engineer indexes content as the document body with url as the citation and the asset description values as image context, giving a retrieval agent live public posts to answer from instead of a stale export.

Each of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.

How does the scraper handle rate limits and blocking?

The Actor does not drive a browser and does not solve CAPTCHAs. It defends against blocking with TLS impersonation, residential egress and backoff.

Fingerprint. Direct requests go out through impit with Chrome 131 impersonation. Proxied requests use httpx with a matching Chrome 131 desktop header set, and on the residential tier each call first tries curl-cffi with Chrome 131 TLS impersonation before falling back to httpx β€” that clears more Cloudflare 403s.

Egress. When an Apify proxy password is available the Actor starts on Apify Residential and stays there, reusing one sticky proxy session across timeline pages so the same exit IP serves the whole pagination. Setting TRUTH_RESIDENTIAL_FIRST=0 switches to an escalating chain instead: direct β†’ Apify auto-proxy β†’ residential.

Backoff. A 403 or Cloudflare challenge triggers a cooldown that grows with each attempt β€” and grows further when the response body is a Cloudflare interstitial β€” then rotates to a new residential session with a fresh exit IP, up to 8 rotations. On 429 the Actor honours Retry-After (capped at 600 seconds) or waits at least 20 seconds and backs off from there; 408, 425, 500, 502, 503 and 504 get exponential backoff with jitter. A 404 is not retried at all. Successful timeline pages are spaced by a pause of 4.5 seconds plus jitter, overridable with TRUTH_PAGINATION_PAUSE.

If a request cannot be recovered inside those budgets the Actor raises and the run stops β€” no partial or placeholder row is ever written, and rows already saved stay in the dataset.

⬇️ Input

Eleven parameters and none of them are required. That is worth reading twice: an empty input is a valid run, and it will scrape realDonaldTrump.

ParameterRequiredTypeDescriptionExample Value
truthSocialUsernameNoarrayOne or more profile handles, one per line. The @ is optional and a full truthsocial.com profile URL is accepted. Empty falls back to realDonaldTrump.["realDonaldTrump"]
maxPostsNointegerPosts to collect per handle. Schema minimum 5, maximum 5000, default 20. Not used in single-post mode.100
onlyFetchPostsWithMediaNobooleanKeep only posts that have at least one attachment. Schema default true.true
mediaTypeNostringRestrict to one media kind: any, image, video or gifv. Default "any". Anything outside that list is silently treated as "any"."image"
flattenMediaNobooleanOn: one row per attachment, recordType "media_asset". Off: one row per post. Default false.true
continueFromLastPostIdNobooleanOnly fetch posts newer than the last post ID saved for that handle. Default false. See the note below.false
onlyFetchRepliesNobooleanOn: return only that handle's replies. Off: replies are excluded. Default false.false
cleanContentNobooleanConvert content from HTML to plain text, including inside nested reblog and quote. Default true.true
startFromPostIdNostringAdvanced. Exactly 18 digits β€” loads posts older than this ID. Cannot be combined with continueFromLastPostId."113900000000000000"
fetchSinglePostByIdOrUrlNoarrayAdvanced. Post IDs or full Truth Social post links, one per line. When non-empty, profile mode is skipped entirely.["https://truthsocial.com/@realDonaldTrump/posts/113900000000000000"]
proxyConfigurationNoobjectApify Proxy settings. See the note below β€” the Actor does not read this field.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}

⚠️ Two parameters do not behave the way their names suggest.

proxyConfiguration is not read by the Actor at all. Proxy behaviour comes from the Apify proxy environment variables and the Actor's own residential-first ladder, so changing the groups here β€” or unchecking Apify Proxy β€” has no effect on the connection. Keep Apify Proxy enabled on your account and the Actor handles the rest.

continueFromLastPostId writes its bookmark to the key truth_scraper_state in the run's default key-value store, and reads it from the same place at the next run's start. On Apify, each run gets a fresh default key-value store, so a later Console run does not see the previous run's bookmark and the toggle has no effect on its own. It resumes as designed when the same store is reused β€” a local apify run against the same storage directory, for example.

Four more honest notes on the remaining inputs:

  • maxPosts is a fetch budget, not a row guarantee. It counts posts that survive the reply and media gates during pagination, at up to 40 posts per API page. The mediaType filter is applied after that, so a post fetched and then dropped for having the wrong media kind still consumed one unit of your budget β€” with mediaType: "video" you can finish well below maxPosts rows. In the other direction, flatten mode can produce more rows than maxPosts, because a single post with four images becomes four rows. A value below 5 is silently raised to 5; the code applies no upper clamp of its own, so 5000 is the schema's ceiling.
  • Two filters run on Truth Social's side, one runs locally, and there is no date filter. onlyFetchPostsWithMedia and onlyFetchReplies are sent to the API as request parameters and re-checked on every returned post; mediaType is computed locally from each post's real attachments. There is no from/to date input anywhere in this Actor β€” the only way to bound a time window is by post ID, using startFromPostId to go older or continueFromLastPostId to go newer.
  • Some combinations stop the run rather than being ignored. continueFromLastPostId together with startFromPostId raises an error, and a startFromPostId that is not exactly 18 digits does the same. Both checks are skipped entirely in single-post mode.
  • Legacy input keys are still accepted and are not in the schema, so the Console will not show them: username, urls, singlePostId, useLastPostId, startFromId, onlyReplies and onlyMedia. urls is the most useful of them β€” it accepts a mixed list where each entry is classified as either a post ID/link or a profile handle/URL. One asymmetry to know when you build input programmatically: the schema default for onlyFetchPostsWithMedia is true, but the code's internal fallback, used if the key never reaches it, is false. Set it explicitly rather than relying on either.

Example input

{
"truthSocialUsername": ["realDonaldTrump", "newsroomdesk"],
"maxPosts": 100,
"onlyFetchPostsWithMedia": true,
"mediaType": "any",
"flattenMedia": false,
"continueFromLastPostId": false,
"onlyFetchReplies": false,
"cleanContent": true,
"startFromPostId": "",
"fetchSinglePostByIdOrUrl": [],
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"]
}
}

Working with the two record types in code

if row["recordType"] == "media_asset":
save(row["url"], row["filenameSuggestion"])
else:
for asset in row["mediaAssets"]:
save(asset["url"], asset["filenameSuggestion"])

⬆️ Output

Rows are typed JSON, pushed to the dataset as each post is built, and exportable as JSON, CSV, Excel or XML β€” or readable through the Apify API. The row shape is decided by one toggle, and recordType always tells you which one you are holding.

Three shape rules to code against. First, the seven Actor-written keys (recordType, mediaCount, mediaTypes, hasImage, hasVideo, hasGif, mediaAssets) are present on every post row, and flatten rows always carry all fourteen of their keys β€” missing values arrive as null or [], never omitted. Second, the pass-through half of a post row is Truth Social's own status object: a key that Truth Social does not return for a given post is simply absent from your row, so use .get() on anything outside those seven. Third, flatten rows carry no post text and no author object beyond accountUsername β€” keep a post-mode run alongside if you need the text, and join on postId.

Every row in the dataset is a post or a media asset. The Actor writes no error rows, no summary rows and no accounting rows, so there is nothing to filter out β€” and because one row_result event is charged per saved row, a handle that returns nothing costs nothing. There is no de-duplication anywhere: list a handle twice, or re-run without a resume bookmark, and the same post is written again.

Note that mediaCount, mediaTypes and mediaAssets are computed from the top-level media_attachments only. A repost carries its media inside the nested reblog object, so a reposted photo does not raise mediaCount and, with onlyFetchPostsWithMedia on, a media repost is normally filtered out rather than collected.

Example output β€” post mode

One post row, cleanContent on. The seven Actor-written keys are merged into Truth Social's status object; the rest of the keys, and their values, are Truth Social's. Values below are illustrative.

{
"id": "113904417238899142",
"created_at": "2026-07-22T14:07:33.000Z",
"in_reply_to_id": null,
"in_reply_to_account_id": null,
"sensitive": false,
"spoiler_text": "",
"visibility": "public",
"language": "en",
"uri": "https://truthsocial.com/users/newsroomdesk/statuses/113904417238899142",
"url": "https://truthsocial.com/@newsroomdesk/posts/113904417238899142",
"content": "Crews finished the bridge deck overnight. Two lanes reopen at 6am β€” photos from the site below.",
"replies_count": 41,
"reblogs_count": 118,
"favourites_count": 507,
"reblog": null,
"quote": null,
"account": {
"id": "107780257626145820",
"username": "newsroomdesk",
"acct": "newsroomdesk",
"display_name": "Newsroom Desk",
"note": "Local reporting, posted as it happens.",
"url": "https://truthsocial.com/@newsroomdesk",
"avatar": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/accounts/avatars/107/780/257/626/145/820/original/a1f0c9e4d2b73815.jpg",
"header": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/accounts/headers/107/780/257/626/145/820/original/6b2d41c88ee0f537.jpg",
"followers_count": 24193,
"following_count": 312,
"statuses_count": 8741,
"created_at": "2022-02-14T00:00:00.000Z",
"locked": false,
"bot": false,
"verified": false
},
"media_attachments": [
{
"id": "113904416900117403",
"type": "image",
"url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/900/117/403/original/9c1e77a4b0d3f215.jpg",
"preview_url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/900/117/403/small/9c1e77a4b0d3f215.jpg",
"remote_url": null,
"description": "Road crew laying asphalt on a bridge deck at night under floodlights",
"meta": {
"original": { "width": 1600, "height": 1067, "aspect": 1.4995 },
"small": { "width": 480, "height": 320, "aspect": 1.5 }
}
},
{
"id": "113904416988402551",
"type": "video",
"url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/988/402/551/original/4d77b0ac91e26f38.mp4",
"preview_url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/988/402/551/small/4d77b0ac91e26f38.png",
"remote_url": null,
"description": null,
"meta": {
"original": { "width": 1280, "height": 720, "duration": 24.6 }
}
}
],
"mentions": [],
"tags": [],
"recordType": "post",
"mediaCount": 2,
"mediaTypes": ["image", "video"],
"hasImage": true,
"hasVideo": true,
"hasGif": false,
"mediaAssets": [
{
"mediaId": "113904416900117403",
"type": "image",
"url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/900/117/403/original/9c1e77a4b0d3f215.jpg",
"previewUrl": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/900/117/403/small/9c1e77a4b0d3f215.jpg",
"description": "Road crew laying asphalt on a bridge deck at night under floodlights",
"width": 1600,
"height": 1067,
"filenameSuggestion": "newsroomdesk_113904417238899142_media1.jpg"
},
{
"mediaId": "113904416988402551",
"type": "video",
"url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/988/402/551/original/4d77b0ac91e26f38.mp4",
"previewUrl": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/988/402/551/small/4d77b0ac91e26f38.png",
"description": null,
"width": 1280,
"height": 720,
"filenameSuggestion": "newsroomdesk_113904417238899142_media2.mp4"
}
]
}

Example output β€” flatten mode

The same post with flattenMedia on produces two rows of exactly fourteen keys each. This is the second one:

{
"recordType": "media_asset",
"postId": "113904417238899142",
"postUrl": "https://truthsocial.com/@newsroomdesk/posts/113904417238899142",
"postCreatedAt": "2026-07-22T14:07:33.000Z",
"accountUsername": "newsroomdesk",
"assetIndex": 1,
"mediaId": "113904416988402551",
"type": "video",
"url": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/988/402/551/original/4d77b0ac91e26f38.mp4",
"previewUrl": "https://static-assets-1.truthsocial.com/tmtg:prime-ts-assets/media_attachments/files/113/904/416/988/402/551/small/4d77b0ac91e26f38.png",
"description": null,
"width": 1280,
"height": 720,
"filenameSuggestion": "newsroomdesk_113904417238899142_media2.mp4"
}

How does it work?

Truth Social runs a Mastodon-compatible HTTP API, and that is what this Actor talks to β€” not rendered HTML, and not a headless browser. It resolves each handle through the account lookup endpoint, then walks that account's statuses endpoint page by page, up to 40 posts per request, carrying Truth Social's own pagination parameters: max_id to go older, since_id to resume, plus the media-only and reply flags. Single posts are fetched directly by ID. Because there are no CSS selectors and no HTML parsing anywhere in the path, a Truth Social visual redesign does not move your field names.

Requests leave through Apify Residential proxies with Chrome 131 TLS impersonation, on a sticky session that rotates to a new exit IP when Cloudflare pushes back. Each post is normalized and pushed the moment it arrives, so the dataset fills continuously. Only publicly visible posts are collected β€” no account, token, cookie or session is used at any point.

Because the source is a documented, Mastodon-shaped API rather than a scraped page, there is no meaningful gap between "what an official API would give you" and what lands in your dataset. The work this Actor does is the part around it: Cloudflare-tolerant egress, retry and backoff, pagination, resume state, and the media normalization layer.

Integrations

Truth Social Scraper With Photos & Videos is an Apify Actor, so it works with anything that can call the Apify API or read a dataset.

Calling the Actor from Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/truth-social-media-post-collector").call(run_input={
"truthSocialUsername": ["newsroomdesk"],
"maxPosts": 100,
"onlyFetchPostsWithMedia": True,
"mediaType": "image",
"flattenMedia": True,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row["postId"], row["type"], row["filenameSuggestion"], row["url"])

Works in Go, Ruby, Node.js, cURL β€” any language that can make an HTTP request. Switch on recordType if a downstream consumer might receive both row shapes.

Scheduling and webhooks

Put the same input on an Apify schedule for a recurring sweep of a handle set, and attach a webhook on run success so each finished dataset is posted to your own endpoint instead of being polled. Because there is no date filter, bound a recurring run with maxPosts and de-duplicate on id (post mode) or mediaId (flatten mode) on your side.

No-code tools (n8n, Make, LangChain)

In n8n, use the Apify node β€” or an HTTP Request node pointed at the Actor's run endpoint with your Apify token β€” and pass the same JSON input shown above; an Item Lists node then splits the dataset into one item per post or attachment. In Make, the Apify module supports run-and-wait, so a daily handle monitor can feed Google Sheets, Airtable or a Slack message directly. For LangChain, the Actor is a plain HTTP tool: call the run endpoint, then read the dataset and pass content straight into a chain with url as the citation.

Scraping publicly visible posts is broadly treated as permissible where no authentication is bypassed, and this Actor returns only public posts β€” what any logged-out visitor sees. But the output is personal data, and that shifts obligations onto you, not the Actor.

Post rows carry identity and authored content. Specifically: account.username, account.acct, account.display_name, account.url, account.avatar and account.header identify a real person or organisation; accountUsername does the same on flatten rows; content is that person's own words; description is text they wrote; media url and previewUrl point at images and video they published, which may show identifiable faces; and id, url and uri are persistent identifiers pointing back to an individual. Under GDPR, UK GDPR and the CCPA, storing or processing those fields needs a lawful basis of your own β€” most commonly legitimate interest, which requires a documented balancing test weighing your purpose against the poster's reasonable expectations. Public availability is not a lawful basis by itself.

Two points specific to this platform. Political opinions are special-category data under GDPR Article 9, and post text on Truth Social frequently reveals them, so a bulk store of content may attract Article 9 obligations that ordinary post text would not. And the balancing test lands differently for a public figure posting in a public role than for a private individual, so segment your collection accordingly rather than treating every handle the same.

Article 14 also applies: because you collect this data without contacting the individual, you owe transparency about what you hold and why, subject to its exceptions. Practise minimisation β€” if you only need a media inventory, run in flatten mode, which carries no post text and no author profile beyond the handle. Set a retention limit rather than keeping rows indefinitely, and be able to honour access and deletion requests against whatever you store.

A media url in your dataset is a link to a file, not a licence to redistribute it β€” copyright in the photo or video stays with its owner regardless of how you obtained the URL.

Consult legal counsel if your use case involves bulk storage of personal data, profiling individuals, or republishing post text, author identities or media files.

❓ Frequently asked questions

Does this Actor collect media files only, or all posts?

It collects posts, and catalogs the media on them. The name describes the emphasis, not a restriction: post mode returns the entire post β€” text, permalink, timestamp, author and counters β€” with a media summary and a normalized asset list merged in. The media-first behaviour comes from two switches: onlyFetchPostsWithMedia (on by default in the schema) drops text-only posts, and flattenMedia turns each attachment into its own row. Turn the first off and you get every post, media or not.

What Truth Social post fields does the Actor return?

The five you will use most are content, url, created_at, mediaAssets and account. Seven keys are written by the Actor on every post row β€” recordType, mediaCount, mediaTypes, hasImage, hasVideo, hasGif and mediaAssets β€” alongside the complete Truth Social status object. Flatten rows have a fixed set of fourteen keys. See the data fields tables above for all of them.

Does the Actor return direct media URLs, and can I download from them?

Yes. Each asset's url is the direct file URL exactly as Truth Social's API publishes it β€” a .jpg, .mp4 or equivalent on Truth Social's static asset host β€” with previewUrl as the thumbnail or poster frame. The Actor does not download, re-host, sign or refresh those URLs; whether one still resolves later is entirely Truth Social's to decide, so fetch promptly if you intend to archive. filenameSuggestion gives you a collision-resistant local name for each file. Availability of a URL is not a licence to redistribute the file.

Does the Actor require a Truth Social account, login or API token?

No. No account, no cookie, no session, no OAuth app and no Truth Social API token. The Actor sends unauthenticated requests to Truth Social's public Mastodon-compatible API and reads only what it serves to an anonymous client. The one credential you need is your Apify token.

How many posts can I extract in one run?

maxPosts is the ceiling per handle, so a run's total is roughly maxPosts Γ— the number of handles, bounded by how many posts each account has actually published. The schema range is 5–5000 and pagination pulls up to 40 posts per API request. Two adjustments: posts dropped by the mediaType filter still consume budget, so you can land below your number; and flatten mode can exceed it, since each attachment is its own row.

What happens if a handle is private, suspended, deleted or has no media posts?

It depends on which one. A handle that Truth Social's lookup endpoint does not resolve β€” deleted, suspended, or simply mistyped β€” raises a 404, which is not retried and stops the whole run, so put one handle per run when you are working through an unverified list. A locked or private account resolves, but its posts are not served to an anonymous client, so the timeline comes back empty, pagination ends and that handle contributes no rows. A handle with no media posts and onlyFetchPostsWithMedia on behaves the same way: zero rows, no error. Single posts are the exception to the strict rule β€” a 404 on an entry in fetchSinglePostByIdOrUrl is logged, skipped, and the run continues. Since charging is per saved row, an empty handle costs nothing.

Can I scrape multiple Truth Social accounts at once?

Yes. truthSocialUsername is a list β€” add as many handles as you like, one per line, with or without the @, and full profile URLs work too. They are processed sequentially with a pause between pages to keep request rates low, and maxPosts applies to each independently. The undocumented urls key accepts a mixed list of handles and post links in one array if you build input by hand.

Are reposts and quote posts distinguished from original posts?

Only in post mode, and only through Truth Social's own fields: a repost carries a nested reblog object and a quote post carries a nested quote object, both cleaned to plain text alongside the parent when cleanContent is on. The Actor adds no isRepost flag of its own β€” check whether reblog or quote is non-null. Two consequences worth planning for: media inside a nested repost is not counted in mediaCount or listed in mediaAssets, and a flattened row carries no repost information at all.

Does the Actor work with Claude, ChatGPT and other AI agent tools?

Yes. It is callable as a standard HTTP endpoint through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step.

How does this compare to other Truth Social scrapers?

Checked on the Apify Store on 25 July 2026, the comparable listings are scrapebase/truth-social-profile-posts-scraper, scrapemesh/truth-social-profile-posts-scraper and scrapelabsapi/truth-social-profile-posts-scraper; the last two did not return a README when checked, so nothing is claimed about them. The scrapebase listing documents the same underlying source β€” truthsocial.com/api/v1 with account lookup and paginated statuses β€” a required username input, a maxItems cap it states as 1–1000 with a default of 100, firstPostId/lastPostId pagination bounds and a proxyConfiguration field, with output described as "one row per post β€” full API status objects". Its listing documents no media summary, no media-type filter and no per-attachment output shape.

Observable differences here: a computed media catalog (mediaCount, mediaTypes, hasImage/hasVideo/hasGif, mediaAssets) merged into every post row, an image/video/gifv filter, a flatten mode that emits one row per attachment with a suggested filename, and a multi-handle list input rather than a single username. In the other direction, that listing documents a required username where this Actor documents a silent fallback, and this Actor has no date-range input.

Does the Actor return data in a format LLMs can use directly?

Yes. Rows are typed, normalized JSON with the same Actor-written field names on every run. No HTML parsing, no selectors β€” and with cleanContent on, content arrives as plain, whitespace-collapsed text rather than markup, so it goes straight into a context window, a vector store or an agent tool.

What happens when Truth Social changes its layout or anti-bot system?

The Actor is maintained, and a layout change is largely a non-event for it: it reads a Mastodon-compatible JSON API rather than rendered HTML, so a redesign of the site does not move any field. The exposed surface is the anti-bot layer, which is handled by Chrome 131 TLS impersonation, residential session rotation and backoff. The seven Actor-written keys and the flatten row shape are defined by this Actor's own code, so they stay stable on your end regardless.

Can I use it without managing proxies or browser infrastructure?

Yes. There is no browser to provision β€” the Actor talks to the API over HTTP with browser-accurate TLS impersonation. Egress is automatic: it uses Apify Residential by default, keeps one sticky session across pagination and rotates the exit IP itself on a 403 or Cloudflare challenge. You never create a proxy account or pick an IP. It does not solve CAPTCHAs; it avoids challenges through fingerprinting and pacing, and cools off when Cloudflare pushes back.

Which fields work best for AI training data and RAG indexing?

For RAG indexing: content is the document body, the asset description values add human-written image and video context, and url is the citation link β€” one post chunks cleanly as one document. For training data and analytics: created_at, mediaCount, mediaTypes, hasImage, hasVideo, hasGif, width and height are the most structurally consistent values across records, since the Actor computes them itself and they are present on every post row. All of them come back as typed primitives, arrays or plain nested objects, so no normalization pass is needed β€” and if your index does not need author identity, drop the account object before you store anything.

Scraper NameWhat it extracts
Twitter/X Posts ScraperPublic posts from X profiles β€” text, engagement counts and media
Twitter/X Reply ScraperReplies under a given X post, with author and timestamp
Threads User Posts ScraperPublic Threads posts by handle, with keyword and date filters
Facebook Posts ScraperPublic Page and profile posts β€” text, reactions, media, transcripts
Instagram Profile ScraperInstagram profile data for cross-platform account monitoring
Reddit Posts ScraperPublic Reddit posts by keyword with score filtering

πŸ’¬ Your feedback

Found a bug, or need a field that Truth Social's API returns but this Actor does not surface? Open an issue on the Actor's Issues tab. Reports that include the exact input JSON and the handle or post URL you ran against are the fastest to reproduce and fix.