# Lemon8 Search Scraper - Posts, Comments & Media (`abotapi/lemon8-discover-scraper`) Actor

Scrape Lemon8 posts by keyword across multiple regions. Extract posts, images, videos, comments, engagement metrics, and detailed post analytics, with support for high-quality media downloads. Ideal for market research, content analysis, and trend monitoring.

- **URL**: https://apify.com/abotapi/lemon8-discover-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Social media, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 103 total users, 8 monthly users, 88.5% runs succeeded, 4 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Lemon8 Scraper: Discovery Posts, Engagement, Comments & Media Links

Lemon8 Scraper turns Lemon8's keyword discovery feed into structured data. Search any keyword and region, or paste discovery links directly, and get post text, images, video links, author profiles, engagement counts, hashtags and full comment threads with nested replies. Export the dataset as JSON, CSV or Excel, or read it straight through the API.

### Why This Scraper?

- **Five ways to target content.** Search by keyword and region, paste discovery or post links, crawl home-feed categories, or scrape an author's profile posts; each link's keyword and region are parsed automatically.
- **Full post detail on demand.** Turn on Get Detailed Post Data for untruncated post text, hashtags, related topics, edit history and real engagement counts.
- **Complete comment threads.** Expand every comment and its replies for a post, with a running total of comments and replies collected.
- **12 regional storefronts.** United States, Australia, New Zealand, Japan, Thailand, Indonesia, Vietnam, Malaysia, Singapore, Canada, United Kingdom and Philippines (the site may serve little or nothing for some regions).
- **Built for schedules.** Incremental mode returns only new, updated and reappeared posts on recurring runs, and a run that could not read the discovery page fails loudly instead of quietly returning an empty dataset.
- **Resume large pulls.** Continue an interrupted run from its run or dataset ID to pick up where it left off.
- **Send results onward.** Pipe scraped posts into Notion, Linear, Airtable or Apify through MCP connectors.

### Use Cases

- **Content and trend research:** track which topics, hashtags and post formats are performing on Lemon8 right now.
- **Creator and influencer analysis:** monitor a creator's posting cadence, engagement and comment sentiment over time.
- **Brand and competitor monitoring:** watch how a product, campaign or hashtag is discussed across regions.
- **Regional market research:** compare topic popularity for lifestyle, beauty or fashion content across storefronts.
- **Dataset building:** collect posts, images and comment threads for archives, reports or model training data.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `id` | `"7412987407534162437"` |
| `title` | `"Weekend Outfit Ideas"` |
| `content` | `"A quick roundup of what I wore this weekend..."` (preview, truncated) |
| `postUrl` | `"https://www.lemon8-app.com/@sample_creator/7412987407534162437?region=us"` |
| `author.name` | `"Sample Creator"` |
| `author.profileUrl` | `"https://www.lemon8-app.com/@sample_creator?region=us"` |
| `author.profileImageUrl` | avatar image URL |
| `author.followerCount` | `null` in search and feed rows (the listing card does not expose it); the real count in profile and post rows |
| `statistics.likesCount` | `"482"` |
| `statistics.savedCount` / `statistics.commentsCount` | `"0"` in search, feed and profile rows; the real counts are in `details.statistics` (see FAQ). Post-mode rows carry the real counts here too |
| `images` | array of `{ src, alt }` image URLs |
| `comments` | `[]`, reserved preview slot (see FAQ) |
| `isVideo` | `true` or `false` |
| `videoData` | poster image and type for video posts; the playable file appears under `details.videoData` |
| `searchQuery` | `"fashion"` |
| `details.fullContent` | untruncated post text, when Get Details is on |
| `details.hashtags` | `["#fashion", "#ootd", "#weekendstyle"]` |
| `details.editTime` | `"9/28"` (month/day last edited) |
| `details.statistics` | real `savedCount` / `likesCount` / `commentsCount`, when Get Details is on |
| `details.galleryImages` | full resolution images, when Get Details is on |
| `details.relatedTopics` | `["outfit ideas", "fall fashion"]` |
| `allComments` | full comment thread with nested `replies`, when Get Details is on |
| `commentStats` | `totalComments`, `totalReplies` and `previewCommentCount` collected for the post |

In incremental mode, every record also carries `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt` (see How to Use).

#### Output views

The dataset has eight ready-made views in the Console: **Posts Overview**, **Post Images**, **Video Posts**, **Content Creators**, **Post Comments**, **Detailed Post Content**, **Engagement Analytics** and **Incremental Changes** (the change type and changed fields of each record, filled in incremental mode).

### How to Use

1. Pick how to target Lemon8: fill in **Search Term** and **Region**, or paste one or more links into **Start URLs** (Start URLs win when both are set).
2. Set **Post Limit**, and turn on **Get Detailed Post Data** for full text, hashtags and comments, or leave it off for a faster shallow pass over the discovery feed.
3. If you will run this actor on a schedule against the same search, turn on **Incremental mode** so later runs return only new and changed posts; otherwise leave that section alone.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search by keyword and region:**

```json
{
  "searchTerm": "skincare",
  "region": "us",
  "limit": 20,
  "getDetails": true
}
```

**Paste a discovery link instead:**

```json
{
  "searchTerm": "fashion",
  "startUrls": ["https://www.lemon8-app.com/discover/coffee?region=jp"],
  "limit": 15
}
```

**Recurring monitoring of the same search:**

```json
{
  "searchTerm": "streetwear",
  "region": "sg",
  "limit": 50,
  "getDetails": false,
  "incrementalMode": true,
  "stateKey": "streetwear-sg-daily"
}
```

**Resume an interrupted run:**

```json
{
  "searchTerm": "travel",
  "region": "au",
  "limit": 200,
  "resumeFromRunId": "abcDEF123xyz"
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/lemon8-discover-scraper").call(run_input={"searchTerm": "fashion", "region": "us", "limit": 20})
for post in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(post["title"], post["statistics"]["likesCount"])
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/lemon8-discover-scraper').call({ searchTerm: 'fashion', region: 'us', limit: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID to pick up where it left off.
- **Incremental mode** (`incrementalMode`) is for scheduling this actor against the same search. State is tracked by `stateKey`, or an automatic key derived from Search Term or Start URLs, Region and Get Detailed Post Data, so unrelated searches never share state. Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED`, or `EXPIRED` (only after a run that scanned every tracked target to its natural end, and only with `emitExpired`). A post is `UPDATED` when any of its fields changes (title, text, hashtags, like and comment counts, comments, media); a re-signed or re-sharded image or video CDN address for the same file is never a change. The nested `author.followerCount` is excluded from the comparison by default (the flat `authorFollowers` is still compared). List fields in `ignoreFieldsForChanges` (names or dot paths such as `statistics.likesCount`) to exclude them from the comparison and from `changedFields`; they stay in the output, and changing the list does not re-flag posts you already track. With incremental mode off, output is exactly as before.

#### URL mode: supported link shapes

`startUrls` is pre-filled with five real examples (four search pages and one post page); profile pages are also accepted. In Scrape mode `auto` (and the other non-URL modes) the untouched example list is treated as "not supplied", so API calls and scheduled runs that never set `startUrls` keep searching by Search Term exactly as before. With Scrape mode `url` the list is always scraped as given, including the five examples. Change, add or remove any URL and URL mode is used instead of Search Term.

| Shape | Example | Result |
| --- | --- | --- |
| Discover search with region | `https://www.lemon8-app.com/discover/fashion?region=us` | Posts for the keyword in that region |
| Experience search with region | `https://www.lemon8-app.com/experience/coffee?region=us` | Posts for the keyword on the experience page |
| Discover search, no region | `https://www.lemon8-app.com/discover/nails` | Uses the Region input (default US) |
| Multi-word keyword, other region | `https://www.lemon8-app.com/discover/skin%20care?region=jp` | Keyword "skin care" in Japan |
| Author profile page | `https://www.lemon8-app.com/@janeddove?region=us` | The author's posts, up to Post Limit (scraped as a profile target) |
| Single post page | `https://www.lemon8-app.com/@janeddove/7460605475378283050?region=us` | One post record with details and comments |

A `?page=N` query parameter is ignored without a warning: the site serves page 1 whatever the value, so there is no URL pagination and page 1 of the keyword is scraped. Any other link (for example `/hashtag/...`, `/search?q=...` or a bare `/discover`) is skipped with a per-URL warning in the log.

#### Modes, feeds and profiles

Pick what to scrape with **Scrape mode** (default `auto`, the classic behaviour: Start URLs when edited, otherwise Search Term).

| Mode | Input used | Notes |
|---|---|---|
| `search` | Keywords (several allowed) or Search Term | Post Limit applies per keyword. Several keywords are scraped one after another. |
| `url` | Start URLs | Discovery, post and profile URLs. |
| `feed` | Feed categories (foryou, food, beauty, fashion, home, fitness, health, relationship, travel, photography, pet, reading, cars, outdoors) | Home-feed posts per category, paged up to Post Limit. |
| `profile` | Profile URLs (or @names) | An author's posts, paged up to Post Limit. |
| `post` | Post URLs | One full record per post. |

Region now also accepts `uk` and `ph` (the site may serve little or nothing for a region; an empty region returns no rows, so no per-result charge; the per-run start fee still applies).

Every row also carries flat attribute fields next to the nested ones: `groupId`, `url`, `articleClass`, `likes`, `saves`, `commentCount`, `authorUserId`, `authorUsername`, `authorNickname`, `authorAvatar`, `authorFollowers`, `videoUrl`, `thumbnailUrl`, `hashtags`, `topComments`, `keyword`, `category`, `region`, `sourceMode`, `scrapedAt`. Saves and comment counts are filled when post details are on. `authorFollowers` is `null` in search and feed rows, like `author.followerCount`, and filled in profile and post rows.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `auto` | `auto`, `search`, `url`, `feed`, `profile` or `post` (see Modes, feeds and profiles). |
| `keywords` | array | (none) | Several keywords for `search` mode; Post Limit applies per keyword. |
| `feedCategories` | array | (none) | Home-feed categories for `feed` mode. |
| `profileUrls` | array | (none) | Author profile URLs or @names for `profile` mode. |
| `postUrls` | array | (none) | Single post URLs for `post` mode. |
| `searchTerm` | string | `fashion` | Keyword to search for on Lemon8. Used only when Start URLs is empty. |
| `startUrls` | array | the five example URLs from the shape table | Lemon8 search pages and post pages to scrape instead of Search Term. Takes precedence over Search Term once you edit the list; in Scrape mode `auto` the untouched example list is ignored so a run that omits this field still searches by Search Term (mode `url` scrapes it as given). |
| `region` | string | `us` | Target region: `us`, `au`, `nz`, `jp`, `th`, `id`, `vn`, `my`, `sg`, `ca`, `uk` or `ph`. |
| `limit` | integer | `2` | Maximum number of posts to extract per target (1 to 5000). |
| `getDetails` | boolean | `true` | Navigate to each post's page for full content, hashtags, related topics and all comments. |
| `detailsLimit` | integer | `2` | Maximum number of posts, out of Post Limit, to fetch detailed data for (1 to 3000). |
| `commentExpansionTimeout` | integer | `30` | Seconds to spend expanding one post's comments before moving on (10 to 300). |
| `resumeFromRunId` | string | (none) | Continue one interrupted run or dataset, picking up where it left off. |
| `incrementalMode` | boolean | `false` | Turn on for recurring runs: classify each post as `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED` or `EXPIRED` against the previous run. |
| `stateKey` | string | (none) | Name or share an incremental-mode monitoring campaign; otherwise a key is derived automatically from the search scope. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) `UNCHANGED` posts every run. |
| `emitExpired` | boolean | `false` | Also return (and bill) posts no longer found, once a run fully scans every tracked target. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only: field names or dot paths excluded from change detection (kept in the output). |
| `saveImages` | boolean | `false` | Reserved; has no effect in the current version. Image links are always returned in `images` (and `details.galleryImages` when Get Detailed Post Data is on). |
| `saveVideos` | boolean | `false` | Reserved; has no effect in the current version. The playable video link is returned in `details.videoData.videoUrl` when Get Detailed Post Data is on. |
| `proxy` | object | prefill: Apify Proxy (US) | Connection settings. Defaults to the Apify datacenter pool (US). Left completely empty (for example, via a bare API call), the actor connects directly. |
| `dev_transform_fields` | array | `[]` | Return only the listed fields per post. Use a dot path for nested objects and a numeric index for arrays. |
| `dev_dataset_name` | string | `default` | Save results into a custom named dataset instead of the default one. Supports `{ACTOR}`, `{DATE}` and `{TIME}` masks. |
| `dev_dataset_clear` | boolean | `false` | Clear the target dataset before this run inserts new items. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each post to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which post pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "id": "7412987407534162437",
  "author": {
    "name": "Sample Creator",
    "followerCount": null,
    "profileUrl": "https://www.lemon8-app.com/@sample_creator?region=us",
    "profileImageUrl": "https://p16-sign-va.tiktokcdn.com/sample-avatar.jpg"
  },
  "title": "Weekend Outfit Ideas",
  "content": "A quick roundup of what I wore this weekend, from brunch to a night out...",
  "postUrl": "https://www.lemon8-app.com/@sample_creator/7412987407534162437?region=us",
  "statistics": {
    "savedCount": "0",
    "likesCount": "482",
    "commentsCount": "0"
  },
  "images": [
    { "src": "https://p16-sign-va.tiktokcdn.com/sample-image-1.jpg", "alt": "" },
    { "src": "https://p16-sign-va.tiktokcdn.com/sample-image-2.jpg", "alt": "" }
  ],
  "comments": [],
  "isVideo": false,
  "videoData": null,
  "searchQuery": "fashion",
  "details": {
    "title": "Weekend Outfit Ideas",
    "fullContent": "A quick roundup of what I wore this weekend, from brunch to a night out. Full untruncated post text goes here.",
    "hashtags": ["#fashion", "#ootd", "#weekendstyle"],
    "editTime": "9/28",
    "statistics": {
      "savedCount": "312",
      "likesCount": "482",
      "commentsCount": "37"
    },
    "galleryImages": [
      { "src": "https://p16-sign-va.tiktokcdn.com/sample-image-1.jpg", "alt": "" },
      { "src": "https://p16-sign-va.tiktokcdn.com/sample-image-2.jpg", "alt": "" }
    ],
    "searchQuery": null,
    "relatedTopics": ["outfit ideas", "fall fashion"],
    "isVideo": false,
    "videoData": null
  },
  "allComments": [
    {
      "author": "Sample Commenter",
      "authorTag": null,
      "content": "Love this look!",
      "time": "9/27",
      "likes": "12",
      "replies": [
        { "author": "Sample Creator", "content": "Thank you!", "time": "9/27", "tag": null }
      ]
    }
  ],
  "commentStats": {
    "totalComments": 37,
    "totalReplies": 9,
    "previewCommentCount": 0
  }
}
```

### Plan Requirement

The default connection setting (Apify datacenter proxy, US exit) is enough for Lemon8's discovery, experience and post pages; no residential proxy is required. This was measured from a hosting-provider (datacenter-class) address rather than through Apify's own proxy pool: the pages loaded with HTTP 200, and the only refusal seen was a single HTTP 403 on a repeated author-profile fetch, so profile mode may be less stable than the other modes.

An Apify free plan does not include Apify Proxy at all. On a free plan the run logs "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." and tries a backup connection, which may not load every page. If nothing loads, the run fails with "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Lemon8 pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again." On a paid plan whose proxy access is refused outright, the failure reads "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required". If the pages simply fail to load you get "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." A paid plan or your own proxy URLs avoid all of these.

### FAQ

#### How much does it cost?

You pay per record returned plus a small start fee per run. On the Apify Free plan that is $0.003 per result and $0.15 per run start (about $3.15 per 1,000 results); other plans have their own rates on the **Pricing** tab, which is the authority. Posts suppressed as unchanged in incremental mode are not returned or billed. Use **Post Limit** and **Details Post Limit** to cap the cost of any run.

#### Is it legal to scrape Lemon8?

This actor collects only publicly available discovery-feed data. You are responsible for how you use it: follow Lemon8's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Post text and counts are generally facts, but images, video and profile photos may be subject to third-party rights.

#### Is there a cap on comments per post?

No fixed count. With Get Detailed Post Data on, the actor expands the comment thread and replies for up to **Details Post Limit** posts, spending at most **Comment Expansion Timeout** seconds per post; a very large thread can be cut short by that timeout.

#### Can I get only new or changed posts on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, updated and reappeared posts, and unchanged ones are not returned or billed unless you turn on **Emit unchanged**.

#### Why do a post's saved and comment counts show 0?

The discovery feed itself only reports a like count, so `statistics.savedCount` and `statistics.commentsCount` are `"0"` at the top level of search, feed and profile rows (post-mode rows already carry the real counts there). Turn on **Get Detailed Post Data** and the real saved, like and comment counts appear in `details.statistics` for each detailed post (up to **Details Post Limit**).

#### Why did my run fail instead of returning an empty dataset?

If Start URLs is set but none of its entries is a recognizable Lemon8 discovery link, or if every request to the discovery page was refused or timed out, the run fails with a specific, actionable message and no dataset item is billed (the per-run start fee still applies). A search or region that genuinely has no matching posts is a different case: the run still succeeds, and simply returns nothing.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more Lemon8 data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>📱 <a href="https://apify.com/abotapi/lemon8-feeds-scraper"><b>Lemon8 Feed Scraper</b></a><br>Scrape Lemon8 feeds across 22 categories and 10+ regions. Extract posts, images, videos...</td><td>📱 <a href="https://apify.com/abotapi/lemon8-profile-scraper"><b>Lemon8 Profile Scraper</b></a><br>Scrape Lemon8 user profiles with automated multi-profile discovery. Extract profile data...</td></tr>
<tr><td>📱 <a href="https://apify.com/abotapi/lemon8-media-scraper"><b>Lemon8 Media Scraper</b></a><br>Lightweight Lemon8 scraper for extracting image and video URLs from posts, with optional...</td><td>📱 <a href="https://apify.com/abotapi/truthsocial-com-scraper"><b>Truth Social Scraper</b></a><br>Scrape public Truth Social profiles, posts, media, metrics, author data, and complete...</td></tr>
<tr><td>📱 <a href="https://apify.com/abotapi/reddit-scraper"><b>Reddit⚡ Lightning</b></a><br>From $1/1K. Scrape Reddit posts, comments, and media from any subreddit or user profile -...</td><td>🛒 <a href="https://apify.com/abotapi/lazada-scraper"><b>Lazada Scraper</b></a><br>Scrape Lazada products and reviews across SEA markets, including Malaysia, Singapore...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/lemon8-discover-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `searchTerm` (type: `string`):

Keyword to search for on Lemon8. Used only when Start URLs is empty.

## `startUrls` (type: `array`):

Lemon8 URLs to scrape: discovery/experience search pages (/discover/<keyword>, /experience/<keyword>, with or without ?region=) and single post pages (/@<author>/<postId>). When edited, these take precedence over Search Term. In Scrape mode "auto" the untouched example list below is ignored (treated as not supplied) so runs that omit this field keep searching by Search Term; Scrape mode "url" scrapes it as given. Author profile pages (/@<author>) are scraped as profile targets.

## `mode` (type: `string`):

What to scrape. "auto" (default) keeps the classic behaviour: Start URLs when edited, otherwise Search Term / Keywords. "search" = keyword search, "url" = Start URLs only, "feed" = home-feed categories, "profile" = author profile posts, "post" = single post pages.

## `keywords` (type: `array`):

Search several keywords in one run (each one is searched in the selected Region; Post Limit applies per keyword). Empty = use Search Term.

## `feedCategories` (type: `array`):

Home-feed categories to scrape when Scrape mode is "feed" (empty = foryou). Post Limit applies per category.

## `profileUrls` (type: `array`):

Lemon8 author profile pages (https://www.lemon8-app.com/@name) or @names. Used when Scrape mode is "profile" (or "auto"). Post Limit applies per profile.

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

Single Lemon8 post pages (https://www.lemon8-app.com/@name/<postId>). Used when Scrape mode is "post" (or "auto").

## `region` (type: `string`):

Target region for Lemon8 content

## `limit` (type: `integer`):

Maximum number of posts to extract (max 500)

## `getDetails` (type: `boolean`):

Navigate to individual post pages to extract full content and all comments

## `detailsLimit` (type: `integer`):

Maximum number of posts to get detailed data for

## `commentExpansionTimeout` (type: `integer`):

Maximum time in seconds to spend expanding comments on each post. Posts with many comments may timeout.

## `saveImages` (type: `boolean`):

Download post images locally

## `saveVideos` (type: `boolean`):

Download videos from video posts

## `proxy` (type: `object`):

Proxy for requests. Defaults to Apify datacenter (US); if the datacenter tunnel is unavailable or gets blocked, the actor automatically falls back to a backup proxy pool.

## `dev_transform_fields` (type: `array`):

Transform the resulting output. Select only needed fields. For nested object use DOT (e.g., 'statistics.rating'). For nested array use NUMBER as array index (e.g., 'comments.0.text').

## `dev_dataset_name` (type: `string`):

Save results into custom named Dataset. Use masks to customize: {ACTOR} = actor name, {DATE} = date (YYYYMMDD), {TIME} = time (HHMMSS). Example: 'lemon8-{DATE}' will create 'lemon8-20250519'.

## `dev_dataset_clear` (type: `boolean`):

Clear Dataset before inserting new data

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large pull of posts without returning or charging for posts already collected there. Use this after an interrupted run, or when continuing a search pull in another run. For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching posts as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED posts. Turn on "Emit unchanged" or "Emit expired" only when you also want those posts returned (and billed). A post counts as UPDATED when any of its fields changes (title, text, hashtags, like/comment counts, comments, media); a re-signed or re-sharded image/video CDN address of the same file never does. Fields listed in Ignore fields are excluded from the comparison but stay in the output. State is kept separately for each search term/Start URL, region, and Get Detailed Post Data setting; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search term/Start URLs, region, and Get Detailed Post Data setting — different search setups then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return posts that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return posts that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned every tracked search term/Start URL — not when Post Limit capped it, or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Incremental mode only. Field names (or dot paths such as details.videoData.videoUrl; arrays are searched element by element) that never make a post UPDATED and never appear in changedFields. The fields stay in the output. Use it for values you do not want to trigger an update, for example statistics.likesCount or commentStats. Changing this list does not re-flag your existing posts.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "searchTerm": "fashion",
  "startUrls": [
    "https://www.lemon8-app.com/discover/fashion?region=us",
    "https://www.lemon8-app.com/experience/coffee?region=us",
    "https://www.lemon8-app.com/discover/nails",
    "https://www.lemon8-app.com/discover/skin%20care?region=jp",
    "https://www.lemon8-app.com/@janeddove/7460605475378283050?region=us"
  ],
  "mode": "auto",
  "region": "us",
  "limit": 2,
  "getDetails": true,
  "detailsLimit": 2,
  "commentExpansionTimeout": 30,
  "saveImages": false,
  "saveVideos": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  },
  "dev_transform_fields": [],
  "dev_dataset_name": "default",
  "dev_dataset_clear": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

Main discovery post records with author, URL, search query, media type, and engagement counts.

## `images` (type: `string`):

Image-focused view for scraped Lemon8 discovery posts.

## `videos` (type: `string`):

Video discovery posts and video metadata where available.

## `authors` (type: `string`):

Author profile fields collected from discovery post records.

## `comments` (type: `string`):

Comments and expanded comment data when detail scraping is enabled.

## `detailedContent` (type: `string`):

Full post content, hashtags, related topics, and detail fields.

## `engagement` (type: `string`):

Saved, likes, comments, and detailed engagement fields.

## `changes` (type: `string`):

Incremental mode only: change type (NEW, UPDATED, UNCHANGED, REAPPEARED, EXPIRED) and changed fields per post.

## `completeOutput` (type: `string`):

Post-level output stored in the default key-value store.

## `metadata` (type: `string`):

Run metadata with search count, post count, and scraping summary fields.

# 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 = {
    "searchTerm": "fashion",
    "startUrls": [
        "https://www.lemon8-app.com/discover/fashion?region=us",
        "https://www.lemon8-app.com/experience/coffee?region=us",
        "https://www.lemon8-app.com/discover/nails",
        "https://www.lemon8-app.com/discover/skin%20care?region=jp",
        "https://www.lemon8-app.com/@janeddove/7460605475378283050?region=us"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/lemon8-discover-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 = {
    "searchTerm": "fashion",
    "startUrls": [
        "https://www.lemon8-app.com/discover/fashion?region=us",
        "https://www.lemon8-app.com/experience/coffee?region=us",
        "https://www.lemon8-app.com/discover/nails",
        "https://www.lemon8-app.com/discover/skin%20care?region=jp",
        "https://www.lemon8-app.com/@janeddove/7460605475378283050?region=us",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/lemon8-discover-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 '{
  "searchTerm": "fashion",
  "startUrls": [
    "https://www.lemon8-app.com/discover/fashion?region=us",
    "https://www.lemon8-app.com/experience/coffee?region=us",
    "https://www.lemon8-app.com/discover/nails",
    "https://www.lemon8-app.com/discover/skin%20care?region=jp",
    "https://www.lemon8-app.com/@janeddove/7460605475378283050?region=us"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/lemon8-discover-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/lemon8-discover-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/A3ugHq174iKd7kG4F/builds/6MpVw2lIaOPkBAjHu/openapi.json
