# Thryft Scraper - Singapore Used Books, Vinyl & Prices (`lofomachines/thryft-scraper`) Actor

Scrape the Thryft Singapore catalogue: pre-loved books and vinyl with prices in SGD, condition, copies in stock, genres, images and links.

- **URL**: https://apify.com/lofomachines/thryft-scraper.md
- **Developed by:** [Lofomachines](https://apify.com/lofomachines) (community)
- **Categories:** E-commerce, Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Thryft Scraper – Singapore Used Books, Vinyl & Prices

**Turn Thryft, Singapore's pre-loved books and vinyl shop, into a clean dataset: every listing with its price in SGD, condition, how many copies are left, genres, cover image and a direct link.**

Built for speed above everything: the catalogue is read in parallel waves and the run stops the moment it has what you asked for. **Measured at up to 765 listings per second.**

No login. No API key. No cookies.

***

### Speed

| Run | Result | Time |
| --- | --- | --- |
| 400 in-stock listings | 400 records | **~20 s** |
| 1,000 in-stock listings | 1,000 records | **~25 s** |
| 150 vinyl records | 150 records | **~21 s** |

Two things make it fast:

- **Parallel waves.** Pages are independent, so 64 are requested at once instead of walking them one by one. Throughput measured against the live shop: 8 in flight → 126 listings/s, 32 → 341, 64 → **730+**.
- **Filtering while reading.** Keywords, format, price and stock are judged as each listing arrives, so a run stops as soon as enough listings qualify rather than pulling the whole shop first.

Catalogue order is therefore the fastest setting. Any other ordering has to read the whole shop before it can be trusted, and the Actor says so in the log when you pick one.

***

### What you get for every listing

**Identity** — title, format (print book, book, vinyl record, audiobook, clothing), shop handle, direct link, vendor

**Price** — cheapest buyable copy in SGD, highest copy price, original price and discount percentage where the shop shows one

**Condition and stock** — the condition of the copy you would actually buy (`Like New`, `Very Good`, `Good`, `Well Read`), every condition currently offered, how many copies are available, how many the listing has ever held

**Every copy** — each physical copy with its own condition, price, SKU and whether it is still available

**Classification** — readable genres, subjects, authors and artists, separated from the shop's internal stock references

**Selling signals** — `sellsFast`, `isPreloved`, `isOnline`

**Media** — cover image and every other image

**Lifecycle** — when the listing was published, created and last changed

***

### Who it is for

| Role | What they get |
| --- | --- |
| **Book resellers & flippers** | Underpriced titles by condition, and what stock is moving |
| **Collectors** | The whole vinyl or first-edition catalogue sorted by price |
| **Price comparison** | Every title with condition-level pricing in SGD |
| **Second-hand market analysts** | Condition mix, price bands and genre depth of a real Singapore thrift catalogue |
| **Librarians & educators** | Cheap titles by subject, with availability |
| **Bargain hunters** | Biggest discounts and items flagged as likely to sell fast |
| **Inventory monitoring** | A dated snapshot to diff week over week for new arrivals and sell-throughs |

***

### Use cases

**1. Find underpriced books by condition**
Sort by price low to high and filter to `Like New` or `Very Good` in the dataset. Anything cheap in good condition is worth a look.

**2. Track new arrivals**
Sort by newest listings and run it weekly. Diff `productId` against last week to get a clean feed of what just landed.

**3. Pull the whole vinyl catalogue**
Filter the format to vinyl records and export every record with price, condition and cover art.

**4. Hunt discounts**
Sort by biggest discount to surface everything marked down against its original price.

**5. Research an author or genre**
Search a name or a genre — matching runs across the title, description **and** genre labels, so you catch listings that do not name the author in the title.

**6. Watch sell-through**
`copiesAvailable` against `copiesTotal` shows how much of each listing has already sold.

**7. Build a price reference for a title**
Every copy is returned separately, so you see the full condition-to-price ladder for the same book.

**8. Spot what moves fast**
Filter on `sellsFast` to see what the shop itself expects to sell quickly.

***

### Input

Only what you actually need — everything technical is handled for you.

| Field | Description |
| --- | --- |
| **Search keywords** | Titles, authors, genres or artists. Matched against title, description, genres and format. |
| **Formats** | Keep only print books, vinyl records, audiobooks and so on. |
| **Collections** | Limit to shop collections by handle or name, for example `all-books` or `art-books`. |
| **Maximum price (SGD)** | Keep only items at or below this price. |
| **Only items in stock** | On by default — sold copies stay listed, so this is what keeps results buyable. |
| **Maximum items** | Upper limit of listings returned. |
| **Sort results by** | Catalogue order is fastest; other orders read the whole shop. |

```json
{
  "searchTerms": ["murakami"],
  "formats": ["Print Books"],
  "maxPriceSgd": 20,
  "inStockOnly": true,
  "maxItems": 1000,
  "sortBy": "relevance"
}
```

***

### Output

One clean row per listing. Abridged below — the real row carries **33 fields**.

```json
{
  "productId": 10738510659865,
  "title": "Theology of the Body for Beginners",
  "url": "https://thryft.asia/products/theology-of-the-body-for-beginners-1",
  "format": "Print Books",

  "priceSgd": 10.9,
  "priceMaxSgd": 13.9,
  "compareAtPriceSgd": null,
  "discountPercent": null,
  "currency": "SGD",

  "inStock": true,
  "condition": "Very Good",
  "conditions": ["Very Good", "Well Read"],
  "copiesAvailable": 1,
  "copiesTotal": 4,
  "copies": [
    {
      "variantId": 52826260177177,
      "label": "Like New",
      "sku": "X67276",
      "priceSgd": 13.9,
      "available": false
    }
  ],
  "skus": ["X67276"],

  "description": "A clear introduction to the theology of the body...",
  "genres": ["Catholic", "Christian Living", "Philosophy", "Religion", "Theology", "Christopher West"],
  "genreCount": 14,
  "internalTags": ["BOX.WHJ.081225", "Preloved", "Online"],

  "sellsFast": false,
  "isPreloved": true,

  "imageCount": 1,
  "primaryImageUrl": "https://cdn.shopify.com/s/files/...jpg",

  "listedAt": "2026-07-14T00:32:06.000Z",
  "updatedAt": "2026-07-29T10:01:52.000Z",
  "matchedTerm": null,
  "scrapedAt": "2026-07-29T10:05:00.000Z"
}
```

All money is in **Singapore dollars** and all timestamps are **ISO 8601**.

Export to **JSON, CSV, Excel, XML or HTML** in one click, or pull it from the API.

***

### What a run costs

Thryft refuses plain and datacentre connections, so reads go over the Apify residential proxy — and that traffic, not compute, is what a run costs. The Actor is built to read as few pages as it can:

| Run | Read | Residential traffic | Cost |
| --- | --- | --- | --- |
| 800 in-stock listings | 8 pages | **1.0 MB** | **~$0.013** |
| One collection (`art-books`) | 9 pages | ~1.1 MB | ~$0.014 |
| Keyword search across the shop | up to 100 pages | ~12 MB | ~$0.12 |

Three things keep it cheap:

- **Waves are sized to the request.** Asking for 800 listings reads 8 pages, not 64. Earlier versions read the same 16,000 products for every run — this alone made a typical run **5.8× cheaper**.
- **A named collection replaces the catalogue.** Filtering to a collection reads only that collection, so a targeted pull never pays for the rest of the shop.
- **The plain route is tried first.** It is checked once per run with a single tiny request; if Thryft ever accepts unproxied traffic, runs become nearly free automatically with no change on your side.

**To keep costs down:** prefer a collection filter or a modest **Maximum items** over a keyword search, and leave the sort on catalogue order. Keyword searches and any other sort order have to read the whole shop, which is where the cost is.

**Memory:** 1 GB is plenty and is what the Actor asks for. Raising it multiplies the compute charge without making the run faster, because the bottleneck is the network.

***

### Notes worth knowing

**Sold copies stay listed.** A thrift shop keeps a listing after its copies sell, and a large part of the catalogue is sold out. That is why **Only items in stock** is on by default — leave it on unless you specifically want sold history.

**Keyword searches read more of the shop.** The shop publishes no bulk search, so a keyword run scans the catalogue rather than querying an index. It still works, it simply reads further than a plain catalogue pull. If the shop stops serving pages part-way, the log says so instead of quietly returning a short list.

**This Actor uses the Apify residential proxy.** Thryft refuses direct and datacentre connections, so residential routing is required and enabled automatically. If your account has no residential proxy, the run stops immediately with a clear message rather than failing obscurely.

***

### Integrations & automation

- **Make (Integromat)** — run weekly and append new arrivals to a Google Sheet
- **n8n** — alert when a watched author or title appears in stock under your price
- **Zapier** — push discounted listings into Slack or Airtable
- **Google Sheets / Airtable** — build a live want-list tracker
- **API / webhooks** — call the Actor from your own stack and read the dataset endpoint
- **AI agents / MCP** — a documented output schema makes this easy to chain

**Popular workflow:** run a weekly catalogue pull sorted by newest, then diff `productId` to get new arrivals and diff `copiesAvailable` to see what sold.

***

### FAQ

**Do I need a Thryft account?**
No. The Actor reads only publicly listed catalogue data.

**How many listings are there?**
Tens of thousands, including sold-out history. In-stock items are a much smaller share, which is what the default returns.

**Why does the condition matter so much?**
It is a second-hand shop: the same title appears at several prices, one per copy condition. The Actor gives you the condition of the copy you would buy plus the full ladder in `copies`.

**Are prices per copy?**
Yes. `priceSgd` is the cheapest buyable copy; `copies` lists every copy with its own price and condition.

**What if I want sold-out listings too?**
Turn off **Only items in stock**. You then also get titles whose copies have all sold, useful for sell-through analysis.

**How fast is it really?**
Up to 765 listings per second measured against the live shop, and about 20–25 seconds for a typical run of a few hundred to a thousand listings.

***

### More tools by Lofomachines

- [Oddle Eats Scraper – Singapore Restaurants, Menus & Prices](https://apify.com/lofomachines/oddle-eats-scraper) — the same treatment for Singapore food delivery
- [Universal E-commerce Scraper](https://apify.com/lofomachines/universal-ecommerce-scraper) — any other online store
- [Vinted Scraper API – Cheapest Cross-Country Listings & Sellers](https://apify.com/lofomachines/vinted-scraper-api) — second-hand fashion resale data
- [eBay Deals Scraper by Country](https://apify.com/lofomachines/ebay-deals-country-scraper) — cross-check second-hand pricing against eBay
- [Pokémon Card & Sealed Box Market Intelligence](https://apify.com/lofomachines/pokemon-card-box-market-intelligence) — collectible pricing intelligence

Browse everything at [apify.com/lofomachines](https://apify.com/lofomachines).

***

*Keywords: Thryft scraper, thryft.asia, Singapore used books, second-hand books Singapore, pre-loved books Singapore, cheap books Singapore, vinyl records Singapore, used vinyl Singapore, book price comparison Singapore, thrift store data, second-hand marketplace scraper, book reseller data, Shopify catalogue scraper, Singapore book prices SGD, book condition data, out of print books Singapore, bookstore inventory data.*

# Actor input Schema

## `searchTerms` (type: `array`):

Titles, authors, genres or artists to look for — matched against the title, description, genres and format. Leave empty to take the catalogue as it comes.

## `formats` (type: `array`):

Keep only these formats. Leave empty for all.

## `collections` (type: `array`):

Limit to specific shop collections, by handle or name — for example all-books, art-books, albums-by-the-beatles.

## `maxPriceSgd` (type: `integer`):

Keep only items at or below this price. Leave empty for any price.

## `inStockOnly` (type: `boolean`):

On by default. Sold copies stay listed in the shop, so leaving this on is what keeps the results buyable.

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

Upper limit of listings returned.

## `sortBy` (type: `string`):

Catalogue order is the fastest, because the run can stop as soon as it has enough. Any other order needs the whole shop read first.

## Actor input object example

```json
{
  "searchTerms": [
    "murakami"
  ],
  "formats": [],
  "collections": [],
  "inStockOnly": true,
  "maxItems": 1000,
  "sortBy": "relevance"
}
```

# Actor output Schema

## `overview` (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 = {
    "searchTerms": [
        "murakami",
        "vinyl"
    ],
    "collections": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("lofomachines/thryft-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 = {
    "searchTerms": [
        "murakami",
        "vinyl",
    ],
    "collections": [],
}

# Run the Actor and wait for it to finish
run = client.actor("lofomachines/thryft-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 '{
  "searchTerms": [
    "murakami",
    "vinyl"
  ],
  "collections": []
}' |
apify call lofomachines/thryft-scraper --silent --output-dataset

```

## MCP server setup

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