The Mine Works
Browse on Apify
Threads Has No Public API: Here Is How to Get Profile and Post Data Anyway
← All posts
tutorial June 22, 2026 · 7 min read Updated June 22, 2026

Threads Has No Public API: Here Is How to Get Profile and Post Data Anyway

Meta has not released a public Threads API. Here is what the data looks like, what fields are available via scraping, and how to collect it without getting blocked.

Try the scraper

The actor referenced in this article is live on Apify. Pay only for results delivered.

Open on Apify →

When Meta launched Threads in July 2023, it grew to 100 million users in under a week. That is a significant audience for brand monitoring, competitive research, and social listening. There is one problem: Meta has not released a public Threads API.

The Instagram Graph API does not cover Threads. The oEmbed endpoint covers only individual public posts you already have URLs for, which is not useful for discovery. The Meta Developer platform has no Threads-specific permission scope. If you want structured Threads data at scale, you need a different approach.

TL;DR: Threads has no public API as of mid-2026. Profile data and post content are publicly accessible via the web interface. A scraper navigates the public profile pages, extracts structured data from the embedded JSON, and paginates through posts without requiring authentication. Fields include post text, like counts, reply counts, timestamps, and profile metadata.

Why Meta Has Not Released a Threads API

The short answer is product maturity. Threads launched fast, built on Instagram’s infrastructure, and the engineering team has been iterating on core features rather than developer tooling. Meta’s public statement has been that API access is planned but not yet available.

There is also a regulatory dimension. The EU’s Digital Markets Act requires large platforms to open APIs for interoperability, and Meta is navigating how Threads fits into that framework alongside its existing platforms. This is a meaningful constraint. Until the regulatory picture settles, a formal API is less likely.

The ActivityPub integration (Threads supports the fediverse protocol) gives you some signal for public profiles that opt in, but it is incomplete and not useful for bulk data collection.

What Is Publicly Accessible

Threads public profiles are accessible without authentication. For any public account, you can view:

  • Profile information: username, display name, bio, follower count, following count, verification status
  • Posts: text content, post type (thread, reply, repost), timestamps, like count, reply count, repost count
  • Media attachments: image URLs and video thumbnails (not the video files themselves)
  • Reply threads: the chain of replies in a thread conversation

What is not accessible without a logged-in session:

  • Private accounts (obviously)
  • Direct messages
  • Notifications
  • Draft content
  • Historical data beyond what the web UI surfaces (typically the most recent several hundred posts)

The data that is accessible covers most brand monitoring and competitive intelligence use cases. If you want to track what a brand is saying, how their audience is engaging, and how their follower count is trending, the public profile data is sufficient.

How the Scraper Navigates Without Auth

The Threads web interface is a React application that loads profile and post data from internal GraphQL endpoints. These are not documented, not versioned for external use, and not stable across releases. However, the page HTML includes a __NEXT_DATA__ JSON blob or a script tag with structured data that the React app uses to render the initial page state.

The scraper works by:

  1. Fetching the public profile URL (https://www.threads.net/@username)
  2. Extracting the embedded JSON from the page source
  3. Parsing the profile metadata and the initial post batch
  4. Following the pagination cursor to load additional posts

The scraper does not log in. It does not touch the internal GraphQL endpoints directly (which are more fragile). Instead it works from the public HTML in the same way a search engine crawler would. This approach is slower than an authenticated API call but is stable against minor API changes because the page HTML rendering is separate from the internal endpoint structure.

Rate limiting is handled by spacing requests and respecting the server’s response behavior. Threads will slow-down or temporarily block IPs that make requests at machine speed without any gaps. Good practice means building in delays, rotating requests if you are doing bulk pulls, and not hammering a single profile repeatedly.

Fields Returned Per Post

Here is the data structure for a single Threads post as returned by the scraper:

{
  "post_id": "3391234567890123456",
  "username": "threadsindia",
  "display_name": "Threads India",
  "timestamp": "2026-06-18T10:23:14Z",
  "text": "The next generation of conversation starts here.",
  "post_type": "thread",
  "like_count": 1842,
  "reply_count": 203,
  "repost_count": 94,
  "has_media": true,
  "media_type": "image",
  "media_urls": [
    "https://scontent-bom1-1.cdninstagram.com/v/t51.2885-15/..."
  ],
  "permalink": "https://www.threads.net/@threadsindia/post/abc123",
  "is_verified": false,
  "is_reply": false,
  "reply_to_id": null,
  "quote_post_id": null
}

Profile-level fields:

{
  "username": "threadsindia",
  "display_name": "Threads India",
  "bio": "Official Threads account for India.",
  "follower_count": 284000,
  "following_count": 312,
  "post_count": 1840,
  "is_verified": false,
  "profile_pic_url": "https://scontent-bom1-1.cdninstagram.com/..."
}

Python Example: Fetch a Profile

The managed Threads scraper handles the extraction and pagination. You call it through the Apify API:

import requests
import time

APIFY_TOKEN = "your_apify_token"

def fetch_threads_profile(username):
    """Fetch profile metadata and recent posts for a Threads username."""
    
    # Start the actor run
    run_url = f"https://api.apify.com/v2/acts/themineworks~threads-scraper/runs"
    
    payload = {
        "usernames": [username],
        "maxPostsPerProfile": 50,
        "includeReplies": False,
    }
    
    resp = requests.post(
        run_url,
        json=payload,
        params={"token": APIFY_TOKEN}
    )
    resp.raise_for_status()
    run_id = resp.json()["data"]["id"]
    
    # Wait for the run to finish
    status_url = f"https://api.apify.com/v2/actor-runs/{run_id}"
    while True:
        status_resp = requests.get(status_url, params={"token": APIFY_TOKEN})
        status = status_resp.json()["data"]["status"]
        if status in ("SUCCEEDED", "FAILED", "TIMED-OUT", "ABORTED"):
            break
        time.sleep(3)
    
    if status != "SUCCEEDED":
        raise RuntimeError(f"Scraper run ended with status: {status}")
    
    # Retrieve results
    dataset_id = status_resp.json()["data"]["defaultDatasetId"]
    results_url = f"https://api.apify.com/v2/datasets/{dataset_id}/items"
    results = requests.get(results_url, params={"token": APIFY_TOKEN}).json()
    
    return results

profile_data = fetch_threads_profile("zuck")
print(f"Fetched {len(profile_data)} posts")
for post in profile_data[:3]:
    print(f"[{post['timestamp']}] {post['like_count']} likes: {post['text'][:80]}")

Python Example: Pull Posts with Pagination

If you need more than the default batch and want to handle pagination yourself, you can increase maxPostsPerProfile or run the scraper in a loop across multiple accounts:

def bulk_monitor_accounts(usernames, posts_per_account=100):
    """Pull posts for a list of Threads accounts and return flat list."""
    
    run_url = f"https://api.apify.com/v2/acts/themineworks~threads-scraper/runs"
    
    payload = {
        "usernames": usernames,
        "maxPostsPerProfile": posts_per_account,
        "includeReplies": False,
        "scrapeProfile": True,
    }
    
    resp = requests.post(
        run_url,
        json=payload,
        params={"token": APIFY_TOKEN}
    )
    resp.raise_for_status()
    run_id = resp.json()["data"]["id"]
    
    # Poll until done
    status_url = f"https://api.apify.com/v2/actor-runs/{run_id}"
    while True:
        r = requests.get(status_url, params={"token": APIFY_TOKEN}).json()
        if r["data"]["status"] in ("SUCCEEDED", "FAILED"):
            break
        time.sleep(5)
    
    dataset_id = r["data"]["defaultDatasetId"]
    items_url = f"https://api.apify.com/v2/datasets/{dataset_id}/items"
    
    all_posts = []
    offset = 0
    limit = 100
    
    while True:
        page = requests.get(
            items_url,
            params={"token": APIFY_TOKEN, "offset": offset, "limit": limit}
        ).json()
        
        if not page:
            break
        
        all_posts.extend(page)
        
        if len(page) < limit:
            break
        
        offset += limit
    
    return all_posts

# Monitor a competitor set
competitor_accounts = ["brandaccount1", "brandaccount2", "brandaccount3"]
posts = bulk_monitor_accounts(competitor_accounts, posts_per_account=200)

# Basic engagement analysis
import statistics
like_counts = [p["like_count"] for p in posts if "like_count" in p]
print(f"Total posts collected: {len(posts)}")
print(f"Median likes: {statistics.median(like_counts):.0f}")
print(f"Max likes: {max(like_counts)}")

Use Cases

Brand monitoring. Track mentions of your brand name or product across public Threads accounts. Because Threads has no keyword search API, the practical approach is monitoring a known list of accounts rather than keyword-based discovery. For discovery, cross-reference with Instagram and build the account list manually.

Social listening for competitive analysis. Pull posts from competitor accounts and analyze engagement patterns. Which content types (text-only vs. image) drive more replies? What posting cadence correlates with higher engagement? This analysis is straightforward once you have structured post data.

Content research for brand accounts. If you run a Threads presence, looking at what posts from similar accounts perform well gives you real data for content planning. Scraping 500 posts from five competitor accounts takes a few minutes; doing it manually takes hours.

Influencer vetting. Before a paid partnership, pull actual engagement numbers from a creator’s Threads account. Reported follower counts are easy to inflate. The ratio of follower count to actual reply and repost counts on recent posts is a useful signal for authentic reach.

Academic research. Threads is a significant venue for public discourse. Researchers studying social platform dynamics, misinformation spread, or public communication patterns need structured data. The scraper produces data in the format academic pipelines expect.

Rate Limits and Responsible Scraping

There is no official rate limit because there is no official API. The practical limits come from Threads’ anti-bot infrastructure, which is similar to Instagram’s (they share infrastructure).

Sensible limits for sustained use: 200 to 500 posts per minute across all requests, with randomized delays between profile fetches (2 to 5 seconds). For bulk historical pulls of a single account, going faster than 100 posts per 30 seconds is where you start seeing increased error rates.

The scraper is built with these limits in mind. It does not exceed what a fast human user would do, which is the threshold that keeps it stable over time.

If you are building a scheduled monitoring workflow, running the scraper once every few hours over a watchlist of accounts is stable indefinitely. Running it at maximum speed continuously against the same accounts will eventually result in IP-level throttling.

Meta’s terms of service allow personal, non-commercial use of public Threads data. Commercial use of scraped data is a grey area that depends on what you are doing with it. This is worth reviewing against your specific use case before building production workflows around it.

Related Actor

Try the scraper referenced in this article — live on Apify, pay only for results.

Open threads-scraper on Apify →