# Luma Events Scraper - New Event Monitor for Cities & Calendars (`neverempty/luma-events-monitor`) Actor

Luma events (lu.ma / luma.com) by city, category or calendar, one row per event: name, link, start and end, venue, hosts, guest count, price, sold-out and approval flags. Monitoring mode remembers what was listed and returns only events that are new since the last run.

- **URL**: https://apify.com/neverempty/luma-events-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 event returneds

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

## Luma Events Scraper - New Event Monitor for Cities & Calendars

Luma events (lu.ma / luma.com) from **city pages, categories and calendars**, one row per event: name, event page, start and end time, venue, calendar, hosts, guest count, price, sold-out, spots remaining and whether approval is required.

- **Monitoring mode returns only new events.** Run the same input on a schedule: the first run remembers what is listed, every later run returns only the events that were not listed before. A run with nothing new returns one free row that says so.
- **Asked for X, got X.** A city, category or calendar that Luma does not have comes back as a `not-found` row. It is never silently replaced by another list.
- **An event found in several lists is one row**, charged once.
- No login, no cookies, no proxy. Only Luma's public event lists are read, one request at a time.

### What you can read

| You give | What is read |
|---|---|
| `cities: ["sf"]` | The upcoming events Luma features on that city's page (80 events for San Francisco on 2026-10-05). Luma listed 87 cities on that day: `sf`, `nyc`, `la`, `london`, `paris`, `berlin`, `tokyo`, `seoul`, `dubai`, `toronto` and so on |
| `cities: ["sf"], categories: ["ai"]` | The events of that category **around** that city, a wider list than the city page (AI around San Francisco: more than 300 events on 2026-10-05, including Palo Alto, Menlo Park and San Jose). Categories: `ai`, `tech`, `crypto`, `arts`, `climate`, `fitness`, `wellness`, `food`, `family`, `books`, `games`, `running` |
| `calendars: ["https://lu.ma/your-calendar"]` | The upcoming events of that calendar (an organizer's page). Its short name or its id (`cal-...`) also work |

Several cities, categories and calendars can be given in one run. Each city, each city and category pair, and each calendar is read as its own list; lists are added together, not narrowed against each other. A category always needs a city, because Luma lists category events around a place.

### Input

- **`cities`** - Luma city names as used in the address (`sf`, `nyc`, `tokyo`), a city name (`San Francisco`) or the lu.ma address. No default: a run reads only what you give.
- **`categories`** - optional Luma categories; read around each city given. Needs `cities`.
- **`calendars`** - calendar addresses (`https://lu.ma/your-calendar`), short names or ids (`cal-...`).
- **`maxEvents`** - upper limit on event rows when monitoring is off (default 200).
- **`monitoringMode`** - return only events that were not listed at earlier runs.
- **`resetMonitoringState`** - forget what monitoring remembers and start again.

### What you get

| Column | Meaning |
|---|---|
| `name`, `url`, `eventId` | Event name, its page on luma.com, and Luma's event id |
| `startAt`, `endAt`, `timezone` | Start and end (ISO 8601, UTC) and the event's own time zone name |
| `city`, `region`, `country`, `countryCode` | Where the event is, as given by Luma |
| `address`, `fullAddress`, `addressMode` | Street address when the host shows it publicly (`addressMode: "shown"`). Many hosts show the address to guests only (`"obfuscated"`, 38 of 80 on the San Francisco page): then only the city is given and `address` is `null` |
| `latitude`, `longitude`, `locationType` | As given by Luma |
| `calendarName`, `calendarUrl`, `calendarId`, `calendarIsPersonal` | The calendar the event belongs to. Events on a personal calendar have `calendarIsPersonal: true` and usually no `calendarUrl` |
| `hosts`, `hostNames` | Host names as shown on the event page, each with the address of its Luma profile page. No email, phone, social handle or photo is collected |
| `guestCount`, `guestListShown` | Number of guests **when the host shows the guest list**. When the list is hidden Luma reports 0, which is not a real count, so `guestCount` is `null` (48 of 80 on the San Francisco page) |
| `isFree`, `price`, `priceMax`, `currency`, `priceIsFlexible` | Lowest ticket price and, when given, the highest, in the event's currency (`27` = 27.00 USD; `7500` = 7,500 JPY). `null` when Luma gives no price |
| `isSoldOut`, `spotsRemaining`, `isNearCapacity`, `registrationAvailability`, `waitlistActive` | Availability as Luma reports it (`open`, `waitlist`, `sold-out`). `spotsRemaining` is `null` when Luma gives no number |
| `requiresApproval` | `true` when the host approves each registration |
| `coverImageUrl` | The event's cover image |
| `foundIn` | The lists of this run the event was found in (city, category, calendar) |
| `change` | Monitoring runs only: `new-event` |
| `status`, `scrapedAt` | `ok` for an event row; when the run delivered it |

A value Luma does not give is `null`, never an empty string or a made-up 0. The event description, the guest list and ticket types are not read.

### Monitoring mode

Turn on `monitoringMode` and schedule the same input (hourly, daily).

1. **First run of a list**: the events listed now are remembered. No event rows, nothing charged, one `baseline-saved` row per list.
2. **Later runs**: only events that were not listed before are returned (`change: "new-event"`).
3. **Nothing new**: one free `no-new-events` row, and one small monitoring check is charged for each list that was read and had nothing new.

Details that matter:

- What is remembered is kept per list (per city, per city and category, per calendar), so adding a calendar to a scheduled input does not reset the others; the added list starts with its own first run.
- An event already returned from one list is not returned again when it later shows up in another list of the same run.
- Monitoring ignores `maxEvents`: every new event is returned. If the maximum total charge of a run is too small for all of them, the ones that did not fit are not remembered and come in the next run.
- Luma's lists are ordered by start time, not by when an event was added, so each monitoring run reads the whole list (up to 40 pages of 50 events).
- If a list could not be read, nothing about it is charged or changed.
- Do not schedule two runs of the same lists at the same moment: both may return the same new event.
- `resetMonitoringState` forgets everything for all lists.

### What is charged

- **Event returned**: one per event row.
- **Monitoring check**: monitoring mode only, one per list that was read and had no new event.
- Not charged: the first monitoring read of a list, rows that say a list was not found or could not be read, `no-events`, `no-new-events`, limit and budget rows. There is no start fee.

### Rows that are not events

Every run says what happened. These rows have a `status` other than `ok`, a `note` in plain English, and are free:

`not-found` (Luma has no such city, category or calendar) · `invalid-input` · `feed-unreadable` / `feed-blocked` (the list could not be read; the HTTP status is in `httpStatus`) · `no-events` (Luma lists nothing upcoming there) · `baseline-saved` · `no-new-events` · `limit-reached` · `budget-reached` · `not-read`.

### Limits

- With monitoring off, `maxEvents` (default 200, at most 5,000) limits the rows; the earliest events by start time are returned.
- One list is read up to 40 pages (2,000 events). One run reads at most 30 lists.
- Only upcoming events are listed by Luma on these pages. Past events are not read.
- City pages are Luma's own selection of events for that city, not every event held there. Use a category with the city for a wider list.
- Category lists are the events Luma places around the city's coordinates; how far "around" reaches is decided by Luma.
- Requests are sent one at a time with more than a second between them. A run of one city takes a few seconds; a category around a large city can take about a minute.
- This Actor is not affiliated with Luma.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `cities` (type: `array`):

Luma city pages to read, by the name Luma uses in the address: sf, nyc, la, london, paris, berlin, tokyo, seoul and so on (lu.ma/sf → sf). A city name such as "San Francisco" or the full lu.ma address also works. Each city is one list of the upcoming events Luma features for it. A name Luma does not list comes back as a free "not-found" row with the names it does list; no other city is used in its place.

## `categories` (type: `array`):

Optional. Luma categories: ai, tech, crypto, arts, climate, fitness, wellness, food, family, books, games, running. With a category, the run reads the events of that category around each city given (a wider list than the city page) instead of the city page itself. Several cities and categories are read pair by pair and are not narrowed against each other. A category always needs a city: Luma lists category events around a place.

## `calendars` (type: `array`):

Luma calendars to read: the calendar's lu.ma address (https://lu.ma/your-calendar), its short name, or its id (cal-...). Each calendar is one list of its upcoming events. An address that is not a calendar comes back as a free "not-found" row.

## `maxEvents` (type: `integer`):

Upper limit on event rows when monitoring is off (earliest start time first). Reading stops once enough events have been read. Monitoring mode ignores this number and returns every new event.

## `monitoringMode` (type: `boolean`):

Run the same input on a schedule. The first run of a list only remembers the events listed now (no rows, no charge). Every later run returns only events that were not listed before. A run that finds nothing new returns a free "no-new-events" row and is charged one small monitoring check per list.

## `resetMonitoringState` (type: `boolean`):

Clears everything monitoring mode remembers for every list before this run, so each list starts again with a first read.

## Actor input object example

```json
{
  "cities": [
    "sf"
  ],
  "maxEvents": 200,
  "monitoringMode": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per Luma event read from the chosen cities, categories and calendars: name, event page, start and end time, venue and city, calendar, hosts, guest count, price, sold-out, spots remaining and approval flags. An event found in several lists is one row. Lists that could not be read or were not found, runs with no new event, and runs that hit a limit come back as their own rows and are not charged.

# 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 = {
    "cities": [
        "sf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/luma-events-monitor").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 = { "cities": ["sf"] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/luma-events-monitor").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 '{
  "cities": [
    "sf"
  ]
}' |
apify call neverempty/luma-events-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/luma-events-monitor"
        }
    }
}
```

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/IERzw5z8qH1gF6SOi/builds/aULqovvMhCFwjQpRO/openapi.json
