# Meetup Event Scraper — Groups, Organizers & RSVP Leads (`scrapersdelight/meetup-scraper`) Actor

Scrape upcoming public Meetup events by keyword and city: title, date, description, venue, RSVP count, the hosting group with its public rating, and the organizer. Deep pagination past Meetup's own totalCount, server-side date/RSVP/format filters. No login, no API key.

- **URL**: https://apify.com/scrapersdelight/meetup-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Lead generation, Marketing, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.90 / 1,000 per 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.

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

## Meetup Event Scraper — Groups, Organizers & RSVP Leads

Scrape upcoming public events from [Meetup.com](https://www.meetup.com) by keyword and city. **One row = one upcoming event**, carrying **title, eventUrl, dateTime, endTime, eventType, description, rsvpCount, maxTickets, rsvpState, venueName, venueAddress, venueCity/State/Country, groupName, groupUrl, groupTimezone, groupRatingAverage, groupRatingCount, hostName** and **hostMemberId**. Filter by keyword, place, in-person/online/hybrid, date window, minimum RSVPs and radius — or paste a Meetup `/find/` URL straight from your browser.

**12 keyword+city searches returned 1,124 unique upcoming events — 51 to 191 per search — containing 729 distinct groups and 595 distinct organizers** (measured live through Apify Proxy on 2026-08-12, not quoted from a marketing page). **95.5% of rows carry a real group rating** (mean 4.61 of 5).

No login. No API key. No cookies. No CAPTCHA solving. Click **Try for free** and hit **Start** — the Actor ships prefilled and returns rows with zero edits.

```json
{
  "keyword": "tech",
  "location": "New York, NY",
  "maxResults": 100
}
```

That is literally the shipped default. It returned **100 events in 22 seconds using 4 HTTP requests** on the run that produced the numbers below.

***

### Highlights

- 🎟️ **One row per upcoming event**, 28 flat columns — no nested JSON to unpack for CSV or Sheets.
- ⭐ **Group ratings on 95.5% of rows** — `groupRatingAverage` + `groupRatingCount`, straight off the group's public profile.
- 🧲 **Organizer on 87.5% of rows** — `hostName` + `hostMemberId`, plus the group URL you can open and pitch.
- 📄 **Deep pagination, not page 1** — Meetup's own `totalCount` said 52 for `tech` in New York; the cursor walk returned **191 unique events**.
- 🚫 **Duplicates dropped before billing** — 52 repeats in a 1,176-edge contiguous walk (4.4%) were dropped, so you were never charged twice.
- 🎛️ **Server-side filters** — event format, date window and minimum RSVPs are applied by Meetup before it sends results, so filtering makes a run *cheaper*.
- 🌍 **Anywhere Meetup works** — free-text places, or exact `lat`/`lon` when a city name is ambiguous.
- 📉 **Cheap transport** — datacenter proxy only; 42 of 43 requests returned a clean 200 with no residential proxy.

#### Copy to your AI assistant

```text
scrapersdelight/meetup-scraper on Apify.
Call: ApifyClient("TOKEN").actor("scrapersdelight/meetup-scraper").call(run_input={...}),
then client.dataset(run["defaultDatasetId"]).list_items().items for results.
Key inputs: keyword (string, default "tech"), location (string, default "New York, NY"),
startUrls (array of meetup.com /find/ URLs), eventType (ANY|PHYSICAL|ONLINE|HYBRID),
startDate/endDate (ISO date), minRsvpCount (int, 0), radiusMiles (int, 0),
sort (RELEVANCE|DATETIME), maxResults (int, 100), maxResultsPerSearch (int, 0),
searches (array of {keyword, location}), lat/lon (string).
Output: one flat object per event with id, title, eventUrl, dateTime, endTime, eventType,
isOnline, description, rsvpCount, maxTickets, rsvpState, venue*, group*, hostName, hostMemberId.
Full actor spec: GET https://api.apify.com/v2/acts/scrapersdelight~meetup-scraper (Bearer TOKEN).
Get token: https://console.apify.com/account/integrations
```

### Meetup API alternative — scrape Meetup without the API

Meetup's official API is gated behind an approval process and a paid tier, and it does not hand you a keyword+city search you can schedule. This Actor is the drop-in **Meetup API alternative**: it reads the same public data Meetup shows a logged-out visitor, returns flat JSON/CSV on demand or on a schedule, and needs **no key, no OAuth app and no approval**. What it cannot do is anything Meetup keeps behind a login — attendee identities and private groups are simply not available, to us or to anyone else without an account.

### How to scrape Meetup events (step by step)

1. [Create a free Apify account](https://console.apify.com/sign-up).
2. Open this Actor and click **Try for free**.
3. On the **Input** tab, type a **Keyword** and a **Location** — or leave the prefilled `tech` / `New York, NY`.
4. Optional: set **Event format**, a **date window**, **Minimum RSVPs**, and **Max results** (your cost ceiling).
5. Click **Start**. The default run finished in 22 seconds.
6. Open **Storage → Dataset** and export **JSON, CSV, Excel or HTML**, or pull it from the API.

Three to four minutes from signup to a downloaded CSV.

### What you get — one row per upcoming event

| Group | Fields |
|---|---|
| Identity | `id`, `title`, `eventUrl` |
| Timing | `dateTime`, `endTime`, `groupTimezone` — ISO-8601 with the event's local UTC offset |
| Format | `eventType` (`PHYSICAL` / `ONLINE` / `HYBRID`), `isOnline` |
| Content | `description` (plain text, trimmed at 5,000 chars) |
| Attendance | `rsvpCount`, `maxTickets`, `rsvpState` |
| Venue | `venueName`, `venueAddress`, `venueCity`, `venueState`, `venueCountry` |
| Group | `groupId`, `groupName`, `groupUrl`, `groupTimezone`, `groupIsNew`, `groupRatingAverage`, `groupRatingCount` |
| Organizer | `hostName`, `hostMemberId` |
| Provenance | `searchKeyword`, `searchLocation` (the place Meetup actually resolved), `scraped_at` |

Dates are ISO-8601 with the event's local offset (`2026-08-15T15:30:00-04:00`). `groupRatingAverage` is a **0–5 scale**, never a percentage. Missing values are `null` and are never dropped, so your CSV columns stay stable across runs. Three dataset views ship with the Actor: **Events**, **Group & organizer leads**, and **Venues**.

### Why scrape Meetup? Use cases by role

| Who | Goal | How this Actor helps |
|---|---|---|
| **Sponsorship / community sales** | Find communities worth sponsoring | Rank by `rsvpCount` + `groupRatingAverage`, pitch `hostName` via `groupUrl` |
| **Developer relations** | Find live tech communities in a city | `keyword: kubernetes`, one run per metro, sort by RSVPs |
| **Event-marketing agency** | Build a city calendar to pitch against | `sort: DATETIME` + a 7-day `endDate` window |
| **Venue / catering / AV vendors** | Find organizers who book rooms | `eventType: PHYSICAL` → `venueName` + `venueAddress` on 78.9% of in-person rows |
| **Events aggregator** | Populate an events feed nightly | Apify Schedule + dedupe on `id`; 4.4% intra-run repeats already removed |
| **Technical recruiter** | Source niche skill communities | Keyword by stack, then work `groupUrl` + `hostName` |

**Worked example.** A DevRel lead runs `keyword: networking` with `lat: 51.45` / `lon: -0.24` (London, UK), `eventType: PHYSICAL`, `endDate` seven days out and `minRsvpCount: 10`. That exact run returned **16 events from 1 HTTP request**, every one in-person, every one with a street address, the smallest at 12 RSVPs — a one-week shortlist of London meetups worth showing up to, for **$0.014**.

### Read this before you run it

1. **Meetup's keyword search is fuzzy, not strict.** A deliberately nonsense keyword (`zzqxwvpq nonexistent topic`) in New York still returned 200 unrelated events — Meetup falls back to nearby events rather than returning nothing. Treat `keyword` as a relevance hint and check `title` yourself; a narrow keyword does **not** guarantee narrow rows.
2. **Bare city names are ambiguous and Meetup picks for you.** In the measured census, `London` resolved to **London, Ontario** and `Berlin` to **Berlin, Connecticut**; a later probe resolved `Amsterdam` to **Amsterdam, NY**. Write `London, UK` / `Berlin, Germany`, or pass exact `lat`/`lon`. Every row carries `searchLocation` with the place Meetup actually used, so a bad match is visible in the data rather than hidden.
3. **`totalCount` under-reports — that is why the Actor paginates.** For `tech` in New York, Meetup reported `totalCount: 52` and the contiguous cursor walk returned **191 unique events**. Do not size a run from the number in the log line.
4. **Upcoming events only.** Meetup publishes no historical events. A past `startDate` is ignored (the run logs that it used "now"); a past `endDate` stops the run with an error rather than delivering an empty dataset.
5. **Online events have no address.** 40.8% of measured rows were `ONLINE`; **none** of them carried a `venueCity`, and their `venueName` is literally the string `Online event`. Set **Event format = In-person** if you are buying addresses.

### Sample row

A real row from the measured run — nothing prettified:

```jsonc
{
  "id": "315547591",
  "title": "Toronto Founders and Entrepreneurs Networking at 5 Elements Espresso Bar",
  "eventUrl": "https://www.meetup.com/toronto-founders-hub/events/315547591/",

  "dateTime": "2026-08-15T15:30:00-04:00",
  "endTime": "2026-08-15T18:00:00-04:00",
  "eventType": "PHYSICAL",
  "isOnline": false,

  "description": "Join the Toronto Founders Hub every other Saturday at 3:30pm for our regular meetup at 5 Elements Espresso Bar.\n\nSit down with other entrepreneurs to network and discuss business ideas or strategies.\n\\- Broaden your network\n\\- Get critical feedback\n\\- Socialize in low pressure setting\n\nI look forward to seeing you!",

  "rsvpCount": 35,
  "maxTickets": 35,
  "rsvpState": "JOIN_OPEN",

  "venueName": "5 Elements Espresso",
  "venueAddress": "131 Avenue Road",
  "venueCity": "Toronto",
  "venueState": "ON",
  "venueCountry": "ca",

  "groupId": "37109317",
  "groupName": "Toronto Founders and Entrepreneurs Hub",
  "groupUrl": "https://www.meetup.com/toronto-founders-hub/",
  "groupTimezone": "America/Toronto",
  "groupIsNew": false,
  "groupRatingAverage": 4.67,
  "groupRatingCount": 64,

  "hostName": "Brandon Mills",
  "hostMemberId": "294353236",

  "searchKeyword": "marketing",
  "searchLocation": "Toronto, Ontario, Canada",
  "scraped_at": "2026-08-13T01:40:57.640Z"
}
```

Fields people misread:

- `groupRatingAverage` is **0–5**, not a percentage. `0` with `groupRatingCount: 0` means *nobody has rated this group* — that is 4.5% of rows, and it is not a scraping failure.
- `rsvpCount` counts RSVPs, not attendees. Median is 3; it is a popularity signal, not a headcount.
- `maxTickets` is `null` when the organizer set no cap (73.0% of rows), not `0`.
- `hostName`/`hostMemberId` are the publicly listed event host — name and numeric id only, exactly what a logged-out visitor sees.

### Field fill — measured on 1,124 unique events

Sample: 12 keyword+city searches (`tech`/New York, `startup`/Austin, `ai`/San Francisco, `yoga`/Chicago, `networking`/London, `javascript`/Berlin, `hiking`/Denver, `marketing`/Toronto, `crypto`/Miami, `book club`/Seattle, `music`/New York, `business`/Los Angeles), walked contiguously to exhaustion or 20 pages, on 2026-08-12 through Apify Proxy.

| Field | Fill | Note |
|---|---|---|
| `id`, `title`, `eventUrl`, `dateTime`, `endTime`, `eventType`, `isOnline`, `rsvpCount`, `rsvpState`, `searchKeyword`, `searchLocation`, `scraped_at` | **100.0%** | Never missing |
| **`groupId`, `groupName`, `groupUrl`, `groupTimezone`, `groupIsNew`, `groupRatingCount`** | **100.0%** | Every event names its group |
| **`groupRatingAverage`** | **100.0% present, 95.5% non-zero** | 51 rows (4.5%) are unrated groups: `0` with `groupRatingCount: 0` |
| `description` | 89.5% | Mean length 1,571 characters |
| **`hostName`, `hostMemberId`** | **87.5%** | 85.5% on in-person events, 90.4% on online |
| `venueName` | 86.3% | Includes the literal `Online event` placeholder on online rows |
| `venueCity`, `venueCountry` | 47.4% | **0% on online events** |
| `venueState` | 47.3% | Blank in countries with no state field |
| `venueAddress` | 46.7% | **78.9% of the 662 in-person events** |
| `maxTickets` | 27.0% | `null` = organizer set no cap |

**The headline that could mislead you: this is not an address product unless you filter for in-person events.** 40.8% of rows are online, and every one of them has an empty `venueCity` and `venueAddress`. Set `eventType: PHYSICAL` and the address fill goes from 46.7% to 78.9%.

Row mix in that sample: 662 in-person (58.9%), 459 online (40.8%), 3 hybrid (0.3%). RSVP distribution: 25th percentile 1, median 3, 75th percentile 11, 90th percentile 23, maximum 1,036 — **309 events (27.5%) have 10 or more RSVPs and only 30 (2.7%) have 50 or more**, so set `minRsvpCount` with that in mind. `rsvpState` breaks down as `JOIN_OPEN` 936, `JOIN_APPROVAL` 166, `CLOSED` 12, `JOIN_DUES_APPROVAL` 7, `NOT_OPEN_YET` 2, `CANCELLED` 1.

**Every run prints its own measured fill in the log**, so you never have to take this table's word for it.

### How to run it

#### 1. Keyword + city (the default)

```json
{ "keyword": "tech", "location": "New York, NY", "maxResults": 100 }
```

100 events, 4 requests, 22 seconds, $0.09.

#### 2. Paste a Meetup find URL

Run the search on meetup.com, copy the address bar, paste it in:

```json
{
  "startUrls": ["https://www.meetup.com/find/?keywords=hiking&location=us--co--Denver"],
  "maxResults": 20
}
```

The keyword and place are read out of the URL. Only `/find/` URLs carry a keyword — a group URL like `https://www.meetup.com/denver-hiking-group/` is skipped with a warning that names the URL, rather than being silently dropped.

#### 3. In-person only, next 7 days, 10+ RSVPs

```json
{
  "keyword": "networking",
  "lat": "51.45",
  "lon": "-0.24",
  "eventType": "PHYSICAL",
  "endDate": "2026-08-20",
  "minRsvpCount": 10,
  "sort": "DATETIME",
  "maxResults": 25
}
```

Meetup applies all three filters server-side. This run returned 16 rows from **one** HTTP request, all with street addresses.

#### 4. Several cities in one run

```json
{
  "searches": [
    { "keyword": "startup", "location": "Austin, TX" },
    { "keyword": "startup", "location": "Denver, CO" },
    { "keyword": "startup", "location": "Miami, FL" }
  ],
  "maxResultsPerSearch": 50,
  "maxResults": 150
}
```

`maxResultsPerSearch` stops one busy city from eating the whole budget.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `keyword` | string | `tech` | What to search for. Required in practice — Meetup's search needs a keyword. |
| `location` | string | `New York, NY` | Free-text place. **Qualify it** (`London, UK`) — bare names resolve unpredictably. |
| `startUrls` | array of strings | `[]` | Meetup `/find/` URLs; keyword + place read from the URL. Extra searches on top of the fields above. |
| `eventType` | select | `ANY` | `ANY` / `PHYSICAL` / `ONLINE` / `HYBRID`. Server-side; narrowing costs nothing. |
| `startDate` | datepicker | empty = now | Events starting on or after. A past date is ignored (Meetup has no history). |
| `endDate` | datepicker | empty = no bound | Events starting on or before. A past date stops the run with an error. |
| `minRsvpCount` | integer | `0` | Server-side RSVP floor. Only 27.5% of events reach 10 and 2.7% reach 50. |
| `radiusMiles` | integer | `0` | 0 = Meetup's default radius, which is what every number here was measured with. |
| `sort` | select | `RELEVANCE` | `RELEVANCE` or `DATETIME` (soonest first). |
| `maxResults` | integer | `100` | **Your cost ceiling.** Duplicates never count against it. |
| `maxResultsPerSearch` | integer | `0` | Per keyword+location cap. 0 = only the total applies. |
| `searches` | array | `[]` | `[{ "keyword": "...", "location": "..." }]` for multi-city runs. Added to the fields above. |
| `lat` / `lon` | string | empty | Exact coordinates; skips geocoding entirely. Must be supplied together. |
| `proxyConfiguration` | proxy | Apify Proxy (datacenter) | Datacenter is enough — see the transport numbers below. |

### Pricing

**$0.0009 per event returned — $0.90 per 1,000 events.** Charged on the `event-scraped` event. There is no actor-start charge and no monthly platform fee from this Actor.

You are charged for events **delivered to your dataset**, never for a request, a retry, a page that turned out to be all duplicates, or the same event twice. Duplicates are dropped on `id` *before* anything is pushed. Rows are charged as they are pushed (`Actor.pushData(items, 'event-scraped')`), so if you hit a budget cap you get whole rows and stop — never a half-billed dataset.

| Run | Events | Cost |
|---|---|---|
| The shipped default (`tech`, New York) | 100 | **$0.09** |
| One city walked to exhaustion (`tech`, New York) | 191 | **$0.17** |
| The 12-search census on this page | 1,124 | **$1.01** |
| A nightly 10-city feed for a month | 10,000 | **$9.00** |

`maxResults` is the dial: set it to what you are willing to spend × 1,111.

### Honest limits

- **There is no groups mode.** You get group data — name, URL, timezone, rating, rating count — as columns on every *event* row, and 1,124 events yielded 729 distinct groups. What you cannot do is search Meetup for groups that have no upcoming event, and there is no `memberCount` or organizer social-link field, because event search does not return them. If a standalone group product is what you need, say so on the Issues tab.
- **Meetup's keyword search is fuzzy and bare city names resolve unpredictably** — both measured, both detailed above. Filter on `title` if you need keyword precision, and qualify the place or pass `lat`/`lon`.
- **`venueAddress` is 46.7% overall** and 78.9% on in-person events; online events (40.8% of rows) carry no address, city or country at all. That is Meetup not publishing one, not a scraping failure.
- **4.5% of rows have `groupRatingAverage: 0`** — the group has never been rated. `0` and `null` are different things here: `0` means rated by nobody, `null` would mean Meetup returned no rating object.
- **No attendee lists, ever.** Meetup keeps attendee identities behind a login. You get RSVP **counts** and the public **host**. There is no workaround that does not involve logging in, which this Actor does not do.
- **Upcoming events only.** Meetup exposes no historical events, so there is no way to backfill last month.
- **`maxTickets` is 27.0%** — most organizers never set a cap. It is not a capacity estimate for the other 73%.
- **One run deduplicates within itself.** Across runs, dedupe on `id`, which is Meetup's own stable event id.

### How it works, and the transport ladder

Meetup renders its find page from an Apollo cache, but the same data is served far more cheaply by Meetup's **public GraphQL endpoint** at `POST https://www.meetup.com/gql2`, which accepts plain un-persisted queries with no authentication. Two operations do the work: `locationSearch(query)` geocodes your free-text place into the lat/lon the event filter requires, then `eventSearch(filter, first, after, sort)` returns a Relay connection whose `after` cursor gives genuine deep pagination. No browser, no cookies, no login and no CAPTCHA solving.

Measured 2026-08-12, **through Apify Proxy** (a home-broadband result would not be shippable — no Apify container has a residential home IP):

| Rung | Result |
|---|---|
| Apify Proxy, datacenter, fresh session per request | **42 of 43 requests returned HTTP 200**; the single failure was a proxy-level `UPSTREAM502`, recovered by the built-in fresh-session retry |
| Searches completed | **12 of 12** keyword+city searches returned events |
| Residential | Not required, not the default — it would only make a run cost more |

Sustained load in that census: **30 contiguous pages, 1,176 edges, 1,124 unique events, zero HTTP 403s and zero rate-limit responses.** The shipped default run used 4 requests for 100 rows.

### Uniqueness and dedupe

Measured on **contiguous** cursor pages — sparse sampling would have hidden this. Across 30 pages and 1,176 edges, **52 edges (4.4%) were events Meetup had already returned on an earlier page** of the same search. Every one was dropped on `id` before `pushData`, so the dataset held 1,124 unique events and your bill would have covered 1,124, not 1,176. The default `tech`/New York run dropped 7 repeats out of 107 edges the same way.

The mechanism is Meetup's own recurring-event consolidation and cursor overlap, not a bug in the Actor. The dedupe key is `Event.id`, Meetup's stable id — the same key to use for cross-run dedupe on a schedule.

### When a run fails

- **Zero rows throws.** A run that scrapes nothing stops with the scope it searched and the HTTP counts: `No events scraped. Scope: "…" @ …. HTTP: 2 gql2 request(s), 2 OK, statuses {"200":2}.` If the counts look healthy, the search matched nothing — that is a filter problem, not a block, and the message says so.
- **Schema drift fails loudly.** If Meetup changes its GraphQL schema, the query fails validation and the run stops with the GraphQL error text. It never degrades into a quiet empty dataset.
- **A past `endDate` errors** instead of returning nothing, because Meetup has no historical events.
- **An unusable `lat`/`lon` stops the run** — one supplied without the other, a value that is not a number, or one out of range — rather than silently falling back to the geocoder and searching somewhere else.
- **An unusable pasted URL warns** with the URL and the supported shape, so you can see which of your inputs was ignored.
- **Transient proxy/network failures retry** on a fresh session up to 4 times before that request is given up on.

### Who buys this

- **Community and DevRel platforms** (Common Room, Orbit-style tooling, developer-tool vendors) — `groupUrl` + `hostName` + `rsvpCount` is a ranked list of live communities to sponsor or speak at.
- **Event sponsorship sales** — `groupRatingAverage` (mean 4.61, 95.5% coverage) plus RSVP counts separates the 27.5% of events with real turnout from the long tail.
- **Venue, catering and AV vendors** — `eventType: PHYSICAL` gives `venueName` + `venueAddress` on 78.9% of rows: who is already booking rooms in your city, and where.
- **Event aggregators and city guides** — a scheduled `sort: DATETIME` run with a 7-day `endDate` is a clean weekly calendar feed, deduped on `id`.
- **Technical recruiters** — keyword by stack, then the group URL and organizer name for the communities that actually meet.
- **Market researchers tracking a category** — RSVP counts and event cadence per keyword per city, sampled weekly.

### Sibling Actors

| Actor | What it is | Why you would use it instead |
|---|---|---|
| [Eventbrite Scraper](https://apify.com/scrapersdelight/eventbrite-scraper) | Ticketed events by city and category | Ticket prices and organizers; Eventbrite skews commercial, Meetup skews community |
| [PartySlate Scraper](https://apify.com/scrapersdelight/partyslate-venue-scraper) | Event venues and vendors across 119 US metros | You want the venue company as the lead, not the event |
| [Giggster Scraper](https://apify.com/scrapersdelight/giggster-space-scraper) | Bookable film, photo and event spaces | Hourly-rate spaces with host contacts, not scheduled meetups |

Meetup is where you go for the **organizer and the community**; Eventbrite is where you go for the **ticket price**; PartySlate and Giggster are where you go for the **venue**.

### Run it from your code

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('scrapersdelight/meetup-scraper').call({
    keyword: 'tech', location: 'New York, NY', maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length);
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("scrapersdelight/meetup-scraper").call(run_input={
    "keyword": "tech", "location": "New York, NY", "maxResults": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["rsvpCount"], item["groupUrl"])
```

```bash
curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~meetup-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "keyword": "tech", "location": "New York, NY", "maxResults": 50 }'
```

Results also push straight to **Make, Zapier, n8n, Google Sheets, Slack, webhooks** and the **Apify MCP server** for AI agents.

### FAQ

**Does this need an account, login or API key?** No. It reads Meetup's public GraphQL endpoint with no authentication at all — the same data a logged-out visitor sees.

**My search returned 0 results — what now?** The run stops with an error naming the scope and the HTTP counts. If the counts are healthy the search genuinely matched nothing: widen the keyword, drop `minRsvpCount`, raise `radiusMiles`, or check that `endDate` is in the future.

**The wrong city was matched.** Bare names are ambiguous — `London` resolved to London, Ontario in our measured run. Write `London, UK`, or pass `lat`/`lon` to skip geocoding entirely. `searchLocation` on every row shows what Meetup picked.

**Why do I get events unrelated to my keyword?** Meetup's search falls back to nearby events when a keyword matches little. A nonsense keyword returned 200 events in New York. Filter on `title` if you need precision.

**Can I get attendee lists or member emails?** No. Meetup keeps attendee identities behind a login, and this Actor does not log in. You get RSVP counts and the public host name plus member id.

**Can I scrape Meetup groups directly, with member counts?** Not today. Group name, URL, timezone and rating come attached to every event row (729 distinct groups in 1,124 events), but there is no standalone group search and no `memberCount`. Ask for it on the Issues tab.

**Can I scrape past events?** No — Meetup publishes upcoming events only. A past `startDate` is treated as "now".

**Two runs on a schedule — will I get duplicates?** Within a run, no: 4.4% of edges were repeats and all were dropped before billing. Across runs, dedupe on `id`, Meetup's stable event id.

**Do I get charged for duplicates or for filtered-out events?** No. You pay $0.0009 for each event actually written to your dataset. Duplicates are dropped before `pushData`, and `eventType`, `minRsvpCount` and the date window are applied by Meetup before results are ever sent.

**Does it need a residential proxy?** No. 42 of 43 requests cleared on Apify's datacenter proxy, so the datacenter rung is the default and residential would only cost you more.

**Why are some fields `null`?** Online events have no venue, most organizers set no `maxTickets`, and unrated groups have `groupRatingCount: 0`. Nulls are kept, never dropped, so your columns stay stable.

**How many events can I get from one city?** In the measured sample, between 51 and 191 per keyword+city search. For more, run several keywords across the same city with `searches`.

**Something looks wrong — how do I debug it?** Every run logs the resolved location, per-page counts, the HTTP status tally, the duplicates dropped, and the field fill measured on that run's own rows. Paste that log into an issue.

### Legal and fair use

This Actor reads only **public, unauthenticated** Meetup data — the same event pages Meetup serves a logged-out visitor via its own public GraphQL endpoint. It does not log in, does not forge or replay credentials, and does not solve CAPTCHAs. It cannot access private groups or attendee identities.

Rows contain **personal data**: `hostName` and `hostMemberId` identify the publicly listed event organizer. You are responsible for complying with **Meetup's Terms of Service** and with how you use the data — including the GDPR, CCPA, CAN-SPAM and PECR where they apply to your outreach.

Meetup® is a trademark of its owner. This Actor is an independent tool and is not affiliated with, endorsed by or sponsored by Meetup.

### Feedback

Found a missing field, or want a filter that is not here? Open an issue on the **Issues** tab — feature requests are read and acted on.

# Actor input Schema

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

What to search Meetup for — e.g. `tech`, `startup`, `yoga`, `book club`, `artificial intelligence`. A keyword is required: Meetup's search needs one, so an empty keyword scrapes nothing. Measured on 12 keyword+city searches (1,124 events), a single keyword+city returns 51–191 events.

## `location` (type: `string`):

Any place text Meetup can geocode: `New York, NY`, `Austin, TX`, `London, UK`, `Berlin, Germany`, `Toronto`. **Qualify ambiguous names** — measured live, a bare `London` resolved to London, Ontario and a bare `Berlin` resolved to Berlin, Connecticut. The resolved place is logged and stored on every row as `searchLocation`, so a bad match is visible in your data rather than hidden. Leave empty and the run defaults to New York, NY.

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

Run the search on meetup.com in your browser, copy the address bar, paste it here — e.g. `https://www.meetup.com/find/?keywords=hiking&location=us--co--Denver`. The keyword and place are read straight out of the URL, and each pasted URL is an extra search on top of the Keyword/Location above. **Only `/find/` search URLs carry a keyword**; group (`/my-group/`) and single-event URLs are not supported and the run logs a warning naming the URL it skipped.

## `eventType` (type: `string`):

Filtered by Meetup server-side, so narrowing costs you nothing extra. Measured on 1,124 events: 58.9% in-person, 40.8% online, 0.3% hybrid — online rows have no venue address, so pick In-person if you need street addresses.

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

Only events starting on or after this date. Meetup publishes **upcoming events only**, so a past date is ignored and the run logs that it used "now" instead. Leave empty for "from now on".

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

Only events starting on or before this date — e.g. set it 7 days out to build a "what's on this week" feed. A date in the past can never match anything, so the run stops with an error instead of handing you an empty dataset.

## `minRsvpCount` (type: `integer`):

Drop events below this attendance. Meetup applies it server-side, so it cuts your bill rather than adding to it. Calibration from 1,124 measured events: median RSVP count is 3, 27.5% have 10+, and only 2.7% have 50+ — a floor of 50 will leave you very few rows. 0 = no floor.

## `radiusMiles` (type: `integer`):

Distance from the resolved location's center. 0 = Meetup's own default radius, which is what the measured numbers on this page were collected with.

## `sort` (type: `string`):

How Meetup orders the result set before pagination. Relevance is Meetup's default. Soonest-first is what you want when you cap `maxResults` and only care about the next few days.

## `maxResults` (type: `integer`):

Hard stop across all searches — **this is your cost ceiling**. You are billed $0.0009 per event delivered, so 100 events = $0.09 and 1,000 = $0.90. Duplicate events are dropped before billing and never count against it. A keyword+city search yielded 51–191 events in the measured sample, so 100 usually covers one city.

## `maxResultsPerSearch` (type: `integer`):

Per keyword+location cap, so one busy city cannot eat the whole budget when you run several searches. 0 = no per-search cap (only the total above applies).

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

For running many keyword/city combinations in one go: `[{"keyword":"startup","location":"Austin, TX"},{"keyword":"yoga","location":"San Francisco, CA"}]`. These are added to — not instead of — the Keyword/Location fields above. Leave empty unless you need more than one search.

## `lat` (type: `string`):

Exact latitude, e.g. `51.45`. Supply it together with Longitude to bypass the geocoder entirely — the fix for an ambiguous city name. Overrides the Location field for every search in the run.

## `lon` (type: `string`):

Exact longitude, e.g. `-0.24`. Must be supplied together with Latitude — one without the other stops the run with an error rather than silently searching the wrong place.

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

Meetup's public GraphQL endpoint is not walled, so the cheap datacenter rung is the default: measured 2026-08-12 through Apify Proxy, 43 requests returned 42 clean 200s with a single proxy-level 502 that the fresh-session retry recovered, and 12 of 12 keyword+city searches returned events. Residential is not required and only makes a run cost more.

## Actor input object example

```json
{
  "keyword": "tech",
  "location": "New York, NY",
  "startUrls": [],
  "eventType": "ANY",
  "startDate": "",
  "endDate": "",
  "minRsvpCount": 0,
  "radiusMiles": 0,
  "sort": "RELEVANCE",
  "maxResults": 100,
  "maxResultsPerSearch": 0,
  "searches": [],
  "lat": "",
  "lon": "",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `events` (type: `string`):

Event title, date/time, description, venue, RSVP count, hosting group (name, URL, rating) and organizer.

# 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 = {
    "keyword": "tech",
    "location": "New York, NY",
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/meetup-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 = {
    "keyword": "tech",
    "location": "New York, NY",
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/meetup-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 '{
  "keyword": "tech",
  "location": "New York, NY",
  "maxResults": 100
}' |
apify call scrapersdelight/meetup-scraper --silent --output-dataset

```

## MCP server setup

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