# Events Scraper: Eventbrite, Luma, Meetup (`frank_conifer_af4/eventbrite-luma-meetup-events-scraper`) Actor

Search events by keyword and city on Eventbrite, Luma and Meetup, or paste organizer, calendar and group URLs, and get one normalized table with organizer, venue, price and attendance data, plus an only-new-events monitoring mode.

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

## Pricing

from $2.00 / 1,000 event listings

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

## Events Scraper: Eventbrite, Luma, Meetup

Search Eventbrite, Luma and Meetup by keyword and city, or paste organizer, calendar and group pages, and get every event in one clean table with the organizer, venue, price and attendance.

### What does Events Scraper: Eventbrite, Luma, Meetup do?

You give it searches such as "AI in San Francisco" or "startup in London", or the pages you already follow: an Eventbrite organizer, a Luma calendar, a Meetup group. It collects the upcoming public events from all three platforms and returns them in a single schema: title, start and end time with time zone, online or in person, venue with coordinates, organizer with follower or member count, price range, category, status and attendee count where the platform shows it. Turn on **Only new events** and schedule it, and each run returns just the events that appeared since the last run, with an optional Slack or webhook ping.

### Why use it

- **Three platforms, one schema.** An Eventbrite conference, a Luma meetup and a Meetup group night look the same in your sheet, CRM or newsletter tool. No more stitching three scrapers with three field layouts.
- **Organizer data built in.** Every row names who runs the event, with the profile URL, ID and audience size: Eventbrite followers, Meetup group members, plus the organizer website when it is public. That is the list event marketers, sponsorship sales and DevRel teams actually need.
- **Made for monitoring.** Only-new-events mode remembers what each search has already returned, so a daily schedule gives you a clean feed of fresh listings instead of the same 50 events every day.
- **One city name for all sources.** Type "Seoul" or "Portland, OR". The Actor maps it to Eventbrite's location pages, Luma's nearby-events feed and Meetup's search, and drops in-person events more than 100 km away.
- **Much cheaper.** $2 per 1,000 events, all three sources included. Status rows, filtered-out events and repeats are free.

### Source coverage

| | Eventbrite | Luma (lu.ma / luma.com) | Meetup |
|---|---|---|---|
| Keyword + city search | Yes, Eventbrite's city search pages | Yes: Luma's keyword search plus the city's events feed | Yes, Meetup's event search |
| City only (no keyword) | All events in the city | All events near the city, by date | Meetup's "events near you" feed |
| URLs you can paste | Search pages (`/d/...`), organizer profiles (`/o/...`), event pages (`/e/...`) | Calendars, city pages (`luma.com/sf`), event pages | Groups, search pages (`/find/?keywords=...`), event pages |
| Organizer | Name, profile, ID, **followers**, website | Calendar (or host) name, page, ID, website | Group name, page, ID, **members** |
| Price | Min / max / currency / free flag | Min / max / currency / free flag, pay-what-you-want | Fee when set, otherwise free |
| Venue coordinates | Yes | Yes (approximate when the host hides the exact address) | Yes for in-person events |
| Attendance | Not public on Eventbrite | Guest count when shown, capacity when published | RSVP count, RSVP limit |
| Full description | Yes (one extra request per event) | Yes (one extra request per event) | Yes (no extra request) |
| Online events | Eventbrite's online listings (with the Online filter) | Rare, Luma discovery is city-based | Yes |

All three sources are read from the same public pages and JSON feeds their websites use, without logging in.

### What data you get

One row per event (`type: "event"`), plus one free status row (`type: "status"`) for each search or URL that returned nothing, could not be resolved or was blocked.

| Field | Description |
|---|---|
| `source`, `eventId`, `url` | `eventbrite`, `luma` or `meetup`, the platform's event ID (stable between runs) and the event page |
| `title`, `summary`, `descriptionText` | Title, a short summary, and the full description as plain text (when **Include full description** is on) |
| `startAt`, `endAt`, `timezone` | ISO timestamps in the event's local time with UTC offset, for example `2026-10-19T19:00:00-07:00`, and the IANA time zone |
| `isOnline`, `attendanceMode` | `true` for online-only events; `attendanceMode` is `in_person`, `online` or `hybrid` |
| `venue` | `{ name, address, city, region, country, postalCode, lat, lng }`, `null` for online events |
| `organizer` | `{ name, url, id, followers, website, type }`. `followers` is Eventbrite followers or Meetup group members |
| `price` | `{ isFree, min, max, currency, raw }` |
| `ticketUrl`, `imageUrl` | Registration or checkout link and cover image |
| `category`, `tags[]` | Platform category (for example "Business & Professional", "Tech", "Career & Business") and topics or tags |
| `status` | `scheduled`, `cancelled`, `postponed`, `sold_out` or `unknown` |
| `attendeeCount`, `rsvpCount`, `capacity` | Counts only, when the platform shows them publicly. Never names |
| `searchKey`, `searchQuery`, `searchLocation` | Which search or URL produced the row (for example `luma:ai@san-francisco`) |
| `isNew` | `true` in only-new-events mode, `null` otherwise |
| `scrapedAt` | Time of the scrape |
| `status`, `message` (status rows) | `no_results`, `no_new_events`, `location_not_resolved`, `blocked`, `failed`, `duplicates_only` or `invalid_input`, with an explanation |

The run's key-value store also has a `SUMMARY` record with, per search and source: the resolved location, events scanned, matched and pushed, new events, and requests per site.

### How to use

1. Add one or more **Searches**: a `location` (city) and an optional `query` (keyword). Leave out the query to get everything in the city.
2. Optionally paste **Organizer, calendar, group or event URLs** you want to follow.
3. Pick the **Sources** (all three by default) and set **Max events per search and source**. That number caps both the result size and the cost.
4. Optionally filter by date, online or in person, and free events only. Filtered-out events are not charged.
5. Click **Start**, then export the **Events** view (or the **Organizers and venues** view) as CSV, Excel or JSON.

```json
{
  "searches": [
    { "query": "AI", "location": "San Francisco" },
    { "query": "startup", "location": "London" },
    { "location": "Seoul", "sources": ["meetup", "luma"] }
  ],
  "startUrls": [
    "https://www.eventbrite.com/o/the-business-minds-108731452361",
    "https://luma.com/readingrhythms-global",
    "https://www.meetup.com/startup-oasis-global/"
  ],
  "sources": ["eventbrite", "luma", "meetup"],
  "dateFrom": "today",
  "dateTo": "60 days",
  "onlineFilter": "in_person",
  "freeOnly": false,
  "maxEventsPerSearch": 50,
  "includeDescription": true
}
```

Locations are city names. Add the state or country when a name is ambiguous ("Portland, OR", "London, Ontario"). Dates accept `YYYY-MM-DD` or relative values such as `today`, `7 days`, `2 weeks`, which keep working in scheduled runs.

### Output example

From a real run of the prefilled input ("tech" in San Francisco), trimmed:

```json
{
  "type": "event",
  "source": "eventbrite",
  "eventId": "2000266637358",
  "url": "https://www.eventbrite.com/e/sf-women-in-tech-founders-professionals-networking-night-tickets-2000266637358",
  "title": "SF Women in Tech: Founders & Professionals Networking Night",
  "summary": "Connect with women in tech, startups, and innovation. Network, share ideas, and build meaningful professional relationships.",
  "descriptionText": "Join us for an empowering and insightful networking event designed for professionals in the dynamic fields of Women in Tech, Fintech, HealthTech...",
  "startAt": "2026-10-19T19:00:00-07:00",
  "endAt": "2026-10-19T22:00:00-07:00",
  "timezone": "America/Los_Angeles",
  "isOnline": false,
  "attendanceMode": "in_person",
  "venue": {
    "name": "Lush Lounge",
    "address": "1221 Polk Street, San Francisco, CA 94109",
    "city": "San Francisco",
    "region": "CA",
    "country": "US",
    "postalCode": "94109",
    "lat": 37.7881262,
    "lng": -122.4204128
  },
  "organizer": {
    "name": "The Business Minds",
    "url": "https://www.eventbrite.com/o/the-business-minds-108731452361",
    "id": "108731452361",
    "followers": 3212,
    "website": null,
    "type": "eventbrite_organizer"
  },
  "price": { "isFree": false, "min": 9.85, "max": 39.19, "currency": "USD", "raw": "9.85 - 39.19 USD" },
  "ticketUrl": "https://www.eventbrite.com/checkout-external?eid=2000266637358",
  "category": "Business & Professional",
  "tags": ["Startups & Small Business", "Meeting or Networking Event", "networking", "womenintech"],
  "status": "scheduled",
  "attendeeCount": null,
  "rsvpCount": null,
  "capacity": null,
  "searchKey": "eventbrite:tech@san-francisco",
  "searchQuery": "tech",
  "searchLocation": "San Francisco",
  "isNew": null,
  "scrapedAt": "2026-10-16T12:59:03.655Z"
}
```

A Meetup row from the same run carries the group as organizer (`"name": "Startup Oasis"`, `"followers": 3910` members), `"attendeeCount": 34` RSVPs and `"category": "Career & Business"`. A Luma row carries the calendar as organizer with its website (`"Context, Community, Connections, & Capital"`, `https://www.contextvc.com/`) and `"price": { "isFree": false, "min": 200, "max": 200, "currency": "USD" }`.

### Pricing

**$0.002 per event row** (event `event-listing`), which is $2 per 1,000 events, for all three sources. Comparable event scrapers on the Store usually charge about $3 to $15 per 1,000 events, often for a single platform.

| Run | Events returned | Cost |
|---|---|---|
| Prefilled input: "tech" in San Francisco, 3 sources × 15 | 45 | $0.09 |
| 3 cities × 2 keywords × 3 sources × 50 | up to 900 | up to $1.80 |
| 20 cities, all events, 3 sources × 100 | up to 6,000 | up to $12.00 |
| Daily monitoring of 10 searches on 3 sources, about 5 new events per search and source per day | about 150 per day | about $0.30 per day (after the baseline run) |

**Not charged:** status rows (no results, location not found, blocked source, failed or invalid URL), events removed by your filters or by the distance check, the same event found again by another search in the same run, events already returned in only-new-events mode, and the webhook. If you set a maximum cost per run, the Actor stops cleanly when it is reached and records that in the summary.

### Monitoring recipe: new events in your cities every morning

1. Create a saved task with your searches (and organizer, calendar or group URLs), **Only new events since the last run** turned on and a relative date window such as `dateTo: "60 days"`.
2. Paste a Slack incoming webhook (or a Zapier, Make or n8n webhook) into **Webhook URL**. After each run with new events, it receives a short text summary plus a JSON list per search.
3. Add a schedule to the task, for example daily at 07:00.
4. The first run is the baseline: it returns up to **Max events per search and source** per search and marks every event it scanned as known. From then on, each run returns and charges only events that were not returned before. When more new events appear than the maximum, the rest come in the next run.
5. Keep the same **State store name** for the same watchlist. Use a different name to start a separate baseline.

### Integrations / scheduling

- **Schedules:** run daily or weekly with only-new-events mode for a fresh events feed.
- **Webhooks:** the built-in webhook posts a summary to Slack, Zapier, Make or n8n. Apify's own run webhooks can trigger any downstream job when a run finishes.
- **API:** start runs and read the dataset from any language with the Apify API or the JavaScript and Python clients. Use `?view=overview` for the compact table or `?view=organizers` for the prospecting view.
- **MCP and AI agents:** the Actor can be called as a tool through the Apify MCP server, so an agent can answer "which AI events in London next month have more than 100 people going?"

### FAQ & limitations

**Who is this for?** Event marketers and sponsorship sales teams looking for events and organizers to partner with, local-guide and newsletter builders who publish "what's on" lists, community and DevRel teams tracking meetups in their space, and market researchers measuring event activity by city or topic.

**Why did I get fewer events than the maximum?** The platform may simply have fewer matching events, filters removed some, the same event was already returned by another search in the run, or in-person events were more than 100 km from the city. Luma's keyword search is global and returns at most 50 matches, so the Actor also scans the city's events feed for titles and organizers that contain your keyword. For small cities that can still be a short list.

**Is the same event on two platforms merged?** No. Rows are deduplicated per platform and event ID. An event cross-posted on Eventbrite and Meetup appears once per platform, with each platform's own data.

**Online events.** With **Online only**, Eventbrite searches its global online listings and Meetup searches online events. Luma's discovery is city-based, so it rarely returns online events. Meetup's search also includes some online events when the filter is **Any**. Choose **In person only** to drop them.

**Attendance numbers.** Eventbrite does not show attendee counts publicly, so those fields are `null`. Luma shows a guest count unless the host hides it. Meetup shows RSVP counts. Only counts are returned, never who is attending.

**Free events on Meetup.** Meetup events without a fee setting are marked free. Some organizers collect payment elsewhere and say so in the description.

**Location resolution.** City names are geocoded once per run and mapped to each platform. If Eventbrite maps a name to a different city, the search returns a `location_not_resolved` status row instead of wrong events. Add the state or country, or paste an Eventbrite search URL.

**Monitoring accuracy.** "New" means not returned before by that search. Feeds move over time: an older event can count as new when it first enters your date window, and Meetup's search order varies a little between requests, so an older Meetup event occasionally shows up as new.

**Speed and politeness.** Requests to each site are limited to about 2 per second. A search costs one or two list requests per 20 to 50 events, plus one small request per event for Eventbrite and Luma descriptions. The prefilled input finishes in about 20 seconds. If a platform starts blocking or rate-limiting, the Actor stops using it for the rest of the run, keeps the other sources, and adds a `blocked` status row.

**Do I need a proxy?** No. All three platforms work with direct requests, which is the default. If you turn on Apify Proxy, Luma and Meetup go through it, but Eventbrite rejects Apify datacenter proxy IPs, so the Actor detects that and connects to Eventbrite directly for the rest of the run.

**Responsible use.** The Actor reads public event listings that organizers publish on Eventbrite, Luma and Meetup. It does not log in and never collects attendee names, guest lists, RSVP member lists or attendee contact details, only counts. Organizer names can be individuals (for example a Luma host), so process the data in line with the GDPR and local law, respect each platform's terms when you republish listings, and link back to the original `url` when you show events to others.

# Actor input Schema

## `searches` (type: `array`):

A list of searches. Each one is an object with a city in "location" and an optional keyword in "query", for example {"query": "AI", "location": "San Francisco"}. Leave out "query" to get all events in that city. Add "sources": \["luma"] to a search to limit it to some sources. Every search runs once per source, and each source returns up to "Max events per search and source" events.

## `startUrls` (type: `array`):

Optional. One URL per line. Eventbrite: search pages (eventbrite.com/d/ca--san-francisco/ai/), organizer profiles (eventbrite.com/o/name-123456) and event pages. Luma: calendars (luma.com/your-calendar or lu.ma/calendar/cal-...), city pages (luma.com/sf) and event pages. Meetup: groups (meetup.com/group-name/), search pages (meetup.com/find/?keywords=...\&location=...) and event pages. URLs are processed whatever you select in "Sources".

## `sources` (type: `array`):

Which platforms the searches run on. Default: all three. Each selected source is one more search per keyword and city, so selecting fewer sources lowers both cost and run time.

## `maxEventsPerSearch` (type: `integer`):

Upper limit of event rows per search per source (and per start URL). This caps your cost: each event row is one charged event. Example: 2 searches x 3 sources x 50 = at most 300 events.

## `dateFrom` (type: `string`):

Only events starting on or after this date (in the event's local time). Use YYYY-MM-DD, or a relative value such as "today", "tomorrow", "7 days" or "2 weeks", which is handy for scheduled runs. Leave empty for all upcoming events.

## `dateTo` (type: `string`):

Only events starting on or before this date. Same format as above, for example "30 days" for the next month.

## `onlineFilter` (type: `string`):

Any: all events. Online: online and hybrid events (Eventbrite then searches its global online listings instead of the city). In person: in-person and hybrid events.

## `freeOnly` (type: `boolean`):

Keep only events marked free. On Meetup an event counts as free when no fee is set.

## `includeDescription` (type: `boolean`):

Adds descriptionText (plain text) to every event. Meetup includes it at no extra cost. On Eventbrite and Luma it needs one extra small request per event, so turning it off makes those sources about twice as fast. Luma categories and capacity also come from that request.

## `onlyNewEvents` (type: `boolean`):

Monitoring mode. Event IDs per search and source are kept in the named key-value store below. Each run outputs (and charges) only events that were not returned before. The first run returns everything as the baseline. Schedule the Actor daily or weekly with the same searches and store.

## `stateStoreName` (type: `string`):

Named key-value store that keeps the seen event IDs between runs (only used with Only new events). Use a different name per watchlist to keep separate baselines. Lowercase letters, digits and hyphens.

## `webhookUrl` (type: `string`):

Optional. After the run, a JSON summary of the events found per search (new events in monitoring mode) is POSTed here, only when there is at least one event. The payload has a Slack-compatible text field, so a Slack incoming webhook URL works directly. Not charged.

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

How many search and source combinations run at the same time. Requests to any single site are always limited to about 2 per second, so values above 3 mostly help long lists of searches.

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

Optional. All three sources work without a proxy, so direct requests are the default. A proxy can help if Luma or Meetup start rate-limiting your runs. Eventbrite rejects Apify datacenter proxy IPs, so the Actor automatically connects to Eventbrite directly when that happens.

## Actor input object example

```json
{
  "searches": [
    {
      "query": "AI",
      "location": "San Francisco"
    },
    {
      "query": "startup",
      "location": "London"
    },
    {
      "location": "Seoul",
      "sources": [
        "meetup",
        "luma"
      ]
    }
  ],
  "startUrls": [
    "https://www.eventbrite.com/o/the-business-minds-108731452361",
    "https://luma.com/readingrhythms-global",
    "https://www.meetup.com/startup-oasis-global/"
  ],
  "sources": [
    "eventbrite",
    "luma",
    "meetup"
  ],
  "maxEventsPerSearch": 15,
  "onlineFilter": "any",
  "freeOnly": false,
  "includeDescription": true,
  "onlyNewEvents": false,
  "stateStoreName": "events-state",
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `organizers` (type: `string`):

No description

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

No description

## `summary` (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 = {
    "searches": [
        {
            "query": "tech",
            "location": "San Francisco"
        }
    ],
    "maxEventsPerSearch": 15,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("frank_conifer_af4/eventbrite-luma-meetup-events-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 = {
    "searches": [{
            "query": "tech",
            "location": "San Francisco",
        }],
    "maxEventsPerSearch": 15,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("frank_conifer_af4/eventbrite-luma-meetup-events-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 '{
  "searches": [
    {
      "query": "tech",
      "location": "San Francisco"
    }
  ],
  "maxEventsPerSearch": 15,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call frank_conifer_af4/eventbrite-luma-meetup-events-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,frank_conifer_af4/eventbrite-luma-meetup-events-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/57r7egTedPb11SZG6/builds/76uZxFcfgEHr0JDCO/openapi.json
