# iFoodie (愛食記) Taiwan Restaurants Scraper (`ardent_fork/ifoodie-taiwan-restaurants`) Actor

Restaurant listings from ifoodie.tw, Taiwan's largest restaurant review site: name, user and editorial rating, average price (TWD), price level, opening hours, address, coordinates, cuisine tags, phone, website, signature dishes. Filter by city, district, keyword and sort. No login, no API key.

- **URL**: https://apify.com/ardent\_fork/ifoodie-taiwan-restaurants.md
- **Developed by:** [KF P](https://apify.com/ardent_fork) (community)
- **Categories:** Travel, Lead generation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 restaurants

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## iFoodie (愛食記) Taiwan Restaurants Scraper

Extracts restaurant listings from [ifoodie.tw](https://ifoodie.tw) (愛食記), a major Taiwanese restaurant review and ranking site (≈15,000 restaurants in Taipei alone, every city and county covered). Filter by city, district, keyword (cuisine, dish or restaurant name) and sort order, and get one clean JSON record per restaurant.

No login, no API key, no proxy needed. Pay-per-event: **one `restaurant` event per restaurant saved**. List pages and restaurant pages that are fetched but not saved cost nothing.

### What you get

Each record (list data, always present):

| Field | Example |
|---|---|
| `id`, `url` | `559d314fc03a103ee86c4e16`, `https://ifoodie.tw/restaurant/559d314fc03a103ee86c4e16-松滿樓` |
| `name`, `storeName` | `松滿樓` |
| `city`, `district`, `address`, `postalCode` | `台北市`, `大安區`, `臺北市大安區濟南路三段26號`, `106` |
| `lat`, `lng` | `25.040058`, `121.5349414` |
| `phone`, `website`, `bookingUrl`, `menuUrl` | `0227518479`, `http://www.smlts.com.tw/`, OpenTable / inline links |
| `rating`, `ifoodieRating`, `ratingDistribution` | user rating `4` (0–5), editorial score `3.2`, `{ "1": 0, …, "5": 4 }` |
| `reviewCnt`, `blogCnt` | `1942`, `7` |
| `avgPrice`, `priceLevel` | `670` (TWD per person), `2` (1 cheap … 4 expensive) |
| `categories` | `["中式料理", "合菜", "午餐", "晚餐"]` |
| `openNow`, `openingHours`, `openingHoursList` | `false`, `今日營業: 11:30-14:00, 17:30-20:50`, one line per weekday. `openNow` and the `現正營業:` / `今日營業:` prefix reflect the moment of the scrape (Taiwan time). |
| `coverUrl`, `summary`, `intro`, `description` | cover photo, one-line tagline, short and long editorial blurbs |
| `lastVisit`, `scrapedAt` | ISO timestamps |

With **Fetch restaurant pages** on (default), each record also has: `facilities` (包廂, online booking…), `notes` (service charge, cards, reservation advice, minimum spend), `dishes` (signature dishes), `priceRange`, `priceCategory`, `suitableGroups`, `mealTypes`, `landmarks` (nearest MRT station etc.), `businessDistricts`, `tags`, `portionSize`, `environment` (ambience description).

### Input

Example: ramen in Da'an, best-rated first —

```json
{ "city": "台北市", "district": "大安區", "keyword": "拉麵", "sortby": "rating", "maxItems": 100 }
```

| Field | Meaning |
|---|---|
| `city` (required) | City or county as ifoodie spells it: 台北市, 新北市, 桃園市, 台中市, 台南市, 高雄市, 基隆市, 新竹市, 新竹縣, 宜蘭縣, 花蓮縣, 台東縣, 屏東縣, 嘉義市, 彰化縣, 南投縣, 苗栗縣, 雲林縣, 澎湖縣, 金門縣. An unknown city fails the run immediately (nothing is charged). |
| `district` | Optional district, e.g. 大安區, 信義區. A misspelt district is not an error on ifoodie's side: the run ends with zero restaurants and a warning in the log. |
| `keyword` | Optional Chinese keyword: cuisine (拉麵, 火鍋, 燒肉), meal (早午餐, 宵夜), venue (咖啡廳) or a restaurant name. |
| `sortby` | `default` (ifoodie's own ranking: by rating on a plain city list, by recent activity on keyword lists), `recent` (recently reviewed), `popular` (most visited) or `rating`. |
| `maxItems` | Stop after this many restaurants. Default 100. |
| `fetchDetails` | Open each restaurant page for the extra fields (default on). Off = list data only, ~15× fewer requests. |
| `maxConcurrency` | Parallel fetches, default 5. |
| `proxyConfiguration` | Optional; not needed for ifoodie.tw. |

#### Reach and limits

- ifoodie shows at most **67 pages × 15 = ~1000 restaurants per query**. To cover a whole city, run once per district (台北市 has 12) or per keyword; the actor de-duplicates within a run, so combine outputs across runs by `id`.
- Sorting by `recent` / `popular` (and `default` on keyword lists, which resolves to `recent`) is not perfectly stable between page requests; a few restaurants may shift pages and be skipped on deep runs. `rating` is stable.
- Text is Traditional Chinese as published on the site. `address` uses the official 臺北市 spelling while `city` uses ifoodie's 台北市.

### Pricing

Pay-per-event with a single event, `restaurant`, charged once per restaurant pushed to the dataset. If your run's **maximum total charge** cannot pay for all of `maxItems`, the actor stops at the budget and says so in the status message; nothing is ever charged beyond it. A run that finds no restaurants (bad keyword, empty district) costs nothing.

### Data source and terms

Only public pages of ifoodie.tw are read, at a modest rate (5 parallel requests by default). Reviews, user profiles and blogger check-ins are deliberately **not** extracted — only the restaurant's own business data and ifoodie's editorial summaries. Please respect ifoodie's terms of use and Taiwan's Personal Data Protection Act when you use the data.

# Actor input Schema

## `city` (type: `string`):

City or county exactly as ifoodie spells it, e.g. 台北市, 新北市, 桃園市, 台中市, 台南市, 高雄市, 基隆市, 新竹市, 新竹縣, 宜蘭縣, 花蓮縣, 台東縣, 屏東縣, 嘉義市, 彰化縣, 南投縣, 苗栗縣, 雲林縣, 澎湖縣, 金門縣. An unknown city fails the run.

## `district` (type: `string`):

Optional district within the city, as ifoodie spells it (e.g. 大安區, 信義區, 中山區 for 台北市). Leave empty for the whole city.

## `keyword` (type: `string`):

Optional cuisine, dish or restaurant-name keyword in Chinese (e.g. 拉麵, 火鍋, 早午餐, 咖啡廳, 燒肉). Leave empty for every restaurant in the city.

## `sortby` (type: `string`):

Order of the results. "default" is ifoodie's own ranking (by rating on a plain city list, by recent activity on keyword lists); "recent" = recently reviewed; "popular" = most visited; "rating" = highest rated (the most stable order for deep runs).

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

Stop after this many restaurants have been saved. Each list page holds 15 and ifoodie shows at most ~1000 results (67 pages) per query — use district or keyword filters to reach more restaurants.

## `fetchDetails` (type: `boolean`):

Open every restaurant page for the extra fields: facilities, notes (service charge, cards, reservations, minimum spend), signature dishes, price range, suitable groups, meal types, nearby landmarks, tags, ambience. Turn off to get only the list data (rating, price, hours, address, coordinates, phone, website) — about 15× fewer requests.

## `maxConcurrency` (type: `integer`):

Parallel page fetches.

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

Optional. ifoodie.tw serves these pages without a proxy; use one only if your IP is blocked.

## Actor input object example

```json
{
  "city": "台北市",
  "district": "大安區",
  "keyword": "拉麵",
  "sortby": "default",
  "maxItems": 300,
  "fetchDetails": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `restaurants` (type: `string`):

Dataset items, one per ifoodie.tw restaurant (name, rating, average price, opening hours, address, coordinates, categories, phone, website).

# 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 = {
    "city": "台北市"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ardent_fork/ifoodie-taiwan-restaurants").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 = { "city": "台北市" }

# Run the Actor and wait for it to finish
run = client.actor("ardent_fork/ifoodie-taiwan-restaurants").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 '{
  "city": "台北市"
}' |
apify call ardent_fork/ifoodie-taiwan-restaurants --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ardent_fork/ifoodie-taiwan-restaurants"
        }
    }
}

```

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/avVnagmc5HURU9TbQ/builds/NvkqY21BnO3nwRssX/openapi.json
