# Lemon8 Scraper - Posts, Saves, Creators & Hashtags (`scrapewise/lemon8-scraper`) Actor

Scrape public Lemon8 posts by hashtag, keyword, creator or feed: caption, likes, saves, views, comments, images, video link and creator followers. Lemon8 data export to JSON, CSV or Excel, with a daily only-new mode. No login.

- **URL**: https://apify.com/scrapewise/lemon8-scraper.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (community)
- **Categories:** Social media
- **Stats:** 18 total users, 14 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.13 / 1,000 lemon8 post delivereds

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

Lemon8 Scraper exports **public Lemon8 posts** to JSON, CSV or Excel. Start from post URLs, a creator's profile, a keyword search, a hashtag or a category feed such as fashion or food. Each post comes with title, full text, hashtags, all images, the video link, likes, saves, comment count, views, publish date and the creator's follower count. It is meant for influencer agencies, brands, social media analysts and researchers working with Lemon8, ByteDance's lifestyle app used in the US, the UK, Japan and Southeast Asia.

**US$ 2.50 per 1,000 posts. No monthly fee, no start fee. Error rows are free.**

### What it fixes

- **Engagement on every mode.** Lemon8's lists show only likes. The Actor opens each post and adds saves, comment count, views, publish date, full text, every image, the video link and the creator's followers, so profile, hashtag, search and feed rows have the same fields as a single post URL.
- **Keyword search that does not stop at 60 posts.** Lemon8's web search shows 5 to 60 posts per keyword. The Actor continues with the hashtag of the same name and then with the searches Lemon8 suggests ("workout" -> "Workout outfits"), and marks those rows in `source`.
- **Full creator profiles.** Profile mode follows Lemon8's pagination until the profile ends or `maxItems` is reached. One test run returned all 218 posts the website lists for @lemon8_us.
- **Hashtag names that resolve.** Lemon8 keeps old empty topics under popular names; the Actor picks the hashtag id that real posts use.
- **Errors as rows, not failed runs.** A removed post, a missing profile or a keyword with no results comes back as one free row with an `errorCode`. Blocked pages are retried several times. On 2026-10-08, 30 of 30 cloud test runs (profiles, posts, keywords, hashtags, feeds, five regions, comments, only-new runs and other scrapers' input formats) ended with posts or the expected free error row.
- **Comments on request.** `includeComments` adds each post's comments at the same price per post.

### At a glance

| Posts | Cost |
|---|---|
| 30 (the default run) | US$ 0.075 |
| 1,000 | US$ 2.50 |
| 10,000 | US$ 25.00 |

One charge is one post row, whatever the mode, with details, creator stats, images and video links included. No start fee; error rows and duplicates inside a run are not charged. Apify compute is included in the price.

### Input

Every field is optional. An empty input returns 30 posts from the public profile `@lemon8_us`. The input form describes each field; this is what each kind of link returns:

| You paste | You get |
|---|---|
| `https://www.lemon8-app.com/@lemon8_us` or `@lemon8_us` | The creator's posts, newest first, up to `maxItems` |
| `https://www.lemon8-app.com/@lemon8_us/7618125167721710094` or the bare id | That one post |
| `https://www.lemon8-app.com/topic/skincare` or `#skincare` | Posts from the hashtag feed |
| `https://www.lemon8-app.com/discover/skincare-routine` | Keyword search for "skincare routine" |
| `https://www.lemon8-app.com/feed/fashion?region=jp` | The fashion feed of Lemon8 Japan |

Keywords and hashtags in one run:

```json
{
  "keywords": ["skincare routine", "easy recipes"],
  "hashtags": ["#fitinspo"],
  "region": "us",
  "maxItems": 50
}
```

A daily run with only new posts (save it as a Task and attach a Schedule):

```json
{ "urls": ["@lemon8_us"], "hashtags": ["skincare"], "maxItems": 30, "onlyNew": true }
```

With `onlyNew`, each run delivers and charges only posts not delivered before; profiles stop at the first page with a known post, so a quiet day costs nothing and ends with "No new posts since the last run". The memory is a Key-Value Store named `lemon8-scraper-only-new` in your account; delete it to start over.

### Switching from another Lemon8 actor? Your existing input works

Paste the JSON input you already have. These field names are read too:

| Your field | Read as |
|---|---|
| `mode` (`feed`, `discover`, `profile`, `post`) | Only the list that belongs to that mode is scraped |
| `keywords`, `searchTerm`, `searchTerms`, `searchQueries`, `query` | `keywords` |
| `profileUrls`, `profileUrl`, `username`, `usernames` | Profiles |
| `postUrls`, `startUrls` (strings or `{"url": ...}`) | `urls` |
| `feedCategories`, `categories` | `feedCategories` |
| `maxResults` | Cap for the whole run (`0` = 500) |
| `limit` | Posts per profile or keyword |
| `getDetails` | `includePostDetails`; `true` also returns comments |
| `incrementalMode` | `onlyNew` |
| `resumeFromRunId` | `resumeFromDataset` when it is a dataset ID |
| `proxy` | `proxyConfiguration` |

Fields with no meaning here (state keys, media downloads, dataset names) are ignored instead of failing the run. Example:

```json
{
  "mode": "profile",
  "profileUrls": ["https://www.lemon8-app.com/@lemon8_us"],
  "keywords": ["fashion outfits"],
  "maxResults": 20
}
```

This returns the 20 newest posts of @lemon8_us and nothing from the keyword.

### Output

One row per post. A real row from the default run on 2026-09-15, with long URLs shortened:

```json
{
  "type": "post",
  "id": "7618125167721710094",
  "groupId": "7618125167721710094",
  "url": "https://www.lemon8-app.com/@lemon8_us/7618125167721710094",
  "postType": "Gallery",
  "region": "us",
  "title": "Week 49 & 50 Winners 🛍️ 💐",
  "content": "Congrats to our Trending Hashtag Challenge winners for Week 49 & 50!\nWe've really enjoyed seeing all of your amazing submissions. 💛 ...",
  "hashtags": [],
  "images": ["https://p19-lemon8-sign-useast5.tiktokcdn-us.com/...webp", "..."],
  "imageCount": 7,
  "thumbnailUrl": "https://p16-lemon8-cross-sign.tiktokcdn-us.com/...webp",
  "videoUrl": null,
  "likes": 323,
  "saves": 44,
  "commentCount": 60,
  "views": 52052,
  "publishedAt": "2026-03-17T08:05:05Z",
  "authorUsername": "lemon8_us",
  "authorNickname": "Lemon8 US",
  "authorVerified": true,
  "authorFollowers": 336417,
  "authorFollowing": 18,
  "authorTotalLikes": 176802,
  "authorPostCount": 276,
  "detailsLoaded": true,
  "source": "profile:@lemon8_us",
  "error": null,
  "errorCode": null,
  "scrapedAt": "2026-09-15T20:22:30.400955+00:00"
}
```

#### Main fields

| Field | Description |
|---|---|
| `type` | `post`, or `error` for a row that could not be delivered |
| `id` / `groupId`, `url` | Lemon8 post id and public page |
| `postType` | `Gallery` (photo carousel), `Video` or `Article` |
| `title`, `content`, `shortContent` | Title, full text (line breaks kept) and list preview |
| `hashtags` | Hashtags used, without `#` |
| `images`, `imageCount`, `thumbnailUrl`, `videoUrl` | Media links (signed; they expire within hours) |
| `likes`, `saves`, `commentCount`, `views` | Public counters at run time |
| `comments` | With `includeComments`: `{id, text, likes, replyCount, createdAt, authorName}` |
| `publishedAt`, `publishTime`, `modifiedAt` | Publish time (ISO 8601 UTC and seconds) and last edit |
| `authorUsername`, `authorNickname`, `authorUrl`, `authorVerified` | Creator |
| `authorFollowers`, `authorFollowing`, `authorTotalLikes`, `authorPostCount` | Creator's public counters |
| `source` | Input that produced the row: `profile:@lemon8_us`, `hashtag:#skincare`, `search:easy recipes`, `search-hashtag:...`, `search-related:...`, `feed:fashion`, `post` |
| `error` / `errorCode` | Only on rows that could not be delivered |

The Console shows two views: **Posts** and **Creators**.

### Errors (never charged)

| errorCode | Meaning |
|---|---|
| `POST_NOT_FOUND` | The post was removed, made private or never existed |
| `PROFILE_NOT_FOUND` | No Lemon8 account with that handle |
| `HASHTAG_NOT_FOUND` | No hashtag with that name in the selected region; paste its `/topic/<id>` URL instead |
| `NO_RESULTS` | The keyword, hashtag, feed or profile has no public posts |
| `INVALID_URL` | Not a Lemon8 post, profile, hashtag, search or feed link |
| `BLOCKED` | Lemon8 blocked every retry for that page; run again |

The run fails only when nothing at all could be delivered because every retry was blocked.

### Use it from code and AI agents

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("scrapewise/lemon8-scraper").call(run_input={"hashtags": ["skincare"], "maxItems": 100})
for post in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(post["url"], post["likes"], post["saves"], post["authorFollowers"])
```

The same works from Node.js, the HTTP API (`run-sync-get-dataset-items`), n8n, Make and Zapier. For AI agents, add `scrapewise/lemon8-scraper` as a tool through the Apify MCP server (`https://mcp.apify.com`).

### Limitations

- **Keyword search is shallow on the web**; the fallback above reaches `maxItems` for most terms, but a few broad words (for example "books") have no search page, hashtag or suggestion and return `NO_RESULTS`, free.
- **Region matters** for search, hashtags and feeds. Posts and profiles open from any region.
- **Profiles list what the website lists.** The post counter can be higher than what Lemon8 serves (@lemon8_us shows 276 and lists 218).
- **Comments are minimal:** display name only, no commenter id or profile; replies come as a count. Creator bios are not collected.
- **Media links expire**; download files soon after the run.

### FAQ

**Do I need a Lemon8 account or cookies?** No. Only public pages are read, without login.

**Can I get every post of a creator?** Yes: set `maxItems` high, for example 5,000.

**How fast is it?** The default run of 30 posts with details takes under half a minute. Turning off `includePostDetails` is several times faster, without saves, comments, views and dates.

**The run was interrupted. Do I pay twice?** No. Pick its dataset in `resumeFromDataset`; posts already there are skipped and not charged.

**Is it legal?** It collects only what Lemon8 shows publicly without login and no personal data of ordinary users. Follow Lemon8's terms and the privacy laws that apply to you.

### Changelog

- **2026-10-08:** comments on request, keyword search continues with Lemon8's suggested searches, `resumeFromDataset`, and the input of other Lemon8 scrapers works as is.
- **2026-10-04:** `onlyNew` for scheduled runs.
- **2026-09-28:** hashtag names that Lemon8 only resolves outside the US (for example #travel) now return posts.

# Actor input Schema

## `urls` (type: `array`):

Lemon8 links, one per line. A profile (https://www.lemon8-app.com/@lemon8_us or just @lemon8_us) returns the creator's posts, newest first, up to 'Max items'. A post link or bare post id (7618125167721710094) returns that one post. Hashtag (/topic/skincare or #skincare), keyword (/discover/skincare-routine) and category feed (/feed/fashion) links work too; ?region=jp inside a link overrides 'Region' for that link.

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

Keywords to search on Lemon8, one per line, as you would type them in the app: skincare routine, easy recipes, summer outfits. Each keyword returns up to 'Max items' posts, in the market chosen in 'Region'. Rows are marked search:<keyword> in the source column.

## `hashtags` (type: `array`):

Hashtags, one per line, with or without #: fitinspo, #skincare. Each returns up to 'Max items' posts from the hashtag's public feed, popular first and then most recent. If a name comes back HASHTAG_NOT_FOUND (free), paste the hashtag's /topic/<id> link in 'Lemon8 URLs' instead.

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

Lemon8 category feeds, one per line: foryou, fashion, beauty, food, wellness, travel, home, pets, relationship, tech, career, reading, fitness. Each category returns up to 'Max items' posts from that feed in the selected region.

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

Lemon8 market used for keyword search, hashtags and category feeds: the same keyword returns different posts in the US and in Japan, and a Japanese keyword in the US market returns few or none. Default: United States. Post and profile links open from any region.

## `maxItems` (type: `integer`):

Maximum posts per profile, keyword, hashtag and category, counted separately for each one. Single post links are always returned. Each delivered post costs US$ 0.0025, so 30 (default) costs US$ 0.075 per source. Set it high (5,000) to read a creator's whole history.

## `onlyNew` (type: `boolean`):

For scheduled runs. Remembers which posts of each profile, hashtag, keyword and category were already delivered (Key-Value Store 'lemon8-scraper-only-new' in your account) and skips them next time: skipped posts are free and do not count against 'Max items'. Profiles stop at the first page with a known post, so a quiet day costs nothing. Post links are always returned. Delete the store to start over. Default off.

## `includePostDetails` (type: `boolean`):

On (default): opens each post to add publish date, views, saves, comment count, full text, every image, the video link and the creator's followers. Slightly slower, same price. Off: a faster list with title, cover, likes and creator only, useful when you need only post ids and links.

## `searchHashtagFallback` (type: `boolean`):

Lemon8's web search shows only 5 to 60 posts per keyword. On (default): a keyword that runs out continues with the hashtag of the same name ('easy recipes' -> #easyrecipes) and then with searches Lemon8 suggests ('workout' -> 'Workout outfits'), until 'Max items'. Those rows have source search-hashtag: or search-related:. Off: only the keyword's own search page.

## `includeComments` (type: `boolean`):

Adds a 'comments' list to each post: text, likes, reply count, date and the commenter's display name, in the order Lemon8 shows them. No commenter id or profile is collected. Same price per post; comments are not charged separately, but the run takes longer (about one extra request per 2 comments). Default off.

## `maxCommentsPerPost` (type: `integer`):

How many comments to read per post when 'Include comments' is on, from 1 to 500. Empty = 20. Higher values make the run slower but do not change the price.

## `resumeFromDataset` (type: `string`):

Optional. Pick the dataset of an earlier run, or paste its ID. Posts already in it are skipped: not returned and not charged. Use it to continue a large pull that was interrupted, with the same input.

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

The Apify datacenter proxy (default) works for Lemon8 and is included in the price. Blocked requests are retried. Change only to route through your own proxy.

## Actor input object example

```json
{
  "urls": [
    "https://www.lemon8-app.com/@lemon8_us"
  ],
  "region": "us",
  "maxItems": 30,
  "onlyNew": false,
  "includePostDetails": true,
  "searchHashtagFallback": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `resultsCsv` (type: `string`):

No description

# 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 = {
    "urls": [
        "https://www.lemon8-app.com/@lemon8_us"
    ],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/lemon8-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 = {
    "urls": ["https://www.lemon8-app.com/@lemon8_us"],
    "maxItems": 30,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/lemon8-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 '{
  "urls": [
    "https://www.lemon8-app.com/@lemon8_us"
  ],
  "maxItems": 30,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapewise/lemon8-scraper --silent --output-dataset

```

## MCP server setup

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