Threads Posts Scraper
Pricing
from $4.70 / 1,000 post returneds
Threads Posts Scraper
Scrape posts from any public Threads profile: full text, like/reply/repost/quote counts, media, and timestamps.
Pricing
from $4.70 / 1,000 post returneds
Rating
0.0
(0)
Developer
EndSpec
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Threads Posts Scraper: Every Post From Any Public Threads Profile
Scrape posts from any public Threads profile — full post text, engagement counts, media, and timestamps — with no login, no cookies, and no browser.
Simple Input-Output Example
Input:
{"usernames": ["natgeo"],"maxPostsPerProfile": 50}
Output (one row per post):
{"postId": "3942534943066968922","code": "Da2sqRsElNa","url": "https://www.threads.com/@natgeo/post/Da2sqRsElNa","username": "natgeo","fullName": "National Geographic","userId": "63269174602","isVerified": true,"profilePicture": "https://scontent.cdninstagram.com/v/natgeo-avatar.jpg","text": "Find someone who's as excited for World Snake Day as this little garter snake.","likes": 1093,"replies": 92,"reposts": 45,"quotes": 7,"media": {"type": "image","imageUrl": "https://scontent.cdninstagram.com/v/garter-snake.jpg","width": 1080,"height": 1350,"isCarousel": false,"carouselCount": 0,"altText": "A garter snake coiled on a mossy log"},"isQuote": false,"quotedPostCode": null,"createdAt": "2026-07-16T16:00:11+03:00","createdAtUnix": 1784206811}
Important Notes:
- Results come back in seconds — no login, no cookies, no browser automation
- Pay per result — $0.00499 per post returned. Failed and empty lookups are never charged
- Public profiles only, and profile feeds only — this Actor does not search Threads by keyword or hashtag
- Posts are returned newest first;
maxPostsPerProfilecontrols how deep each profile goes (1–500) - All data comes from public sources only
- Contact: contact@endspec.net
Full Actor Documentation
Threads Posts Scraper
Get every public post from any Threads account, as clean structured data. Post text, like/reply/repost/quote counts, media details, author info, and exact publish timestamps — for one profile or a whole list of them.
Overview
The Threads Posts Scraper is an Apify Actor that scrapes posts from public profiles on Threads. Give it a list of Threads usernames or profile URLs and it walks each profile's feed newest-first, returning one clean row per post with the full text, all four engagement counters, media metadata, and timestamps.
It runs against a data service, not a browser — so there is no login step, no cookie handling, no proxy tuning, and no CAPTCHA to solve. You send usernames, you get rows.
What this Actor does not do: it does not search Threads by keyword, hashtag, or topic, and it does not read private profiles. It is a profile-feed scraper, deliberately and only. If you need keyword search across Threads, this is not the right tool.
What You Can Do
- Scrape a Threads profile's full post feed: newest first, up to 500 posts per profile per run
- Capture complete post text: the full body of each post, not a truncated preview
- Measure engagement: likes, replies, reposts, and quotes for every post
- Collect media metadata: media type (image / video / carousel), image URL, dimensions, carousel item count, and accessibility alt text
- Identify quote posts: know whether a post quotes another, and which post it quotes
- Track multiple accounts in one run: pass a list of usernames and get all their posts into a single dataset
- Feed downstream systems: export to JSON, CSV, or Excel from the Apify dataset, or pull it via the Apify API into your own pipeline
Data Availability
All data returned by this Actor is publicly available information. It only returns posts that account owners have published publicly on Threads — the same content any logged-out visitor can see on a public profile page. Private profiles cannot be retrieved. This Actor simply makes public post data easier to collect programmatically and at scale.
Actor Input Parameters
The Actor accepts the following input parameters:
usernames
• Type: array of strings
• Required: Yes (in practice — the run fails with no valid entry)
• Default: prefilled with ["natgeo"] in the Apify console
• Description: Threads usernames or profile URLs to scrape. Each entry may be a bare handle, an @handle, or a full profile URL.
e.g., ["natgeo", "@zuck", "https://www.threads.com/@mosseri"]
maxPostsPerProfile
• Type: integer
• Required: Optional
• Default: 50
• Minimum: 1 — Maximum: 500
• Description: Maximum number of posts to return for each profile in usernames. Posts are fetched newest first.
e.g., 100
Important Notes:
- At least one valid username is required — the input schema marks nothing as strictly required, but a run with no usable entry in
usernamesfails immediately withNo valid Threads username or profile URL provided.and returns no rows and no charges. - Three input formats, all equivalent —
natgeo,@natgeo, andhttps://www.threads.com/@natgeoall resolve to the same profile.threads.netURLs work too. Mix formats freely in one list. - Invalid entries are skipped, not fatal — an entry that isn't a recognizable username or profile URL is dropped with a warning in the run log; the remaining profiles still run.
- Duplicates are removed — the same profile listed twice (in any format) is scraped once.
maxPostsPerProfileis per profile, not per run — three profiles atmaxPostsPerProfile: 100can return up to 300 posts, and you are billed for every post returned.- The cap is a ceiling, not a target — a profile with 12 posts returns 12 posts, whatever the cap says.
Input Examples
Example 1: Single Profile, Default Depth
{"usernames": ["natgeo"]}
When to use: The simplest run. Returns up to 50 of @natgeo's most recent posts (the default maxPostsPerProfile).
Example 2: Multiple Profiles, Mixed Input Formats
{"usernames": ["natgeo","@zuck","https://www.threads.com/@mosseri"],"maxPostsPerProfile": 100}
When to use: Competitor or portfolio tracking across several accounts. All three formats are accepted and normalized to the same thing — you do not need to clean your list first.
Example 3: Shallow Probe of a Large List
{"usernames": ["natgeo", "zuck", "mosseri", "threads", "instagram"],"maxPostsPerProfile": 10}
When to use: Checking recent activity across many accounts cheaply, or test-driving the Actor before a big run. Low maxPostsPerProfile keeps both runtime and cost small.
Example 4: Deep Archive of One Profile
{"usernames": ["natgeo"],"maxPostsPerProfile": 500}
When to use: Backfilling a profile's post history with full engagement data. 500 is the hard maximum per profile per run.
Example 5: No Usable Input (Will Fail)
{"usernames": []}
Result: The run fails with No valid Threads username or profile URL provided. No rows are produced and nothing is charged. The same happens if every entry in the list is malformed.
Output Structure
The Actor pushes one row per post to the Apify dataset. Results can be downloaded as JSON, CSV, Excel, XML, or fetched via the Apify API. The dataset's Overview table view shows username, text, likes, replies, reposts, quotes, createdAt, and url; the full field set is always present in the raw data and in exports.
Successful Post Row Format
{"postId": "string","code": "string","url": "string","username": "string","fullName": "string","userId": "string","isVerified": true,"profilePicture": "string","text": "string","likes": 0,"replies": 0,"reposts": 0,"quotes": 0,"media": null,"isQuote": false,"quotedPostCode": null,"createdAt": "string","createdAtUnix": 0}
Field Descriptions:
postId • Type: string • Description: Threads' numeric identifier for the post. Stable — use it as your deduplication key.
code • Type: string • Description: The post's short code, as it appears at the end of its Threads URL.
url • Type: string • Description: Direct link to the post on Threads.
username
• Type: string
• Description: Handle of the post's author (without @). Falls back to the username you supplied if the post carries no author record.
fullName • Type: string | null • Description: The author's display name.
userId • Type: string | null • Description: Threads' numeric identifier for the author.
isVerified • Type: boolean | null • Description: Whether the author's account carries a verification badge.
profilePicture • Type: string | null • Description: URL of the author's avatar image.
text
• Type: string | null
• Description: The full text of the post. null or empty for media-only posts.
likes • Type: number | null • Description: Like count at the time of scraping.
replies • Type: number | null • Description: Reply count at the time of scraping.
reposts • Type: number | null • Description: Repost count at the time of scraping.
quotes • Type: number | null • Description: Quote count at the time of scraping.
media
• Type: object | null
• Description: Media attached to the post, or null for text-only posts. When present, contains:
type— one of"image","video", or"carousel"imageUrl— direct URL to the post's image (for video and carousel posts, the cover image)width/height— pixel dimensions of that imageisCarousel— boolean; true for multi-item postscarouselCount— number of items in the carousel (0when not a carousel)altText— the accessibility caption, when the author or platform supplied one
isQuote
• Type: boolean
• Description: true when this post quotes another post.
quotedPostCode
• Type: string | null
• Description: The short code of the quoted post, when isQuote is true.
createdAt • Type: string | null • Description: Publish time as an ISO 8601 timestamp.
createdAtUnix • Type: number | null • Description: Publish time as a Unix timestamp, for easy sorting and filtering.
Error and Empty-Result Row Format
Rows that are not posts carry a status field. Successful post rows have no status field — testing for the presence of status is the reliable way to separate posts from problems:
{"status": "error","username": "string","error": "string"}
status
• Type: string
• Description: Always "error" on these rows.
username • Type: string • Description: The normalized username the row refers to, so you can tell which profile in your list had the problem.
error • Type: string • Description: One of exactly four messages (see the examples below). Every one of them ends with "You were not charged." — because you were not.
Output Examples
Example 1: Image Post With Full Engagement Data
{"postId": "3942534943066968922","code": "Da2sqRsElNa","url": "https://www.threads.com/@natgeo/post/Da2sqRsElNa","username": "natgeo","fullName": "National Geographic","userId": "63269174602","isVerified": true,"profilePicture": "https://scontent.cdninstagram.com/v/natgeo-avatar.jpg","text": "Find someone who's as excited for World Snake Day as this little garter snake.","likes": 1093,"replies": 92,"reposts": 45,"quotes": 7,"media": {"type": "image","imageUrl": "https://scontent.cdninstagram.com/v/garter-snake.jpg","width": 1080,"height": 1350,"isCarousel": false,"carouselCount": 0,"altText": "A garter snake coiled on a mossy log"},"isQuote": false,"quotedPostCode": null,"createdAt": "2026-07-16T16:00:11+03:00","createdAtUnix": 1784206811}
Example 2: Text-Only Post That Quotes Another Post
Text-only posts return media: null. Nothing is missing — there is simply no media on the post.
{"postId": "3942534943066968923","code": "Da2sqRsElNb","url": "https://www.threads.com/@natgeo/post/Da2sqRsElNb","username": "natgeo","fullName": "National Geographic","userId": "63269174602","isVerified": true,"profilePicture": "https://scontent.cdninstagram.com/v/natgeo-avatar.jpg","text": "This is the part everyone gets wrong about snake season.","likes": 141,"replies": 12,"reposts": 3,"quotes": 1,"media": null,"isQuote": true,"quotedPostCode": "DadRSwPoMbB","createdAt": "2026-07-15T12:13:31+03:00","createdAtUnix": 1784106811}
Example 3: Carousel Post With Partial Author Data
Not every field is populated on every post. Missing values come back as null rather than being dropped, so your schema stays stable.
{"postId": "3942534943066968931","code": "Da2sqRsElNc","url": "https://www.threads.com/@smallshop/post/Da2sqRsElNc","username": "smallshop","fullName": null,"userId": "71422900135","isVerified": false,"profilePicture": null,"text": "Three new colorways, live now.","likes": 18,"replies": 2,"reposts": 0,"quotes": 0,"media": {"type": "carousel","imageUrl": "https://scontent.cdninstagram.com/v/colorway-1.jpg","width": 1080,"height": 1080,"isCarousel": true,"carouselCount": 3,"altText": null},"isQuote": false,"quotedPostCode": null,"createdAt": "2026-07-14T09:02:44+03:00","createdAtUnix": 1783929764}
Example 4: Profile Found, But No Posts (Empty Result)
The profile resolved, but its feed returned nothing — an account that has never posted, or one whose posts are not publicly visible.
{"status": "error","username": "emptyaccount","error": "No posts were found for this profile. You were not charged."}
Example 5: Profile Could Not Be Retrieved
Emitted when the profile cannot be resolved — a typo, a handle that no longer exists, a private account, or an intermittent lookup failure. Retrying is worthwhile: see the FAQ.
{"status": "error","username": "no_such_handle_here","error": "This profile could not be retrieved. Check that the username is correct and the profile is public, then retry. You were not charged."}
Example 6: Temporarily Rate Limited
{"status": "error","username": "natgeo","error": "Our servers are busy right now — please retry shortly. You were not charged."}
Example 7: Unexpected Failure
{"status": "error","username": "natgeo","error": "Something went wrong on our side. You were not charged."}
Those four error strings are the complete set. The Actor never emits anything else in that field — no raw technical errors, no stack traces, no partial internal detail. If you see one of these, it means what it says, and no charge was made for it.
Use Cases
For Social Media Marketers and Brand Teams
Competitive and brand monitoring: Track what competitor accounts publish on Threads, how often, and what actually lands. The four engagement counters on every post let you rank content by real performance rather than gut feel.
Example Workflow:
- List your competitors' Threads handles in
usernames - Set
maxPostsPerProfileto cover your reporting window (e.g.100) - Schedule the Actor to run daily or weekly in the Apify console
- Export the dataset to CSV or push it to your BI tool
- Sort by
likesandrepliesto see which themes outperform, and bycreatedAtto see cadence
For Analysts and Researchers
Engagement benchmarking: Build a baseline for what "good" looks like in your category. Because every post carries likes, replies, reposts, quotes, and createdAtUnix, you can compute engagement rates, posting frequency, and time-of-day effects across any set of accounts.
Example Workflow:
- Assemble a cohort of accounts in the same category
- Run the Actor at
maxPostsPerProfile: 500to backfill history - Group rows by
usernameand bucketcreatedAtUnixby week - Compare median engagement per post across the cohort
- Re-run periodically and append to track drift over time
For Businesses and Content Teams
Content research and repurposing: Find which of your own Threads posts performed best, and mine adjacent accounts for format ideas — carousels vs. single images vs. text-only is directly visible in the media field.
Example Workflow:
- Scrape your own profile plus three accounts you admire
- Split rows by
media.type(image,video,carousel, ornullfor text-only) - Compare average engagement per media type
- Rebuild your content calendar around the formats that actually win
For Developers and Data Teams
Pipeline integration: Pull Threads content into a warehouse, a social listening product, or an LLM pipeline. The output shape is fixed and every field is documented, so it maps cleanly onto a table.
Example Workflow:
- Call the Actor via the Apify API or a scheduled task
- Use the presence of
statusto route error rows away from post rows - Deduplicate against your store using
postId - Load
textinto your embedding or classification pipeline - Keep
likes/replies/reposts/quotesas time-series snapshots per scrape
Best Practices
Input
- Don't pre-clean your list — bare handles,
@handles, and profile URLs all work, and duplicates are removed for you - Start small — do a
maxPostsPerProfile: 10test run against your list before launching a 500-per-profile job - Remember the cap multiplies —
maxPostsPerProfileapplies to every profile, so a 20-profile list at500can return up to 10,000 billable posts - Batch sensibly — profiles are processed one after another, so very large lists mean longer runs; split them across several runs or scheduled tasks
Error Handling
- Test for
status, not for success — post rows have nostatusfield; anything carryingstatus: "error"is a problem row, not a post - Retry a "profile could not be retrieved" — profile lookups can fail intermittently even for accounts that exist. The Actor already retries internally before giving up, but a fresh run on that single handle is often enough to get it
- Treat "our servers are busy" as transient — wait a little and re-run just the affected profiles
- If every profile in a run comes back busy or failed, contact us — a whole run failing is not normal and is worth a support ticket
- Log the Apify run ID — include it when you contact support
Data Usage
- Deduplicate on
postId— it is stable across runs;codeandurlare fine for humans butpostIdis the key - Snapshot, don't assume — engagement counts are true at the moment of scraping and keep moving; store them with a scrape timestamp if you care about trends
- Sort with
createdAtUnix— it is the least ambiguous of the two timestamps for programmatic use - Respect privacy and platform terms — use public post data for legitimate business purposes only
Cost, Performance, and Limits
Billing
- You pay per post returned — the Actor charges a single event,
post-returned, once for each post row it delivers. $0.00499 per post. - Errors and empty results are free — every
status: "error"row is unbilled, which is why each of those messages ends in "You were not charged." - Failed profiles don't poison the run — in a multi-profile run you pay for the posts you actually received, and nothing for the profiles that failed
- Partial results are billed as delivered — if a profile returns some posts and then the feed stops early, you keep and pay for the posts you got, and no error row is added for that profile
Performance
- Fast, and no browser — this is a data-service Actor, not a headless-browser scraper. Runs finish in seconds to a couple of minutes depending on how many posts you ask for.
- Posts arrive in pages — the feed is paginated behind the scenes and rows stream into the dataset as pages come in
- Retries are built in — profile resolution and page fetches are retried automatically with backoff before any error row is emitted, so ordinary transient hiccups never reach you
Limits
- Profile feeds only. There is no keyword, hashtag, or topic search in this Actor. It cannot answer "who mentioned my brand on Threads" — only "what did this account post".
- Public profiles only. Private accounts return
This profile could not be retrieved... - 500 posts per profile per run is the hard maximum; the default is 50. For deeper history, that ceiling is the ceiling.
- Newest first, no date filter. You control depth with
maxPostsPerProfile; you cannot request a specific date range. Filter oncreatedAt/createdAtUnixafter the fact. - Media metadata, not media files. You get the media type, cover image URL, dimensions, and alt text — not a downloadable video file.
- Engagement counts are point-in-time, captured when the run happens.
Data Sources and Legality
All data returned by this Actor is publicly available information published on Threads. It returns only posts that account owners have chosen to make public — the same content visible to any member of the public viewing that profile. No private profiles, no logged-in-only content, no personal data behind an access control.
Your responsibility: You are responsible for complying with all applicable laws and regulations, and with the terms of service of any platform whose public data you collect. That includes GDPR, CCPA, and any local rules that apply to you when personal data is involved. Scraping publicly available data is generally lawful in many jurisdictions, but how you store, process, and use it is on you.
Practical guidance:
- Use collected post data for legitimate purposes such as research, analytics, and competitive intelligence
- Do not republish scraped content in ways that infringe the author's rights
- Honor removal requests from individuals whose public posts you have stored
- If in doubt about a specific use case, take legal advice — this section is information, not a legal guarantee
FAQ
Q: Can I search Threads for a keyword, hashtag, or topic? A: No. This Actor scrapes profile feeds only. You give it accounts; it gives you those accounts' posts. It cannot search Threads for mentions of a term.
Q: Can it scrape private profiles?
A: No. Only publicly visible profiles can be retrieved. A private account returns This profile could not be retrieved. Check that the username is correct and the profile is public, then retry. You were not charged.
Q: I got "This profile could not be retrieved" for an account I know exists. Why? A: Profile lookups are occasionally flaky — the same handle can fail on one attempt and succeed on the next. The Actor already retries several times with backoff before giving up, so this is uncommon, but it happens. First check the spelling and that the profile is public; if both are fine, just run that handle again.
Q: Am I charged for profiles that fail or return nothing? A: No. You are charged only for posts actually delivered to your dataset. Every error row says so explicitly.
Q: What input formats does usernames accept?
A: Bare handles (natgeo), @handles (@natgeo), and full profile URLs (https://www.threads.com/@natgeo, and threads.net URLs too). You can mix all three in one list.
Q: How many posts can I get from one profile?
A: Up to 500 per profile per run, set via maxPostsPerProfile. The default is 50. Live checks have paginated well past 80 posts on a single profile without failures.
Q: How fresh is the data? A: It reflects the profile's live public feed at the moment of the run — in our checks, the newest post in a feed was less than a day old. Engagement counts are equally a snapshot of that moment.
Q: Does it download images or videos? A: It returns media metadata — type, cover image URL, dimensions, carousel item count, and alt text. It does not return downloadable video files.
Q: How do I tell post rows apart from error rows in my code?
A: Post rows have no status field. Error rows always have status: "error". Check for the presence of status.
Q: Can I scrape several profiles in one run?
A: Yes — that is what usernames is for. Note that maxPostsPerProfile applies to each profile separately, and you are billed per post returned across all of them.
Q: What formats can I export? A: Anything the Apify dataset supports — JSON, CSV, Excel, XML — via the console or the Apify API.
Q: Can I run this on a schedule?
A: Yes. Use Apify's scheduling to run it daily or weekly and append to your own store, deduplicating on postId.
Contact & Support
Questions? Need help? We're here for you.
For questions, technical support, feature requests, or anything else about the Threads Posts Scraper, reach out:
Email: contact@endspec.net
Response Time: We respond within 24 hours on business days.
What to include in your message:
- A description of the question or issue
- Your Apify run ID, if applicable
- The input you used
- Any
errormessage orstatusvalue you received
You can also report issues through the Issues tab on this Actor's Apify page. We're committed to helping you get the most out of this Actor — whether you're integrating it into a pipeline, troubleshooting a run, or figuring out whether it fits your use case.
Related Actors
- Instagram Instant Media Scraper – scrape media and post data from public Instagram profiles.
- YouTube Instant Email Scraper – find publicly listed contact emails for YouTube channels.
- LinkedIn Instant Profile Search – search LinkedIn profiles by name, job title, or company.
Last updated: July 2026