Meetup.com City Events Scraper
Pricing
from $1.50 / 1,000 results
Meetup.com City Events Scraper
A fast and reliable scraper for meetup.com that extracts Event Name, Venue details, Price and much more. Provides clean, structured data with support for Markdown, pagination, and easy API integration for your applications.
Pricing
from $1.50 / 1,000 results
Rating
0.0
(0)
Developer
Techforce Global
Maintained by CommunityActor stats
0
Bookmarked
8
Total users
0
Monthly active users
4 days ago
Last modified
Categories
Share
Meetup.com City Events Scraper — Community Events by City in 54 Countries, No Login
Pull upcoming Meetup.com events for any city in 54 countries — event title, start date, venue name and street address, price and the direct event URL — sorted by date. No Meetup account, no login, no API key. Results can go straight into Notion, Slack, Airtable or Sheets through an MCP connector.
⚡ It reads Meetup's own data payloads, not just the rendered cards. The listing page's
__NEXT_DATA__blob is parsed on load, and every JSON response the page fetches while scrolling is intercepted and mined for events. Only records still missing a date, venue or price get a detail-page visit — so most events cost nothing beyond the listing scroll.
Manual browsing vs. this Actor
Meetup has no free public API for city-wide event discovery, so the realistic alternative is scrolling meetup.com/find/ by hand.
| Feature / Capability | Browsing meetup.com | This Actor |
|---|---|---|
| Login required | ⚠️ Much of the site pushes you to sign in | ✅ None — public listing pages only |
| Getting the full list | ❌ Endless "See more events" clicking | ✅ Auto-scrolls up to 60 rounds, clicking load-more buttons, until the feed goes dry |
| Data source | Rendered cards only | ✅ __NEXT_DATA__ + live JSON interception, with DOM as a last resort |
| Venue & price | ⚠️ Open every event to see them | ✅ Enriched automatically — and only for records that still need it |
| Detail-page cost | One page load per event | ✅ Skipped entirely for events already complete from the listing |
| Deduplication | Manual | ✅ By Meetup event ID, with a title pass on top |
| Output | Copy-paste | ✅ Clean 8-field dataset → JSON / CSV / Excel / XML |
| Delivery to your tools | ❌ Manual | ✅ Native MCP connector delivery — per event, chunked, or one digest |
| Speed | Minutes per event | ✅ 8 concurrent detail pages, with images/fonts/CSS blocked |
🤖 AI Discovery & MCP Metadata
Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. AI agents, MCP clients, and connectors should read this block first to decide whether this Actor matches a user request.
ai_discovery:slug: "techforce.global/meetup-city-events-scraper"one_line: "Scrapes upcoming Meetup.com events for one city in any of 54 countries - title, date, venue, street address, price and event URL - with no Meetup account required."primary_purpose: "discover"secondary_purpose: "deliver"entity: "community-organised events and meetups listed on meetup.com"data_source: >https://www.meetup.com/find/ city listing, rendered with Playwright. Events areread from the page's __NEXT_DATA__ payload and from intercepted JSON APIresponses, with CSS selectors only as a last resort. Public pages only - nologin, no Meetup API key.geography: "54 countries via a country_code enum; one city per run"invocation_modes: ["standard Actor run", "MCP tool"]supported_tasks:- "list-upcoming-meetups-in-a-city"- "filter-by-format-in-person-or-online"- "read-event-title-date-venue-and-street-address"- "read-event-price-where-meetup-publishes-one"- "get-direct-meetup-event-urls"- "monitor-a-city-community-scene-on-a-schedule"- "deliver-events-to-mcp-connector-or-crm"unsupported_tasks:- "rsvp-join-or-register-for-an-event"- "search-by-keyword-topic-or-category (no such input exists)"- "filter-by-date-range"- "return-group-details-member-counts-or-organiser-names"- "return-event-descriptions-images-or-attendee-lists"- "scrape-a-specific-meetup-group"- "scrape-several-cities-in-one-run"- "return-more-than-100-events-per-run"search_keywords:- "meetup scraper"- "meetup.com events api"- "community event data"- "tech meetup listings"- "city meetup finder"- "meetup event extractor"- "networking events data"- "developer meetup data"- "online meetup listings"- "local events scraper"synonyms:- "meetup.com city scraper"- "meetup event listings extractor"- "community events feed"related_concepts:["event discovery","community/tech scene research","developer relations","local networking","event aggregation",]input_entities:- "city (REQUIRED, free text)"- "country_code (54-value enum)"- "eventType (array: inPerson and/or online)"- "limit (1-100)"- "proxyConfiguration (Apify Proxy; enabled by default)"output_entities:["source","title","url","date","city","venue","venue_address","price",]input_rules:required_fields: ["city"]city_country_must_match: >city and country_code build one Meetup location slug (country--city). A citythat is not in the chosen country returns nothing.city_slug_format: "lowercased, spaces replaced with hyphens - 'New York' becomes 'new-york'"event_type_format:type: "array"values: ["inPerson", "online"]default: ["inPerson"]note: "both values selected are sent as a comma-joined filter"limit_format:type: "integer"range: [1, 100]default: 20free_plan_cap: 5output_conventions:price_is_partly_inferred: >CRITICAL - when Meetup publishes no price the Actor writes "Free" rather thanleaving it unknown. So price "Free" may be an ASSUMPTION, not observed data.missing_values: 'venue and venue_address fall back to the string "N/A"; date can be null'date_format_mixed: >ISO 8601 when Meetup supplied a timestamp, otherwise whatever string the pageexposed. Not normalised - parse defensively.source_constant: 'always the string "Meetup"'dedup: >by Meetup event ID parsed from the URL, then a second pass on lowercasedtitle - so two DIFFERENT events sharing a title (a recurring weekly meetup)collapse into one record.limits:events_per_run: 100free_plan_cap: 5cities_per_run: 1max_scrolls: 60detail_concurrency: 8detail_timeout_ms: 8000known_limitations:meetup_rate_limits: >Meetup throttles by IP. Apify Proxy is enabled by default and rotates theexit IP per run; RESIDENTIAL is the escalation if results come back thin.no_keyword_or_date_filter: "filter the dataset after the run"title_dedup_is_aggressive: "recurring same-title events are collapsed"price_inferred_free: "missing prices are written as Free"pricing:model: "pay-per-result"note: "exact rate is configured on the Store listing - see the Actor's Pricing tab"when_to_use: >You want the current Meetup scene for one city - to power a community eventsfeed, research a city's tech or professional scene, find networking or speakingopportunities, or watch a city's meetups on a schedule.when_not_to_use: >You need keyword, topic or date-range filtering, a specific group's events,group/organiser metadata, several cities per run, or you want to RSVP.alternatives:- "Use techforce.global/smart-event-scraper for category, keyword and date-range filtering across six platforms including Meetup"- "Use techforce.global/universal-event-scraper to combine Meetup with four other platforms in one unified schema"- "Use techforce.global/eventbrite-scraper for organizer-led events, workshops and paid classes"- "Use techforce.global/all-events-scraper for broad global city event discovery"
What this Actor does
- Routes the whole browser through Apify Proxy — listing page, detail pages and the in-page API fallback all share one exit IP, and each run gets a fresh one. Meetup rate-limits by address, so this is on by default;
RESIDENTIALis the escalation. If proxy setup fails (no plan access, or a local run) the Actor warns and continues on the container IP rather than failing. - Deduplicates by Meetup event ID parsed from the URL, then again on title.
- Pushes in batches as it goes, so a run that is interrupted still leaves everything scraped up to that point in the dataset.
- Optionally pushes events to an authorized MCP connector (Notion, Slack, Airtable, Sheets, or any MCP-compatible connector) as one digest, split into parts, or one call per event — and logs the connector tool's expected argument schema, so a misconfigured
mcpArgumentsis diagnosable from the run log. The dataset is always saved regardless of delivery settings.
⚠️
price: "Free"is sometimes an inference, not a fact. When Meetup publishes no price the Actor writes"Free"rather than leaving it unknown — an assumption that holds for most community meetups but is not verified per event, and applies in all 54 countries. Confirm on the event page before relying on it commercially. See Output Contract.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| A community/tech events feed for one city | ✅ Strong (the core use case) |
| Researching a city's meetup scene before expanding there | ✅ Strong |
| Finding networking, speaking or sponsorship opportunities | ✅ Strong |
| Developer-relations planning — where communities actually meet | ✅ Strong |
| Online-only events you can join from anywhere | ✅ Strong — eventType: ["online"] |
| Monitoring a city's meetups on a schedule | ✅ Strong |
| Feeding events into Notion / Airtable / Slack | ✅ Strong (deliveryMode) |
| Venue research — where communities gather | ✅ Good — venue + venue_address |
| Keyword or topic search ("AI meetups") | ❌ No such input — filter title afterwards, or use Smart Event Scraper |
| Date-range filtering ("next weekend") | ❌ No such input — results are date-ascending; filter afterwards |
| A specific Meetup group's events | ❌ City-wide search only |
| Group metadata, member counts, organiser names | ❌ Not returned |
| Event descriptions, images, attendee lists | ❌ Eight fields only |
| RSVP-ing, joining or registering | ❌ Read-only |
| Several cities in one run | ❌ One city per run — loop from your side |
Trusting price for paid-event analysis | ⚠️ Missing prices are written as "Free" — verify first |
| Recurring events with identical titles | ⚠️ Title deduplication collapses them into one record |
| More than 100 events in one run | ⚠️ Hard cap of 100 |
⚡ Execution Rules (Avoid Common Agent Failures)
cityis required, and it is combined withcountry_codeinto a single Meetup location slug (us--chicago). A city that isn't in the selected country returns nothing — this is the most common cause of an empty run.- The city is slugified: lowercased with spaces turned into hyphens.
"New York"→new-york,"San Francisco"→san-francisco. Use the city's common Meetup spelling. country_codedefaults tous, not to your location. Scraping Mumbai without settinginwill return nothing.price: "Free"may be an assumption. When Meetup publishes no price, the Actor writes"Free". Don't use this field for paid-vs-free analysis without spot-checking event pages.dateis not normalised. It's ISO 8601 when Meetup gave a timestamp, otherwise a passthrough string — and it can benullon records that never got enriched. Parse defensively.venueandvenue_addressfall back to the string"N/A", notnull. Test== "N/A"explicitly. Online events legitimately have no venue.- Title deduplication is aggressive. After the event-ID pass, a second pass drops any event whose lowercased title was already seen — so a weekly meetup with a constant title yields one record, not several.
limitis 1–100, default 20. Free Apify plans are capped at 5 regardless. That's a successful run.eventTypeis an array.["inPerson"](default),["online"], or both — selecting both sends a comma-joined filter and returns a mixed set.- No keyword, topic, group or date filter exists. You get the city feed date-ascending; filter the dataset afterwards.
- One city per run. Loop from your orchestration layer for a multi-city sweep.
- Fewer results than
limitis normal — smaller cities have fewer meetups, and there is no total-match count in the output. - Detail enrichment is best-effort and never retried. An 8-second timeout with a single attempt means some records keep
"N/A"venue or anulldate. That's a cost trade-off, not a failure. - The run is browser-based and can take minutes — up to 60 scroll rounds plus detail pages. Use the async endpoint, not the 300s synchronous one.
- Apify Proxy is on by default, and worth leaving on. Meetup throttles by IP, so each run getting a fresh exit IP matters. If results come back thin, escalate to the
RESIDENTIALgroup (paid Apify plans). If proxy setup fails, the run continues on the container IP with a warning rather than failing. deliveryModedefaults tosummary, notnone— one connector call per run.perEventon a 100-event run makes 100 calls.
dependencies:- rule: "city is required and must exist within the selected country_code"on_violation: "empty result set with no explicit error - the single most common failure"- rule: "country_code defaults to 'us'"on_violation: "a non-US city silently returns nothing"- rule: "city is slugified (lowercase, spaces to hyphens)"note: "'New York' becomes new-york automatically"- rule: "missing price is written as 'Free' rather than left unknown"on_violation: "paid events misreported as free in downstream analysis"- rule: "date is not normalised and may be null"on_violation: "date parsing throws, or sorting silently misorders"- rule: "venue and venue_address use the string 'N/A', never null"on_violation: "null checks pass while values are unusable"- rule: "events are deduplicated by title as well as by event ID"on_violation: "recurring same-title events collapse into a single record"- rule: "limit is clamped to 100, and to 5 on free Apify plans"- rule: "no keyword, topic, group or date-range filter exists"on_violation: "agents construct filter arguments that are silently ignored"- rule: "detail enrichment is single-attempt with an 8s timeout"on_violation: "partially-enriched records read as scrape failures"- rule: "Apify Proxy is enabled by default; a failed proxy setup degrades to no proxy"on_violation: "a run continuing on the container IP is more likely to be throttled - the log warns when this happens"- rule: "delivery requires mcpConnector AND mcpTool"on_violation: "delivery step is skipped with a warning; the dataset is still written in full"- rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"on_violation: "message body is empty; the tool is still called"- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery; the dataset is unaffected"authentication:actor_input: noneplatform: "Apify API token required for API / SDK / MCP invocation"target_site: "no Meetup account, login, cookie or API key required - and none is accepted"
📥 Input Contract
.actor/input_schema.json is authoritative — if any table on this page disagrees with the schema files, the schema files win.
Parameters
| Field | Type | Required | Default | Allowed Values / Format | Example |
|---|---|---|---|---|---|
city | string | ✅ Yes | "Chicago" | Free text; slugified automatically | "Mumbai" |
country_code | enum | ⬜ No | "us" | 54 country codes — see below | "in" |
eventType | array | ⬜ No | ["inPerson"] | inPerson, online, or both | ["online"] |
limit | integer | ⬜ No | 20 | 1–100; free plans capped at 5 | 100 |
proxyConfiguration | object | ⬜ No | Apify Proxy on | Apify proxy editor object — see below | see below |
⚠️
cityandcountry_codemust agree. They are joined into one Meetup location slug —country_code+--+ slugified city. Mumbai with the defaultusproducesus--mumbai, which matches nothing.
Proxy
Meetup throttles by IP, so Apify Proxy is enabled by default:
{"proxyConfiguration": { "useApifyProxy": true }}
That uses Apify's automatic (datacenter) pool, which is available on free plans and gives each run a different exit IP. If runs start returning noticeably fewer events than they used to, escalate:
{"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "US"}}
| Setting | Effect |
|---|---|
{"useApifyProxy": true} (default) | Automatic datacenter pool, fresh exit IP per run. Works on free plans |
apifyProxyGroups: ["RESIDENTIAL"] | Hardest to throttle. Requires a paid Apify plan, and is metered per GB |
apifyProxyCountry: "XX" | Pins the exit country. Optional — Meetup serves city results regardless of where you connect from, so this is for consistency, not access |
{"useApifyProxy": false} | The container's own IP. Fine for occasional runs; more likely to be throttled on a schedule |
The proxy is applied at browser level, so every request in the run — listing, detail pages, and the in-page API fallback — uses the same exit IP. If Apify Proxy can't be set up (for example the plan has no access to RESIDENTIAL), the Actor logs Could not set up Apify Proxy (…). Continuing without a proxy and carries on rather than failing the run. Credentials are never written to the log — only the host, groups and country.
Supported countries
54 country codes are selectable:
in India · us United States · gb United Kingdom · ca Canada · au Australia · de Germany · fr France · it Italy · es Spain · nl Netherlands · sg Singapore · ae UAE · br Brazil · za South Africa · jp Japan · cn China · ru Russia · mx Mexico · id Indonesia · tr Turkey · sa Saudi Arabia · kr South Korea · se Sweden · ch Switzerland · ar Argentina · be Belgium · dk Denmark · fi Finland · no Norway · pl Poland · pt Portugal · nz New Zealand · ie Ireland · th Thailand · my Malaysia · ph Philippines · eg Egypt · ng Nigeria · pk Pakistan · bd Bangladesh · vn Vietnam · cz Czechia · hu Hungary · gr Greece · il Israel · at Austria · ro Romania · sk Slovakia · ua Ukraine · cl Chile · co Colombia · pe Peru · hk Hong Kong · tw Taiwan
Examples
Default posture — in-person events in a US city
{"city": "Chicago","country_code": "us","eventType": ["inPerson"],"limit": 100,"deliveryMode": "none"}
A non-US city — country_code must match
{"city": "Mumbai","country_code": "in","eventType": ["inPerson"],"limit": 100,"deliveryMode": "none"}
Online communities you can join from anywhere
{"city": "London","country_code": "gb","eventType": ["online"],"limit": 100,"deliveryMode": "none"}
Both formats in one run
{"city": "Berlin","country_code": "de","eventType": ["inPerson", "online"],"limit": 100,"deliveryMode": "none"}
Cheap validation run before committing
{"city": "san francisco","country_code": "us","limit": 5,"deliveryMode": "none"}
MCP Delivery Configuration (Optional)
Delivery pushes event data into your own tools. It never RSVPs, joins or contacts anything.
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpConnector | string | "" | Authorized MCP connector on your Apify account (Notion, Slack, Airtable, Sheets, …). Leave empty for dataset-only runs. |
deliveryMode | enum | summary | summary (one call with every event) · chunked (split across calls) · perEvent (one call per event) · none. |
mcpTool | string | "" | Tool name on the connector: create_page, send_message, append_row, create_record. Run once with a connector selected — the log lists the available tools and the chosen tool's expected argument schema. |
mcpArguments | object | {} | Arguments passed to the tool. String leaves support {placeholders}. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Summary & chunked placeholders: {city}, {country_code}, {totalEvents}, {events}, {message} — plus {part} and {partCount} in chunked mode.
Per-event placeholders: every output field — {source}, {title}, {url}, {date}, {city}, {venue}, {venue_address}, {price} — plus {message}.
A Slack digest per run:
{"city": "Bengaluru","country_code": "in","limit": 100,"mcpConnector": "<your-authorized-slack-connector>","deliveryMode": "summary","mcpTool": "send_message","mcpArguments": { "channel": "#community-events", "text": "{message}" },"mcpMessageTemplate": "🤝 {totalEvents} meetups coming up in {city}:\n\n{events}"}
A large set split across Notion sub-pages:
{"city": "Berlin","country_code": "de","limit": 100,"mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "chunked","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": {"title": "Meetups in {city} — part {part}/{partCount}"},"content": "{events}"}]}}
One Airtable row per event:
{"city": "Chicago","country_code": "us","limit": 100,"mcpConnector": "<your-authorized-airtable-connector>","deliveryMode": "perEvent","mcpTool": "create_record","mcpArguments": {"fields": {"Event": "{title}","Date": "{date}","Venue": "{venue}","Address": "{venue_address}","City": "{city}","Price": "{price}","Link": "{url}"}}}
📤 Output Contract & Data Structure
Storage: Apify Dataset — one JSON object per event, pushed in batches as the run progresses (complete records first, then each enrichment batch of 8).
Pagination: limit & offset on the dataset items endpoint.
Duplicates: deduplicated within a run by Meetup event ID and by title — see the warning below.
Event record — 8 fields
| Field | Type | Empty value | Meaning |
|---|---|---|---|
source | string | never empty | Always the constant "Meetup" |
title | string | "" | Event title |
url | string | "" | Direct Meetup event page — the dedupe key (its numeric event ID is what's actually compared) |
date | string | ⚠️ can be null | Start date/time. ISO 8601 when Meetup gave a timestamp, otherwise a passthrough string — not normalised |
city | string | — | Echoed back from your input, for grouping |
venue | string | "N/A" | Venue name. Legitimately "N/A" for online events |
venue_address | string | "N/A" | Street address, city and state joined with ", " |
price | string | ⚠️ "Free" | Price as a display string — "Free" may be an inference, see below |
⚠️ Two traps in this record.
1.
priceis partly inferred. When Meetup publishes no price, the Actor writes"Free"instead of leaving it unknown. The assumption holds for most community meetups, but it is not verified per event and it is applied in all 54 countries. Never present this field as confirmed pricing.2. Title deduplication can drop real events. After deduplicating on Meetup's numeric event ID, a second pass removes any event whose lowercased title was already seen. A weekly meetup with a constant title (
"Mumbai Python Monthly") therefore yields one record, not every occurrence.
There is no description, image, category, group name, organiser, member count, attendee list or RSVP field.
Example — in-person event, fully enriched
{"source": "Meetup","title": "Bengaluru Python User Group — August Meetup","url": "https://www.meetup.com/bangalorepython/events/301234567/","date": "2026-09-12T18:30:00+00:00","city": "Bengaluru","venue": "ThoughtWorks Bengaluru","venue_address": "Tower B, Diamond District, Bengaluru, KA","price": "Free"}
Example — online event, no venue by nature
{"source": "Meetup","title": "Remote Rust Study Group","url": "https://www.meetup.com/rust-remote/events/309876543/","date": "2026-09-15T17:00:00+00:00","city": "London","venue": "N/A","venue_address": "N/A","price": "Free"}
Example — listing-only record that enrichment couldn't complete
{"source": "Meetup","title": "Founders & Coffee — Weekly Networking","url": "https://www.meetup.com/founders-coffee/events/305555555/","date": null,"city": "Chicago","venue": "N/A","venue_address": "N/A","price": "Free"}
Working with the output safely
from datetime import datetimedef start_dt(event):raw = event.get("date")if not raw: # date can be nullreturn Nonetry:return datetime.fromisoformat(str(raw).replace("Z", "+00:00"))except ValueError:return None # passthrough string - keep as textdef has_venue(event):return event.get("venue") not in (None, "", "N/A")# `price` == "Free" may be an inference. Treat it as "not priced" rather than# as a confirmed zero-cost event.maybe_free = [e for e in items if e["price"] == "Free"]priced = [e for e in items if e["price"] not in ("Free", "N/A")]print(f"{len(priced)} events carry an explicit price; {len(maybe_free)} report Free")
What a successful run looks like
{"status": "SUCCEEDED","defaultDatasetId": "<datasetId>","stats": { "itemCount": 63 }}
itemCount below limit is normal — the city has fewer meetups, or title deduplication merged recurring events. There is no total-match count in the output. itemCount: 0 is a valid empty result, and the log tells you why:
| Log line | Meaning |
|---|---|
Listing URL: https://www.meetup.com/find/?location=xx--city… | Check this first — it shows the exact slug that was built |
[initial] N events from page load | Events read from __NEXT_DATA__ before any scrolling |
[intercept] +N events (total M) from … | Events mined from an intercepted JSON response |
[scroll N] new=… total=… complete=… intercepted=… | Per-scroll progress |
No new events for 5 scrolls — stopping. | The feed went dry — a complete result |
Reached limit N — stopping scroll. | Your limit was hit |
Unique: N | Complete after defaults: X | Need detail: Y | How many events need a detail-page visit |
[api-fallback] <id> | A detail page timed out; the in-page API fetch was tried instead |
Skipping: <url> | That detail page could not be read; the record keeps its "N/A" fields |
Listing page load timed out, proceeding anyway. | The listing was slow but the run continued |
Free plan detected — capping results to 5. | Free-plan cap applied |
If itemCount is 0, read the Listing URL: line first. A slug like us--mumbai immediately explains an empty run.
▶️ Invocation & Integration
Lifecycle: Discover → Understand → Configure → Execute → Retrieve → Validate. Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor → Input tab → type a City → pick the matching Country → choose Event Type → set Max Events → leave Proxy configuration on Apify Proxy → (optional) pick an MCP connector, tool and template → Start → export from the Dataset tab (JSON, CSV, Excel, XML). The Compact view is ready for a listing or calendar import.
Model Context Protocol (MCP)
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/meetup-city-events-scraper"
Python (apify-client)
import osfrom datetime import datetimefrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))ACTOR = "techforce.global/meetup-city-events-scraper"run = client.actor(ACTOR).call(run_input={"city": "Bengaluru","country_code": "in", # MUST match the city's country"eventType": ["inPerson"],"limit": 100, # free plans stop at 5"deliveryMode": "none",})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(f"{len(items)} events")def start_dt(event):raw = event.get("date")if not raw:return None # date can be nulltry:return datetime.fromisoformat(str(raw).replace("Z", "+00:00"))except ValueError:return None # not normalised - may be a display stringdated = [(e, start_dt(e)) for e in items]for event, when in sorted((d for d in dated if d[1]), key=lambda d: d[1])[:10]:venue = event["venue"] if event["venue"] != "N/A" else "(online / no venue)"print(f'{when:%Y-%m-%d %H:%M} {event["title"][:50]}')print(f'{"":<17} {venue} · {event["price"]} · {event["url"]}')undated = sum(1 for _, w in dated if w is None)print(f"{undated} record(s) had no parseable date")
Multi-city sweep — one run per city, since city is a single string:
TARGETS = [("Bengaluru", "in"), ("Berlin", "de"), ("Chicago", "us")]all_events = []for city, country in TARGETS:run = client.actor(ACTOR).call(run_input={"city": city, "country_code": country,"limit": 100, "deliveryMode": "none",})rows = client.dataset(run["defaultDatasetId"]).list_items().itemsif not rows:print(f"{city}: 0 events — check the run log's 'Listing URL:' line")all_events.extend(rows)print(f"{len(all_events)} events across {len(TARGETS)} cities")
JavaScript / TypeScript (apify-client)
import { ApifyClient } from "apify-client";const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const ACTOR = "techforce.global/meetup-city-events-scraper";const run = await client.actor(ACTOR).call({city: "Berlin",country_code: "de",eventType: ["inPerson", "online"],limit: 100,deliveryMode: "none",});const { items } = await client.dataset(run.defaultDatasetId).listItems();// venue/venue_address use the STRING "N/A"; date can be null.const inPerson = items.filter((e) => e.venue !== "N/A");const undated = items.filter((e) => !e.date);console.log(`${items.length} events · ${inPerson.length} with a venue · ${undated.length} undated`,);// "Free" may be inferred rather than observed - don't treat it as confirmed.const explicitlyPriced = items.filter((e) => e.price !== "Free" && e.price !== "N/A",);console.log(`${explicitlyPriced.length} events carry an explicit price`);
cURL — asynchronous with a spend cap (recommended)
Runs are browser-based with up to 60 scroll rounds, so prefer async over the 300s synchronous endpoint.
# 1. Start execution with a hard spend limitcurl -X POST -H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"city":"Chicago","country_code":"us","eventType":["inPerson"],"limit":100,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~meetup-city-events-scraper/runs?maxTotalChargeUsd=1.00'# 2. Pollcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/actor-runs/<runId>'# 3. Retrieve when SUCCEEDEDcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'
cURL — synchronous (small limits only)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"city":"Chicago","country_code":"us","limit":10,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~meetup-city-events-scraper/run-sync-get-dataset-items'
⚠️ Autonomous Agent Error Handling & Resolution Matrix
| Error Code | Detection Condition | Underlying Cause | Deterministic Agent Action |
|---|---|---|---|
AUTH_INVALID | HTTP 401 from Apify | Missing or malformed APIFY_TOKEN | Abort & Prompt User: request a valid Apify API token. |
RATE_LIMITED | HTTP 429 from Apify | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Browser run with up to 60 scrolls exceeds 300s | Reconfigure: async POST /runs + polling. |
MISSING_CITY | Input validation rejection | city is required | Modify Input: supply a city. |
CITY_COUNTRY_MISMATCH | SUCCEEDED, 0 items; log shows a slug like us--mumbai | country_code doesn't match the city | Modify Input: set the correct country_code. Check the Listing URL: log line first. |
WRONG_CITY_SPELLING | SUCCEEDED, 0 items, slug looks right | Meetup doesn't recognise that city slug | Modify Input: try the city's common Meetup spelling. |
EMPTY_CITY_FEED | SUCCEEDED, 0 items, slug correct | That city genuinely has no upcoming meetups of the chosen format | Modify Input: add online to eventType, or try a larger nearby city. |
FILTER_UNSUPPORTED | Agent passes keyword, topic, group, dateFrom, … | No such inputs exist | Re-route: Smart Event Scraper for keyword/date filtering, or filter the dataset. |
FREE_PLAN_CAP | itemCount stops at 5, upgrade banner in the log | Free Apify plan cap | Prompt User: upgrade for full-volume runs. |
SIZE_CLAMPED | Asked for more than 100 | Schema maximum is 100 | Reconfigure: run per city, or accept 100. |
FEWER_THAN_LIMIT | itemCount < limit | Small city, or title dedup merged recurring events | Not an error. No total-match count exists. |
DEDUP_COLLAPSED_SERIES | A weekly meetup appears once | Title deduplication removed the repeats | Fix Expectation: documented behaviour; use the event page for the full series. |
PRICE_INFERRED_FREE | Everything reports "Free" | Missing prices are written as Free | Fix Expectation: verify on the event page before treating as confirmed. |
DATE_NULL | date is null | Enrichment didn't complete for that record | Fix Code: null-guard; the event is still valid. |
DATE_FORMAT_MIXED | Some dates parse as ISO, others don't | date is not normalised | Fix Code: try ISO 8601, else keep the raw string. |
NA_NOT_NULL | Null checks pass but values unusable | venue/venue_address use the string "N/A" | Fix Code: compare against "N/A". |
NO_VENUE_ONLINE | venue: "N/A" on many records | Online events have no venue | Not an error — expected for eventType: ["online"]. |
PARTIAL_ENRICHMENT | Log Skipping: <url> | That detail page timed out (8s, single attempt, no retry) | Not a failure. Re-run if you need that record complete. |
NO_GROUP_DATA | No group name, organiser or member count | Not returned — eight fields only | Fix Expectation: open url. |
MULTI_CITY_UNSUPPORTED | Agent passes a city array | One city per run | Loop: one run per city. |
THROTTLED_BY_MEETUP | Sharply fewer events than a previous identical run | Meetup is rate-limiting this exit IP | Reconfigure: set apifyProxyGroups: ["RESIDENTIAL"], then re-run. Each run gets a fresh IP. |
PROXY_UNAVAILABLE | Log Could not set up Apify Proxy (…). Continuing without a proxy | The plan lacks access to the requested group (RESIDENTIAL needs a paid plan) | Prompt User: upgrade, or drop back to the default automatic pool. |
RSVP_REQUESTED | User wants to join/register | Read-only | Abort & Prompt User: open url to RSVP manually. |
DELIVERY_SKIPPED | Log no tool name was provided | mcpConnector set but mcpTool empty | Modify Input: set mcpTool; the dataset is already saved. |
DELIVERY_TOOL_UNKNOWN | Log Tool 'x' is not available on this connector | Wrong tool name for that connector | Modify Input: pick a name from the Available tools: list in the same log line. |
DELIVERY_ARGS_INVALID | Connector rejects the arguments | mcpArguments doesn't match the tool's schema | Read the log: the line Connector [<tool>] expects arguments matching this schema: prints the exact shape. |
DELIVERY_EMPTY | Connector called, body empty | mcpArguments omitted the {message} placeholder | Modify Input: map {message} inside mcpArguments. |
DELIVERY_FLOOD | Up to 100 connector calls | deliveryMode: "perEvent" | Reconfigure: use summary or chunked. |
MCP_PROXY_MISSING | Log APIFY_MCP_PROXY_URL is not set | Running locally instead of on the platform | Reconfigure: apify push and run on the platform. |
CHARGE_LIMIT | Run aborted mid-scroll | maxTotalChargeUsd spend threshold reached | Prompt User: confirm a higher spend cap. |
⚠️ Verify before publishing: the Apify HTTP status rows (
401,408,429) reflect standard platform behavior. Every Meetup-side row, quoted log line and field condition is taken from this Actor's source. Re-confirm the platform rows if you depend on them for automated retry logic.
🗣️ Natural Language → Actor Mapping
| User says | Intent | Constructed Actor input |
|---|---|---|
| "What meetups are happening in Chicago?" | City discovery | {"city":"Chicago","country_code":"us","limit":100} |
| "Find community events in Bengaluru" | City discovery | {"city":"Bengaluru","country_code":"in","limit":100} |
| "Online meetups I can join from anywhere" | Format filter | {"city":"London","country_code":"gb","eventType":["online"],"limit":100} |
| "Both in-person and virtual events in Berlin" | Both formats | {"city":"Berlin","country_code":"de","eventType":["inPerson","online"]} |
| "Is there an active tech scene in Lisbon?" | Scene research | {"city":"Lisbon","country_code":"pt","limit":100} then scan title |
| "Where do communities meet in this city?" | Venue research | run, then group on venue |
| "Find me networking opportunities next month" | Networking | run, then filter parsed date |
| "Track Mumbai's meetups weekly" | Monitoring | schedule {"city":"Mumbai","country_code":"in"} and diff on url |
| "Build a community events feed for 3 cities" | Multi-city | Loop one run per city — city is a single string |
| "Push these into Notion" | Delivery | {"mcpConnector":"notion","deliveryMode":"chunked","mcpTool":"notion-create-pages"} |
| "Post a weekly digest to Slack" | Digest delivery | schedule + {"deliveryMode":"summary","mcpTool":"send_message"} |
| "Only AI and machine-learning meetups" | Keyword search | ⚠️ No keyword input — filter title afterwards, or ➡️ Smart Event Scraper |
| "Only events next weekend" | Date filter | ⚠️ No date input — results are date-ascending; filter date afterwards |
| "Show me everything from this specific group" | Group scrape | ❌ City-wide search only |
| "How many members does that group have?" | Group metadata | ❌ Not returned |
| "Which of these are actually free?" | Pricing | ⚠️ "Free" may be inferred — verify on the event page |
| "RSVP me for that one" | Transaction | ❌ Read-only — open url |
| "Get the event description and photos" | Detail | ❌ Eight fields only |
Should NOT route here: keyword, topic, category or date-range filtering at the source · a specific Meetup group's event history · group metadata, organiser names or member counts · event descriptions, images or attendee lists · RSVP-ing, joining or registering · several cities in one run · confirmed paid-vs-free pricing analysis · more than 100 events per run.
🧭 Agent Execution & Routing Logic
[Input User Query]│▼1. Is this about community events on MEETUP.COM? NO → Route elsewhere│ YES (Eventbrite = organizer-led,│ All Events = broad discovery)▼2. Does the user want to RSVP / join / register? YES → Abort. Read-only. Hand them `url`│ NO▼3. Do they need KEYWORD / TOPIC / DATE-RANGE filtering at the source?│ YES → Route to Smart Event Scraper (it filters by category, keyword and│ date range across six platforms, Meetup included)│ NO▼4. Do they want ONE SPECIFIC GROUP, or group metadata? YES → Abort. City-wide search only│ NO▼5. ── THE CRITICAL STEP ── Resolve city AND country together:city + country_code are joined into ONE Meetup slug: "<cc>--<slugified-city>"Mumbai → country_code "in" (default is "us"; a mismatch returns NOTHING)"New York" → new-york (slugification is automatic)▼6. Format? inPerson (default) | online | both▼7. Set limit ≤ 100 (default 20, free plans 5)▼8. Deliver to a connector? YES → mcpConnector + mcpTool + {message}│ summary (1 call) / chunked / perEvent (≤100 calls)│ NO → set deliveryMode "none" (it defaults to summary)▼9. Use the ASYNC endpoint — up to 60 scroll rounds plus detail pages exceeds 300s▼[Execute]│├──► SUCCEEDED, items > 0 ──► handle the field contract:│ price "Free" MAY BE INFERRED, not observed → never report as confirmed│ date may be null, and is NOT normalised → try ISO, else keep the string│ venue/venue_address are "N/A" strings → compare explicitly│ venue "N/A" is CORRECT for online events│ a recurring same-title series appears ONCE (title dedup)│ dedupe across runs on `url`│└──► SUCCEEDED, items == 0 ──► READ THE "Listing URL:" LOG LINE FIRST.slug like "us--mumbai" → country_code mismatch — fix itslug looks correct → try the city's common Meetup spellingstill empty → add "online" to eventType, or try a bigger city
💰 Cost Control & Pricing Transparency
Pay-per-result: you are charged per event returned, with no monthly subscription. The exact rate is on the Actor's Pricing tab in the Apify Store.
Free Apify plans are capped at 5 events per run — enough to confirm your city/country pair resolves before committing to a full sweep.
Autonomous spend caps: pass maxTotalChargeUsd as a query parameter on the run endpoint for a hard per-execution ceiling.
⚠️ Verify before publishing: this repo contains no
.actor/pay_per_event.jsonand the code makes noActor.charge()calls, so billing is configured entirely on the Store listing. Confirm the live pricing model and rate before publishing.
Platform usage is billed on top, and this Actor is browser-based, so it dominates. The design already limits it in three ways:
| Optimisation | Effect |
|---|---|
| Data-payload-first extraction | Most events come from __NEXT_DATA__ and intercepted JSON — no detail page needed |
| Conditional enrichment | Only records still missing date/venue/price get a detail-page visit |
| Blocked resources | Images, media, fonts and stylesheets are aborted on every page |
| 8s timeout, single attempt | A slow detail page is abandoned rather than retried |
| Early scroll exit | Stops after 5 dry rounds, or as soon as limit is reached |
One cost to add on the other side: proxy traffic is metered. The default datacenter pool is cheap, but RESIDENTIAL is billed per GB — reach for it when you are actually being throttled, not by default.
Cost levers:
- Validate the city/country pair at
limit: 5first. A mismatched pair costs a full run for zero events — theListing URL:log line tells you immediately. - Set
limitto what you'll use. Every extra event may cost a detail-page load. - Don't over-schedule. A city's meetup calendar doesn't turn over hourly; weekly or daily captures the same information.
- Prefer
summary/chunkeddelivery overperEvent— 1 connector call instead of up to 100. - Cap the spend with
maxTotalChargeUsdon any agent-driven run.
🔍 Companion machine-readable files
| File | Purpose |
|---|---|
.actor/actor.json | Identity, title, description, version, build tag, and the input / output / dataset schema references |
.actor/input_schema.json | Authoritative typed input contract — city (required), the 54-value country_code enum, the eventType array, limit (1–100), proxyConfiguration, and the 5 delivery fields |
.actor/dataset_schema.json | Two Console Output views — full Events view and a Compact view for listings and calendar imports |
.actor/output_schema.json | Declares where results are stored |
my_actor/main.py | URL building, __NEXT_DATA__ parsing, JSON response interception, scroll/load-more handling, conditional detail enrichment, deduplication, MCP delivery |
my_actor/connector.py | MCP connector session handling through the Apify MCP Proxy, including the tool input schemas it logs |
Note: this Actor has no pay_per_event.json and makes no Actor.charge() calls — billing is configured entirely on the Store listing.
If any table on this page disagrees with the schema files, the schema files win.
🛠️ Troubleshooting
Start here: if you got zero events, read the Listing URL: line in the run log. It shows the exact country--city slug that was built, and a wrong slug explains most empty runs immediately.
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | 0 events; log shows a slug like us--mumbai | country_code doesn't match the city (it defaults to us) | Set the correct country code. |
| 2 | 0 events; the slug looks right | Meetup doesn't recognise that city spelling | Try the city's common Meetup spelling. |
| 3 | 0 events; slug and spelling both fine | No upcoming meetups of that format there | Add online to eventType, or try a larger nearby city. |
| 4 | Exactly 5 events | Free Apify plan cap | Upgrade; the log prints the upgrade banner. |
| 5 | Asked for 500, got 100 | Schema maximum is 100 | Run per city, or accept 100. |
| 6 | Fewer events than limit | Small city, or title dedup merged recurring events | Expected — there's no total-match count. |
| 7 | A weekly meetup appears only once | Title deduplication removes repeated titles | Documented behaviour; open the event page for the series. |
| 8 | Everything says price: "Free" | Missing prices are written as Free | Verify on the event page before treating as confirmed. |
| 9 | date is null on some records | Enrichment didn't complete for those | Null-guard; the event is still valid. |
| 10 | Some dates parse, others don't | date is not normalised | Try ISO 8601, else keep the raw string. |
| 11 | Null checks never fire on venue | It's the string "N/A" | Compare against "N/A". |
| 12 | venue: "N/A" on most records | You scraped eventType: ["online"] | Correct — online events have no venue. |
| 13 | Log Skipping: <url> | That detail page timed out (8s, no retry) | Not a failure. Re-run if you need it complete. |
| 14 | Log [api-fallback] <id> | A detail page timed out; the in-page API fetch was tried | Informational. |
| 15 | Log Listing page load timed out, proceeding anyway. | Slow listing page | The run continues; check the final count. |
| 16 | Sharply fewer events than an earlier identical run | Meetup is throttling this exit IP | Set the proxy group to RESIDENTIAL and re-run — each run gets a fresh IP. |
| 17 | No group name, organiser or member count | Not returned — eight fields only | Open url. |
| 18 | No keyword/topic/date filter available | Those inputs don't exist | Filter the dataset, or use Smart Event Scraper. |
| 19 | Only one city came back | city is a single string | Loop one run per city. |
| 20 | Run takes several minutes | Up to 60 scroll rounds plus detail pages | Expected; use the async endpoint. |
| 21 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard 300-second ceiling | Use async POST /runs → poll → fetch. |
| 22 | Up to 100 connector calls | deliveryMode: "perEvent" | Switch to summary or chunked. |
| 23 | Notion / Airtable timed out | One call carrying every event is too large | Use deliveryMode: "chunked". |
| 24 | Connector rejected the arguments | mcpArguments doesn't match the tool's schema | Read the Connector [<tool>] expects arguments matching this schema: log line. |
| 25 | Slack / Notion received nothing | Delivery needs both mcpConnector and mcpTool | Set both. The dataset is still written in full. |
| 26 | Connector fired but the body is empty | mcpArguments did not reference {message} | Map {message} and populate mcpMessageTemplate. |
| 27 | Log APIFY_MCP_PROXY_URL is not set | You ran locally; connectors only resolve on the platform | Deploy with apify push and run on the platform. |
| 28 | HTTP 401 / 403 from Apify | Missing, expired or malformed APIFY_TOKEN | Regenerate in Apify Console → Settings → API & Integrations. |
| 29 | HTTP 429 from Apify | Apify account concurrency / rate limits | Retry with exponential backoff (2s → 4s → 8s). |
Known limitations
price: "Free"is partly inferred — missing prices are written asFreerather than left unknown.- Title deduplication collapses recurring events that share a title.
dateis not normalised and can benull.- Meetup throttles by IP. Apify Proxy is on by default and rotates the exit IP per run;
RESIDENTIALis the escalation, and it needs a paid Apify plan. - No keyword, topic, group or date-range filter, and no group metadata, descriptions or images in the output.
- One city per run, browser-based, so wall-clock time is measured in minutes.
Diagnostic checklist before opening an issue
- Read the
Listing URL:log line. It shows the exact slug built from your city + country. Most empty runs are explained right there. - Baseline run.
{"city":"Chicago","country_code":"us","limit":5,"deliveryMode":"none"}— a dense, well-covered city. - Items returned? If yes, the pipeline is fine and your earlier city/country pair was the problem.
- Check the scroll log.
[scroll N] new=… intercepted=…shows whether events were arriving at all, andNo new events for 5 scrollsmeans the feed legitimately ran out. - Check the enrichment split.
Unique: N | Complete after defaults: X | Need detail: Ytells you how much came free from the listing versus needing detail pages.
If the issue survives all five steps, open an Issues ticket on the Actor page (or email support) with the run ID, the exact input JSON, and the Listing URL: line from the log.
❓ FAQ
Setup & scope
Do I need a Meetup account or API key?
No — and neither is accepted. The Actor reads public listing and event pages only.
Why did my city return nothing?
Almost always because country_code didn't match the city. The two are joined into one Meetup slug, and country_code defaults to us — so Mumbai without setting in becomes us--mumbai, which matches nothing. The run log's Listing URL: line shows exactly what was built.
Do I need to format the city name?
No. It's lowercased and spaces become hyphens automatically, so "New York" becomes new-york. Just use the city's common Meetup spelling.
Can I plug it into Claude, Cursor, or a LangChain agent?
Yes — it is a native MCP tool:
claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/meetup-city-events-scraper"
Coverage & filtering
Can I search by topic or keyword?
Not here — there's no keyword input. Run the city feed and filter on title, or use Smart Event Scraper, which filters by category, keyword and date range across six platforms including Meetup.
Can I scrape a specific Meetup group?
No — this is a city-wide search. It returns individual events, not group pages or group histories.
Can I get online events?
Yes: eventType: ["online"], or both formats together. Online events legitimately have venue: "N/A".
Does it get every meetup in the city?
It scrolls until the feed goes dry (up to 60 rounds) or your limit is reached, whichever comes first. Title deduplication also merges recurring same-title events, so treat the count as the distinct upcoming feed rather than an exhaustive occurrence list.
Output
Is price: "Free" reliable?
Only partly. When Meetup publishes no price, the Actor writes "Free" rather than leaving it unknown. It's usually right for community meetups, but it isn't verified per event — check the event page before using it for paid-vs-free analysis.
Why is date sometimes null or in an odd format?
Enrichment is best-effort: an 8-second timeout with a single attempt. Records that don't get enriched keep date: null. And date isn't normalised — it's ISO 8601 when Meetup gave a timestamp, otherwise whatever string the page exposed.
Why "N/A" instead of null for venue?
That's the convention for venue and venue_address. Test == "N/A" rather than relying on null checks. Note date is the exception — it really can be null.
Why does my weekly meetup appear only once?
Deduplication runs on event ID and then on title, so a recurring series with a constant title collapses into a single record.
Can I get the group name, organiser or member count?
No — the output is eight fields. Open url for anything more.
Pricing
How is this billed?
Pay-per-result — charged per event returned, no monthly subscription. The current rate is on the Actor's Pricing tab. Apify platform usage is billed separately and is the larger cost here, since the run uses a browser.
How do I keep a run cheap?
Validate the city/country pair at limit: 5, set a realistic limit, and don't schedule more often than the calendar actually changes.
How do I guarantee an agent never overspends?
Pass maxTotalChargeUsd as a query parameter on the run endpoint.
🔗 Related Actors
Event data, by source
Pick by where the events are listed — or by whether you need filtering.
| Actor | Source | Best for | Why pick it over this one |
|---|---|---|---|
| This Actor | meetup.com | Community & tech meetups in one city | Deepest pass over Meetup's own city feed, with venue and address enrichment |
| Smart Event Scraper | 6 platforms incl. Meetup | Filtered multi-platform search | ⭐ Category, keyword and date-range filters — the filtering this Actor has none of |
| Universal Event Scraper | 5 platforms incl. Meetup | One unified schema across platforms | Meetup plus AllEvents, EventsEye, District and Eventbrite in one flat record shape |
| Eventbrite Scraper | eventbrite.com | Organizer-led events, workshops, paid classes | The commercial/ticketed side of the same city |
| All Events Scraper | allevents.in | Broad global city discovery | Wider consumer coverage than community meetups |
| Events Eye Scraper | eventseye.com | Trade shows, B2B exhibitions | Professional events, not community ones |
⭐ Need keyword or date filtering? Use Smart Event Scraper. It carries Meetup as one of six sources and adds a
category, a free-textsearch_queryand adate_from/date_torange. "AI meetups in Berlin next month" is a single run there; here it's a full city scrape plus your own filtering.Come back to this Actor when you want depth on Meetup specifically — it scrolls the city feed to exhaustion and enriches venue and street address, which the aggregators don't.
Enrichment & downstream pipeline
| Actor | Use it for |
|---|---|
| Google Maps Business Leads & Sales Intelligence | Venue contact details — email, phone, website — which this Actor never returns |
| Website Contact Scraper | Contacts from a venue's or community's own website |
| LinkedIn Company Employees | Who works at the companies hosting or sponsoring meetups |
| Hospitality Lead Scraper | Bars, cafés and venues near a meetup location |
Suggested pipeline patterns
🤝 City community feed
Weekly schedule per city → deliveryMode: "chunked" into Notion → a self-maintaining "what's on in the community" page.
🌍 Tech-scene benchmarking Run across several candidate cities → count events and distinct venues per city → compare where a community is most active before opening an office or planning a tour.
🎤 DevRel / speaking pipeline
Run monthly for target cities → filter title for your topic → track venues and recurring series worth pitching a talk to.
🏢 Venue & sponsor prospecting
Group by venue → Google Maps Business Leads or Website Contact Scraper on the venue → a contactable list of spaces that already host communities.
🌐 Complete city coverage This Actor + Eventbrite Scraper + All Events Scraper → dedupe on title + date → community meetups and the ticketed long tail in one feed.
Browse all Actors by Techforce Global at apify.com/techforce.global.
🔐 Compliance & Data Privacy
This Actor is an independent tool. It is not affiliated with, endorsed by, or sponsored by Meetup, Bending Spoons, or any group, organiser or venue named in its output. All trademarks are the property of their respective owners.
It reads publicly available event listing pages on meetup.com. It does not log in, does not accept credentials, does not access anything behind authentication, and never RSVPs or joins anything.
Most of the output is event data, but community events sit closer to personal data than commercial listings do. Three points deserve care:
- Event titles can identify people. Community meetups are often named after their organiser, and a title or venue can identify an individual — particularly for small groups meeting at a private address. Where that happens you are processing personal data under GDPR/UK GDPR and CCPA: have a lawful basis, set a retention period, and don't repurpose it simply because it was public.
- Do not use this to build an outreach list against community organisers. Meetup exists to help people find events, not to supply prospect lists. Bulk-contacting organisers harvested this way is both a poor fit for the platform's purpose and likely to fall foul of GDPR Art. 6/21, ePrivacy/PECR, CAN-SPAM and CASL.
priceis partly inferred and may be wrong. Because missing prices are written as"Free", republishing this field as fact can mislead people about the cost of attending. If you display prices publicly, attribute the source and link through tourlfor the authoritative figure — under GDPR Art. 5(1)(d) and general consumer-protection rules, presenting inferred data as verified is your risk.
The Actor already limits its footprint — blocked assets, an 8-connection ceiling, single-attempt detail loads. Proxy support exists to spread load across exit IPs, not to evade limits: don't defeat the design by running many parallel executions against the same city, and use of Apify Proxy is itself governed by Apify's terms. You remain responsible for complying with Meetup's terms of service and all applicable data-protection and marketing law in the markets you operate in.
🆘 Support & Custom Pipeline Engineering
Need keyword or date filtering wired in, group-level scraping, multi-city scheduling with change detection, or a full community-intelligence pipeline feeding your own systems?
- Email: bhavin.shah@techforceglobal.com
- Custom Enterprise Integrations: Book a 15-Minute Technical Consultation
- Maintained by: Techforce Global — Specialists in High-Performance Web Scrapers and Agentic Workflows.
Made with ❤️ by Techforce Global Specialists in High-Performance Web Scrapers and AI Automation.
🏷️ Structured data for search & AI discovery
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "Meetup.com City Events Scraper — Community Events by City in 54 Countries","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Scrapes upcoming Meetup.com events for one city in any of 54 countries — event title, start date, venue name and street address, price and the direct event URL — with no Meetup account or API key required. Events are read from the listing page's own data payload and from intercepted JSON responses, and only records still missing a date, venue or price cost a detail-page visit. Results can be delivered into Notion, Slack, Airtable, Google Sheets or any authorized MCP connector. Note: when Meetup publishes no price, the Actor records it as Free.","url": "https://apify.com/techforce.global/meetup-city-events-scraper","offers": {"@type": "Offer","priceCurrency": "USD","description": "Pay-per-result, charged per event returned. The current rate is listed on the Actor's Pricing tab."}}