# Meetup Scraper (`rowfeed/meetup-events-scraper`) Actor

Extract upcoming Meetup.com events for any keyword and location: title, start time, venue, hosting group, RSVP count and price. No login, no browser, no persondata. Switch to GROUPS mode for community stats instead.

- **URL**: https://apify.com/rowfeed/meetup-events-scraper.md
- **Developed by:** [Rowfeed](https://apify.com/rowfeed) (community)
- **Categories:** Social media, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 events

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

Pull upcoming **Meetup.com events** for any keyword and location as clean JSON: title, start time, venue, hosting group, RSVP count and price, no login and no browser. Built for **event aggregators and calendar apps** that need a fresh feed of local meetups, **market researchers** tracking community activity around a topic, **growth/community teams** scouting where their audience already gathers, and **AI agents and dashboards** that need a structured "what's happening near me" answer. Switch to `source: GROUPS` and get one row per Meetup group instead (name, city, member count, rating) to map the communities behind the events.

Plain HTTPS GETs against Meetup's own public Find page (`meetup.com/find/`), the same server-rendered page a browser loads, parsed from its embedded JSON - no API key, no cookies, no headless browser.

### What you get

- **Every event as one row** - id, title, URL, start time (converted to UTC), timezone, online/in-person, venue name/city/state/country, hosting group name and URL, RSVP count, price and currency, and the full description.
- **Any keyword, any location** - run several keywords in one call; Meetup's own location-slug format (`us--ny--New York`, `us--ca--San Francisco`, `gb--london--London`) picks the search area, and an unrecognized value just falls back to Meetup's nearest match instead of erroring.
- **A cost cap that actually caps** - `maxItems` stops the run once that many rows are billed, across however many keywords you listed.

### No personal data, ever

Meetup's Find page never exposes organizer names, attendee lists or member emails/phones to begin with - it's a public search result, not a member directory. This Actor only reads aggregate numbers (an RSVP *count*, a group's total *member count*), never the underlying attendee or member records the page's GROUPS query happens to embed for its own UI. If a future Meetup response ever put a person's name in a field this Actor reads, that field would be dropped, not shipped.

### Sample row

A real row from a default run (`keywords: ["python"]`, `location: "us--ny--New York"`, `source: EVENTS`):

```json
{
  "id": "316297665",
  "title": "Package Your Judgment, Keep Your Intent",
  "url": "https://www.meetup.com/nyhackr/events/316297665/",
  "start_utc": "2026-09-28T23:00:00+00:00",
  "end_utc": null,
  "timezone": "America/New_York",
  "is_online": false,
  "event_type": "PHYSICAL",
  "venue_name": "Pless Hall",
  "venue_city": "New York",
  "venue_state": "NY",
  "venue_country": "us",
  "group_name": "New York Open Statistical Programming Meetup",
  "group_url": "https://www.meetup.com/nyhackr/",
  "group_member_count": null,
  "rsvp_count": 1,
  "max_tickets": 1,
  "is_free": false,
  "price": 7.0,
  "currency": "USD",
  "description": "External registration required at nyhackr. This month we have a talk on providing judgment to agents...",
  "image_url": "https://secure.meetupstatic.com/photos/event/e/a/1/highres_535863745.jpeg",
  "rsvp_state": "JOIN_OPEN",
  "keyword": "python",
  "scraped_at": "2026-09-11T07:56:22+00:00"
}
```

`group_member_count` is always null on EVENTS rows - Meetup's event-search response doesn't include it. Switch `source` to `GROUPS` and it becomes `member_count`, populated for every row:

```json
{
  "id": "15288302",
  "name": "PyData NYC",
  "url": "https://www.meetup.com/pydatanyc",
  "city": "New York",
  "state": "NY",
  "country": "us",
  "member_count": 8007,
  "rating_average": 4.66,
  "rating_count": 366,
  "is_private": false,
  "status": "PAID",
  "description": "PyData is an educational program of NumFOCUS...",
  "keyword": "python",
  "scraped_at": "2026-09-11T07:56:33+00:00"
}
```

### Filters

| Input | Default | What it does |
|---|---|---|
| `keywords` | `["python", "startup"]` | Search terms; each is one request against Meetup's Find page. |
| `location` | `"us--ny--New York"` | Meetup location slug: `<country>--<state>--<City Name>`. |
| `source` | `"EVENTS"` | `EVENTS` for one row per event, `GROUPS` for one row per Meetup group. |
| `maxItems` | `100` | Stop after this many rows total, across all keywords. |

### Pricing

Pay per event, no subscription: **$2 per 1,000 rows** - every row is billed as one `event`, whether it is an event (`EVENTS` run) or a group (`GROUPS` run). `description` is the organizer's own unredacted text and may contain contact details the organizer chose to publish. A default run (2 keywords) typically returns 20-40 rows, a few cents. A keyword that turns up nothing near the given location produces no billed rows and no error - the run still succeeds; only a keyword whose request genuinely fails (blocked, network error) counts against it, and only zero rows *plus* at least one such failure marks the whole run failed.

### Use it from your tools

- **API and SDKs** - call it via the Apify API or the official Python/JavaScript clients: one call to start the run, one to fetch the results as JSON or CSV.
- **Schedules** - run it daily inside Apify and pipe newly-listed events into Google Sheets, a webhook or your own storage.
- **n8n, Make and Zapier** - trigger runs and feed extracted events into a workflow through Apify's integration for each.
- **AI agents and MCP** - eligible for agentic use via Apify's MCP server with pay-per-event pricing, so an agent can call it mid-task and pay only for the events it actually pulls.

### Details

- **Source**: `https://www.meetup.com/find/`, Meetup's own public search page, parsed from the `__NEXT_DATA__` JSON it embeds server-side (the same data the page renders from). No authentication, no proxies, no browser.
- **One page per keyword**: Meetup's Find page carries a real pagination cursor, but only its first results page (roughly 12-20 items) is present in the server-rendered HTML - later pages load client-side through a browser-only GraphQL call this Actor does not make. Run more keywords or a tighter location to widen coverage instead.
- **Reliability**: 429 and 5xx responses are retried with exponential backoff (5 tries); 404 and 401/403 are never retried; a 200 response without the expected embedded JSON counts as a failure, not a silent empty success.
- **Run stats**: the `STATS` record in the run's key-value store holds row/error counts and per-category request-error counts.
- **Politeness**: 0.2 s between requests, 30 s timeout, a User-Agent that names the Actor.
- **Not affiliated with Meetup.** Built against Meetup's own public search page; no login, no scraping of member-only content.

# Actor input Schema

## `keywords` (type: `array`):

Search terms to look up on Meetup's Find page (matched against event/group titles and descriptions). Each keyword is one request.

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

Where to search, in Meetup's own location-slug format "<country>--<state>--<City Name>" (e.g. "us--ny--New York", "us--ca--San Francisco", "gb--london--London"). An unrecognized value falls back to Meetup's own nearest-match location, it never errors.

## `source` (type: `string`):

EVENTS returns one row per event (title, time, venue, group, RSVP count, price). GROUPS returns one row per Meetup group (name, city, member count, rating) instead.

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

Stop after this many rows total across all keywords. Each row is one event/group event (see Pricing). Meetup's find page only renders its first results page without a browser (roughly 12-20 items per keyword), so a run with few keywords may return fewer rows than maxItems even before this cap is hit.

## Actor input object example

```json
{
  "keywords": [
    "python",
    "startup",
    "hiking"
  ],
  "location": "us--ny--New York",
  "source": "EVENTS",
  "maxItems": 100
}
```

# Actor output Schema

## `results` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("rowfeed/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("rowfeed/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 '{}' |
apify call rowfeed/meetup-events-scraper --silent --output-dataset

```

## MCP server setup

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