# Luma Events Scraper - by City, Calendar or URL (lu.ma) (`jpopendata/luma-events`) Actor

Luma (lu.ma) event scraper: public events by city, organiser calendar or pasted URL with start/end and time zone, city, online/in-person, price range, registration status and guest count. Filters: keyword, dates, category. Event facts only, no host/guest data. Unofficial; not affiliated with Luma.

- **URL**: https://apify.com/jpopendata/luma-events.md
- **Developed by:** [JP Open Data](https://apify.com/jpopendata) (community)
- **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.

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 - by City, Calendar or URL (lu.ma)

**Public events on Luma (luma.com / lu.ma) as clean JSON: title, start and end with time zone, city, online or in person, price range and currency, registration status (open / waitlist / sold out), guest count when the page shows it, and the organising calendar (organisations only).**

Pick Luma cities (San Francisco, New York, London, Tokyo, Berlin, ... or `all`), paste event or organisation-calendar URLs, and filter by keyword, date range or Luma's own categories (AI, Tech, Crypto, Arts, ...). Optionally open each event page for Luma's categories and more fields.

> **Unofficial tool. Not affiliated with or endorsed by Luma Labs, Inc.** It reads publicly visible event pages only (no login), politely (strictly serial requests at least 1.5 s apart, a hard per-run request budget, no challenge solving, an honest User-Agent, one IP per run), checks every URL against luma.com/robots.txt, and never calls Luma's internal APIs. It extracts **event facts only — no host or guest names, profiles, social handles, street addresses, descriptions or images.** You are responsible for making sure your use of the data complies with Luma's terms and applicable law.

***

### Quick start — verified input

Copy this into the input (JSON tab) or send it via the API. Running with an empty input `{}` uses exactly these values.

```json
{
  "cities": ["sf", "nyc"],
  "maxItems": 40
}
```

It was run successfully on Apify on 2026-10-13 with the default residential proxy: two city pages, 2 requests, 40 upcoming events in a few seconds.

More examples:

```json
{ "cities": ["london", "berlin", "paris"], "keyword": "AI", "startDate": "today", "endDate": "+14d" }
```

```json
{ "urls": ["https://lu.ma/your-event", "https://luma.com/your-community-calendar"], "includeDetails": true }
```

```json
{ "cities": ["sf"], "categories": ["ai", "crypto"] }
```

#### Common input mistakes

| Wrong | Right | Why |
|---|---|---|
| `"cities": ["California"]` or `["Japan"]` | `"cities": ["sf", "la"]` or `["tokyo", "osaka"]` | Luma organises events by city pages, not states or countries. Use a continent (`europe`, `na`, `apac`, `sa`, `africa`) or `all` for many cities. |
| `"cities": ["San Fransisco"]` | `"cities": ["San Francisco"]` or `["sf"]` | City names and slugs both work (case and accents don't matter); typos stop the run with the list of valid cities. |
| `"urls": ["https://luma.com/user/someone"]` | an event or organisation calendar URL | User profiles and personal calendars are personal; this Actor does not read them. |
| `"urls": ["https://luma.com/ai"]` (a category page) | `"cities": ["sf"], "categories": ["ai"]` | Category pages list calendars, not events, in their HTML. Categories work as a filter on city or calendar events. |
| `"startDate": "10/20/2026"` | `"startDate": "2026-10-20"` (or `"today"`, `"+7d"`) | Dates are YYYY-MM-DD and compared with each event's local start date. |
| `"keyword": "hackathon"` expecting all of Luma | combine it with `"cities": ["all"]` | The keyword filters the events listed on the pages you choose; it is not a site-wide search. |

An invalid value stops the run immediately, before any request to Luma, with a message that names the field and lists the valid values. Numbers outside their range are clamped with a warning instead.

#### Empty results?

A run that finds nothing finishes with 0 items and `complete: true` — that is not an error. Typical causes: a keyword or date range that excludes every listed event, a `categories` filter (Luma tags only some events with categories), an event that is private, members-only or deleted ("Page Not Found"), or a personal calendar. The `RUN_SUMMARY` record in the key-value store shows, for every page read, how many events were on the page, filtered out, duplicates or not public.

***

### Who is this for?

- **Event and community teams:** see what is happening in your city or niche this week, with prices and how full events are.
- **Sales, partnerships and sponsorship research:** find active organisations and communities (organiser calendars) by city and topic.
- **Newsletters, calendars and AI agents:** feed a weekly digest or an events database with clean, time-zone-correct records.
- **Market research:** compare event volume, pricing and sell-outs across cities.

### Input

| Field | Default | Notes |
|---|---|---|
| `cities` | `["sf", "nyc"]` | Luma city slugs or names (`"San Francisco"`, `"nyc"`, `"london"`, `"tokyo"`, ...), `"all"`, or a continent: `europe`, `na`, `apac`, `sa`, `africa`. A city page URL works too. |
| `urls` | empty | Event pages (`https://luma.com/abc123de`, `https://lu.ma/abc123de`), organisation or community calendars (`https://luma.com/<calendar>`), or city pages. When URLs are given and `cities` still holds the pre-filled list, only the URLs are read. |
| `categories` | empty | Filter on Luma's categories: `ai`, `tech`, `crypto`, `arts`, `climate`, `fitness`, `wellness`, `running`, `food`, `books`, `games`, `family`. Opens each event page (slower). |
| `keyword` | empty | Every word must appear in the title, organiser calendar or venue (not case- or accent-sensitive). |
| `startDate` / `endDate` | empty | Inclusive local dates: `YYYY-MM-DD`, `today`, `tomorrow` or `+7d`. |
| `includeDetails` | `false` | Also read every event page: Luma's categories, and for calendar events the price, registration status, guest count, city and organiser. One extra request per event. |
| `includeExternalEvents` | `true` | Calendars can list events hosted on other websites; they come with `platform: "external"`, their own URL, dates and city. |
| `maxItems` | `40` | Events for the whole run (1 to 10,000). |
| `maxRequests` | `100` | Hard budget of page requests to luma.com (1 to 2,000): one per city or URL page, plus one per event page when event pages are opened. |
| `proxyConfiguration` | Apify RESIDENTIAL, country US | luma.com sometimes rate-limits Apify datacenter IPs (HTTP 429 "Rate Limit Hit"), so the default uses Apify's residential proxy. One sticky session (one IP) is kept for the whole run; only if the very first request is refused is it retried once on a fresh session. Never rotation. `{"useApifyProxy": false}` runs without a proxy. |

### Sample output (one dataset item)

```json
{
  "eventId": "evt-HTpZiOi5P6x0UNh",
  "title": "#play14 Meetup Tokyo @TiB #2",
  "url": "https://luma.com/8qr1dgtq",
  "platform": "luma",
  "startAt": "2026-10-13T09:00:00.000Z",
  "endAt": "2026-10-13T12:00:00.000Z",
  "startLocal": "2026-10-13T18:00:00+09:00",
  "endLocal": "2026-10-13T21:00:00+09:00",
  "timezone": "Asia/Tokyo",
  "durationMinutes": 180,
  "attendanceMode": "in-person",
  "city": "Chiyoda City",
  "region": "Tokyo",
  "country": "Japan",
  "countryCode": "JP",
  "venueName": "Tokyo Innovation Base",
  "addressVisibility": "public",
  "isFree": true,
  "priceMin": 0,
  "priceMax": 0,
  "currency": null,
  "priceIsFlexible": false,
  "registrationStatus": "waitlist",
  "isSoldOut": true,
  "isNearCapacity": false,
  "requiresApproval": false,
  "guestCount": 20,
  "categories": null,
  "calendarTags": null,
  "calendarName": "#play14 Japan",
  "calendarUrl": "https://luma.com/play14japan",
  "calendarId": "cal-H7vdyw2cvBzYmP5",
  "featuredCity": "tokyo",
  "sourceType": "city",
  "sourceValue": "tokyo",
  "detailsFetched": false,
  "source": "Luma (luma.com) public event pages",
  "sourceUrl": "https://luma.com/",
  "license": "Publicly available data — unofficial tool; users are responsible for compliance with the source site's terms",
  "retrievedAt": "2026-10-13T02:00:00.000Z"
}
```

Field notes:

- `startAt` / `endAt` are UTC; `startLocal` / `endLocal` are the same moments in the event's own time zone (daylight-saving aware).
- `priceMin` / `priceMax` are in major units of `currency` (JPY 2000 = ¥2,000; USD 25 = $25). Free events have `isFree: true` and prices `0`. `priceIsFlexible` marks "pay what you want" tickets.
- `registrationStatus` is Luma's own state (`open`, `waitlist`, `sold-out`, ...). `isNearCapacity` is Luma's "Near Capacity" badge.
- `guestCount` is the number of guests going, **only when the event page shows its guest list**; otherwise `null`.
- `city` is the most precise public place Luma gives (in Tokyo that is the ward, e.g. "Shibuya"). `venueName` is given only when the address is public, the event is run by an organisation, and the place name is a business or public venue (never a street address or a residential building).
- `calendarName` / `calendarUrl` / `calendarId`: the organisation or community calendar that runs the event. `null` for events on a person's personal calendar.
- `categories`: Luma's categories, only when the event page was read (`detailsFetched: true`); `calendarTags`: labels the calendar gave the event (calendar sources).
- `platform: "external"`: an event listed on a Luma calendar but hosted on another website (`url` is that site). No price or guest data for these.

### Honest limits

- **First 20 events per city page.** A Luma city page carries the first 20 events in its HTML; the rest load in the browser through Luma's internal API, which this Actor deliberately does not use. Use several cities, `all`, or organisation calendars for more. `RUN_SUMMARY` shows how many events Luma lists for each city.
- **Calendars:** the upcoming events listed in the calendar page's HTML. Calendar listings carry no prices or guest counts — turn on `includeDetails` for those.
- **No people, no descriptions, no images.** Hosts, guests, attendee lists, profiles, social handles, street addresses, coordinates, event descriptions and images are never extracted. Titles are output as the organisers wrote them.
- **Private and members-only events** and pages whose host asked search engines not to index them are skipped.
- **Categories are Luma's.** Luma assigns them automatically to some events only, so the filter can remove many events.
- **Snapshot data.** Status, prices and guest counts are as shown at `retrievedAt`.
- **Residential proxy by default.** Luma sometimes answers Apify datacenter IPs with "Rate Limit Hit" (HTTP 429), so runs use Apify's residential proxy (US) by default. A residential IP can occasionally be refused too; the first request is then retried once on a fresh session, and if that is refused as well the run fails without storing (or charging) anything — run it again a little later.
- **The site can change or refuse requests.** If Luma refuses a request mid-run, the run keeps what it has and ends successfully with `complete: false`. If the page data format changes, the Actor falls back to the pages' schema.org data with fewer fields.

### Compliance

- **Public data only:** pages anyone can open without signing in. Personal calendars and user profiles are not read.
- **robots.txt:** luma.com/robots.txt has rules for Googlebot only; this Actor still avoids every path listed there (`/social-share`, `/in/`, `/company/`, `/session-*`) and checks each URL before requesting it.
- **GDPR / privacy:** records contain event facts, not personal data fields. Event titles are published by organisers and may occasionally name a speaker or performer. If you process the data in the EU/UK, you are the controller of your use of it.
- **Luma's terms:** Luma's Terms of Use restrict reproducing site content and require access through Luma's publicly supported interfaces (this Actor reads the public web pages only). Review the terms for your use case: https://luma.com/terms
- **Attribution:** every record carries `source`, `sourceUrl`, `license` and `retrievedAt`.

### Pricing

Pay per result: you are charged for each event stored in the dataset. A run stops by itself when it reaches the maximum charge you set for the run.

# Actor input Schema

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

Luma city pages to read, one per line: a city slug or name, e.g. "sf", "San Francisco", "nyc", "london", "tokyo", "berlin". Each city page carries the first 20 events Luma shows for that city. Also accepted: "all" (every Luma city, 87 pages) or a continent: europe, na (North America), apac (Asia & Pacific), sa (South America), africa. A city page URL (https://luma.com/tokyo) works too. If both Cities and URLs are empty, the Quick start cities are used; if URLs are given while this still holds the pre-filled list, only the URLs are read.

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

Optional. Public Luma pages pasted from your browser, one per line: an event (https://luma.com/abc123de or https://lu.ma/abc123de), an organisation or community calendar (https://luma.com/<calendar-name>) or a city page. A calendar returns its listed upcoming events. Personal calendars and user profiles (luma.com/user/...) are not read.

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

Optional filter on Luma's own event categories: ai, tech, crypto, arts, climate, fitness, wellness, running, food, books, games, family (names such as "Food & Drink" also work). Keeps only events Luma tagged with at least one of them. Needs each event's page (one extra request per event, slower); Luma tags only some events, so this filter can remove many.

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

Optional. Keeps events whose title, organiser calendar or venue contains every word (not case- or accent-sensitive), e.g. "hackathon" or "AI founders". Applied to the events listed on the pages you choose; it does not search all of Luma.

## `startDate` (type: `string`):

Optional. Keep events starting on or after this LOCAL date: YYYY-MM-DD (e.g. 2026-10-20), "today", "tomorrow" or "+7d". Leave empty for no lower bound.

## `endDate` (type: `string`):

Optional. Keep events starting on or before this LOCAL date (inclusive): YYYY-MM-DD, "today", "tomorrow" or "+30d". Leave empty for no upper bound.

## `includeDetails` (type: `boolean`):

Also read every event's own page: adds Luma's categories, and for calendar events the price range, registration status, guest count, city and organiser. One extra request per event (about 1.5 s each). Turned on automatically when the categories filter is used.

## `includeExternalEvents` (type: `boolean`):

Calendars can list events hosted on other websites. When on (default), they are returned with platform "external", their own URL, dates and city (no price or guest data).

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

Maximum number of event records for the whole run (1..10000; values outside are clamped). The run stops reading pages once this many events are stored.

## `maxRequests` (type: `integer`):

Hard budget of page requests to luma.com for the whole run (1..2000; clamped): one per city / URL page plus one per event page when event pages are opened. Requests are strictly serial, at least 1.5 s apart. If the budget runs out, the run still SUCCEEDS with what it has and complete=false in the RUN_SUMMARY record.

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

Apify proxy settings. Default: Apify RESIDENTIAL proxies, country US — luma.com sometimes rate-limits Apify datacenter IPs (HTTP 429 "Rate Limit Hit" on 1 of 6 datacenter test runs, 2026-10-13), while the same requests through a residential IP were answered. One sticky session (one IP) is kept for the whole run, never rotation; only if the very first request is refused is it retried once on a fresh session. Set {"useApifyProxy": false} to run without a proxy.

## Actor input object example

```json
{
  "cities": [
    "sf",
    "nyc"
  ],
  "urls": [],
  "categories": [],
  "keyword": "",
  "startDate": "",
  "endDate": "",
  "includeDetails": false,
  "includeExternalEvents": true,
  "maxItems": 40,
  "maxRequests": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `records` (type: `string`):

Public Luma event records (event facts only) with source attribution (source, sourceUrl, license, retrievedAt) on every item.

# 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",
        "nyc"
    ],
    "urls": [],
    "categories": [],
    "keyword": "",
    "startDate": "",
    "endDate": "",
    "includeDetails": false,
    "includeExternalEvents": true,
    "maxItems": 40,
    "maxRequests": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpopendata/luma-events").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",
        "nyc",
    ],
    "urls": [],
    "categories": [],
    "keyword": "",
    "startDate": "",
    "endDate": "",
    "includeDetails": False,
    "includeExternalEvents": True,
    "maxItems": 40,
    "maxRequests": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("jpopendata/luma-events").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",
    "nyc"
  ],
  "urls": [],
  "categories": [],
  "keyword": "",
  "startDate": "",
  "endDate": "",
  "includeDetails": false,
  "includeExternalEvents": true,
  "maxItems": 40,
  "maxRequests": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call jpopendata/luma-events --silent --output-dataset

```

## MCP server setup

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

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/4owei98fCuW6zWpBv/builds/ffT0YBiUYlo63ijNa/openapi.json
