Meetup.com City Events Scraper avatar

Meetup.com City Events Scraper

Pricing

from $1.50 / 1,000 results

Go to Apify Store
Meetup.com City Events Scraper

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

Techforce Global

Maintained by Community

Actor stats

0

Bookmarked

8

Total users

0

Monthly active users

4 days ago

Last modified

Share

Meetup.com City Events Scraper — Community Events by City in 54 Countries, No Login

Apify Actor MCP Ready Source Countries Formats

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 / CapabilityBrowsing meetup.comThis Actor
Login required⚠️ Much of the site pushes you to sign inNone — 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 sourceRendered 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 costOne page load per eventSkipped entirely for events already complete from the listing
DeduplicationManual✅ By Meetup event ID, with a title pass on top
OutputCopy-paste✅ Clean 8-field dataset → JSON / CSV / Excel / XML
Delivery to your tools❌ Manual✅ Native MCP connector delivery — per event, chunked, or one digest
SpeedMinutes 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 are
read from the page's __NEXT_DATA__ payload and from intercepted JSON API
responses, with CSS selectors only as a last resort. Public pages only - no
login, 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 city
that 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: 20
free_plan_cap: 5
output_conventions:
price_is_partly_inferred: >
CRITICAL - when Meetup publishes no price the Actor writes "Free" rather than
leaving 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 page
exposed. Not normalised - parse defensively.
source_constant: 'always the string "Meetup"'
dedup: >
by Meetup event ID parsed from the URL, then a second pass on lowercased
title - so two DIFFERENT events sharing a title (a recurring weekly meetup)
collapse into one record.
limits:
events_per_run: 100
free_plan_cap: 5
cities_per_run: 1
max_scrolls: 60
detail_concurrency: 8
detail_timeout_ms: 8000
known_limitations:
meetup_rate_limits: >
Meetup throttles by IP. Apify Proxy is enabled by default and rotates the
exit 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 events
feed, research a city's tech or professional scene, find networking or speaking
opportunities, 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; RESIDENTIAL is 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 mcpArguments is 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

ScenarioFit
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)

  1. city is required, and it is combined with country_code into 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.
  2. 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.
  3. country_code defaults to us, not to your location. Scraping Mumbai without setting in will return nothing.
  4. 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.
  5. date is not normalised. It's ISO 8601 when Meetup gave a timestamp, otherwise a passthrough string — and it can be null on records that never got enriched. Parse defensively.
  6. venue and venue_address fall back to the string "N/A", not null. Test == "N/A" explicitly. Online events legitimately have no venue.
  7. 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.
  8. limit is 1–100, default 20. Free Apify plans are capped at 5 regardless. That's a successful run.
  9. eventType is an array. ["inPerson"] (default), ["online"], or both — selecting both sends a comma-joined filter and returns a mixed set.
  10. No keyword, topic, group or date filter exists. You get the city feed date-ascending; filter the dataset afterwards.
  11. One city per run. Loop from your orchestration layer for a multi-city sweep.
  12. Fewer results than limit is normal — smaller cities have fewer meetups, and there is no total-match count in the output.
  13. Detail enrichment is best-effort and never retried. An 8-second timeout with a single attempt means some records keep "N/A" venue or a null date. That's a cost trade-off, not a failure.
  14. 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.
  15. 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 RESIDENTIAL group (paid Apify plans). If proxy setup fails, the run continues on the container IP with a warning rather than failing.
  16. deliveryMode defaults to summary, not none — one connector call per run. perEvent on 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: none
platform: "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

FieldTypeRequiredDefaultAllowed Values / FormatExample
citystringYes"Chicago"Free text; slugified automatically"Mumbai"
country_codeenum⬜ No"us"54 country codes — see below"in"
eventTypearray⬜ No["inPerson"]inPerson, online, or both["online"]
limitinteger⬜ No201100; free plans capped at 5100
proxyConfigurationobject⬜ NoApify Proxy onApify proxy editor object — see belowsee below

⚠️ city and country_code must agree. They are joined into one Meetup location slug — country_code + -- + slugified city. Mumbai with the default us produces us--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"
}
}
SettingEffect
{"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.

ParameterTypeDefaultDescription
mcpConnectorstring""Authorized MCP connector on your Apify account (Notion, Slack, Airtable, Sheets, …). Leave empty for dataset-only runs.
deliveryModeenumsummarysummary (one call with every event) · chunked (split across calls) · perEvent (one call per event) · none.
mcpToolstring""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.
mcpArgumentsobject{}Arguments passed to the tool. String leaves support {placeholders}.
mcpMessageTemplatestring""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

FieldTypeEmpty valueMeaning
sourcestringnever emptyAlways the constant "Meetup"
titlestring""Event title
urlstring""Direct Meetup event page — the dedupe key (its numeric event ID is what's actually compared)
datestring⚠️ can be nullStart date/time. ISO 8601 when Meetup gave a timestamp, otherwise a passthrough string — not normalised
citystringEchoed back from your input, for grouping
venuestring"N/A"Venue name. Legitimately "N/A" for online events
venue_addressstring"N/A"Street address, city and state joined with ", "
pricestring⚠️ "Free"Price as a display string — "Free" may be an inference, see below

⚠️ Two traps in this record.

1. price is 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 datetime
def start_dt(event):
raw = event.get("date")
if not raw: # date can be null
return None
try:
return datetime.fromisoformat(str(raw).replace("Z", "+00:00"))
except ValueError:
return None # passthrough string - keep as text
def 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 lineMeaning
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 loadEvents 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: YHow 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 os
from datetime import datetime
from apify_client import ApifyClient
client = 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().items
print(f"{len(items)} events")
def start_dt(event):
raw = event.get("date")
if not raw:
return None # date can be null
try:
return datetime.fromisoformat(str(raw).replace("Z", "+00:00"))
except ValueError:
return None # not normalised - may be a display string
dated = [(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().items
if 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`);

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 limit
curl -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. Poll
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/actor-runs/<runId>'
# 3. Retrieve when SUCCEEDED
curl -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 CodeDetection ConditionUnderlying CauseDeterministic Agent Action
AUTH_INVALIDHTTP 401 from ApifyMissing or malformed APIFY_TOKENAbort & Prompt User: request a valid Apify API token.
RATE_LIMITEDHTTP 429 from ApifyApify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointBrowser run with up to 60 scrolls exceeds 300sReconfigure: async POST /runs + polling.
MISSING_CITYInput validation rejectioncity is requiredModify Input: supply a city.
CITY_COUNTRY_MISMATCHSUCCEEDED, 0 items; log shows a slug like us--mumbaicountry_code doesn't match the cityModify Input: set the correct country_code. Check the Listing URL: log line first.
WRONG_CITY_SPELLINGSUCCEEDED, 0 items, slug looks rightMeetup doesn't recognise that city slugModify Input: try the city's common Meetup spelling.
EMPTY_CITY_FEEDSUCCEEDED, 0 items, slug correctThat city genuinely has no upcoming meetups of the chosen formatModify Input: add online to eventType, or try a larger nearby city.
FILTER_UNSUPPORTEDAgent passes keyword, topic, group, dateFrom, …No such inputs existRe-route: Smart Event Scraper for keyword/date filtering, or filter the dataset.
FREE_PLAN_CAPitemCount stops at 5, upgrade banner in the logFree Apify plan capPrompt User: upgrade for full-volume runs.
SIZE_CLAMPEDAsked for more than 100Schema maximum is 100Reconfigure: run per city, or accept 100.
FEWER_THAN_LIMITitemCount < limitSmall city, or title dedup merged recurring eventsNot an error. No total-match count exists.
DEDUP_COLLAPSED_SERIESA weekly meetup appears onceTitle deduplication removed the repeatsFix Expectation: documented behaviour; use the event page for the full series.
PRICE_INFERRED_FREEEverything reports "Free"Missing prices are written as FreeFix Expectation: verify on the event page before treating as confirmed.
DATE_NULLdate is nullEnrichment didn't complete for that recordFix Code: null-guard; the event is still valid.
DATE_FORMAT_MIXEDSome dates parse as ISO, others don'tdate is not normalisedFix Code: try ISO 8601, else keep the raw string.
NA_NOT_NULLNull checks pass but values unusablevenue/venue_address use the string "N/A"Fix Code: compare against "N/A".
NO_VENUE_ONLINEvenue: "N/A" on many recordsOnline events have no venueNot an error — expected for eventType: ["online"].
PARTIAL_ENRICHMENTLog 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_DATANo group name, organiser or member countNot returned — eight fields onlyFix Expectation: open url.
MULTI_CITY_UNSUPPORTEDAgent passes a city arrayOne city per runLoop: one run per city.
THROTTLED_BY_MEETUPSharply fewer events than a previous identical runMeetup is rate-limiting this exit IPReconfigure: set apifyProxyGroups: ["RESIDENTIAL"], then re-run. Each run gets a fresh IP.
PROXY_UNAVAILABLELog Could not set up Apify Proxy (…). Continuing without a proxyThe plan lacks access to the requested group (RESIDENTIAL needs a paid plan)Prompt User: upgrade, or drop back to the default automatic pool.
RSVP_REQUESTEDUser wants to join/registerRead-onlyAbort & Prompt User: open url to RSVP manually.
DELIVERY_SKIPPEDLog no tool name was providedmcpConnector set but mcpTool emptyModify Input: set mcpTool; the dataset is already saved.
DELIVERY_TOOL_UNKNOWNLog Tool 'x' is not available on this connectorWrong tool name for that connectorModify Input: pick a name from the Available tools: list in the same log line.
DELIVERY_ARGS_INVALIDConnector rejects the argumentsmcpArguments doesn't match the tool's schemaRead the log: the line Connector [<tool>] expects arguments matching this schema: prints the exact shape.
DELIVERY_EMPTYConnector called, body emptymcpArguments omitted the {message} placeholderModify Input: map {message} inside mcpArguments.
DELIVERY_FLOODUp to 100 connector callsdeliveryMode: "perEvent"Reconfigure: use summary or chunked.
MCP_PROXY_MISSINGLog APIFY_MCP_PROXY_URL is not setRunning locally instead of on the platformReconfigure: apify push and run on the platform.
CHARGE_LIMITRun aborted mid-scrollmaxTotalChargeUsd spend threshold reachedPrompt 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 saysIntentConstructed 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 researchrun, then group on venue
"Find me networking opportunities next month"Networkingrun, then filter parsed date
"Track Mumbai's meetups weekly"Monitoringschedule {"city":"Mumbai","country_code":"in"} and diff on url
"Build a community events feed for 3 cities"Multi-cityLoop 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 deliveryschedule + {"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 it
slug looks correct → try the city's common Meetup spelling
still 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.json and the code makes no Actor.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:

OptimisationEffect
Data-payload-first extractionMost events come from __NEXT_DATA__ and intercepted JSON — no detail page needed
Conditional enrichmentOnly records still missing date/venue/price get a detail-page visit
Blocked resourcesImages, media, fonts and stylesheets are aborted on every page
8s timeout, single attemptA slow detail page is abandoned rather than retried
Early scroll exitStops 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: 5 first. A mismatched pair costs a full run for zero events — the Listing URL: log line tells you immediately.
  • Set limit to 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/chunked delivery over perEvent — 1 connector call instead of up to 100.
  • Cap the spend with maxTotalChargeUsd on any agent-driven run.

🔍 Companion machine-readable files

FilePurpose
.actor/actor.jsonIdentity, title, description, version, build tag, and the input / output / dataset schema references
.actor/input_schema.jsonAuthoritative 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.jsonTwo Console Output views — full Events view and a Compact view for listings and calendar imports
.actor/output_schema.jsonDeclares where results are stored
my_actor/main.pyURL building, __NEXT_DATA__ parsing, JSON response interception, scroll/load-more handling, conditional detail enrichment, deduplication, MCP delivery
my_actor/connector.pyMCP 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 seeMost likely causeFix
10 events; log shows a slug like us--mumbaicountry_code doesn't match the city (it defaults to us)Set the correct country code.
20 events; the slug looks rightMeetup doesn't recognise that city spellingTry the city's common Meetup spelling.
30 events; slug and spelling both fineNo upcoming meetups of that format thereAdd online to eventType, or try a larger nearby city.
4Exactly 5 eventsFree Apify plan capUpgrade; the log prints the upgrade banner.
5Asked for 500, got 100Schema maximum is 100Run per city, or accept 100.
6Fewer events than limitSmall city, or title dedup merged recurring eventsExpected — there's no total-match count.
7A weekly meetup appears only onceTitle deduplication removes repeated titlesDocumented behaviour; open the event page for the series.
8Everything says price: "Free"Missing prices are written as FreeVerify on the event page before treating as confirmed.
9date is null on some recordsEnrichment didn't complete for thoseNull-guard; the event is still valid.
10Some dates parse, others don'tdate is not normalisedTry ISO 8601, else keep the raw string.
11Null checks never fire on venueIt's the string "N/A"Compare against "N/A".
12venue: "N/A" on most recordsYou scraped eventType: ["online"]Correct — online events have no venue.
13Log Skipping: <url>That detail page timed out (8s, no retry)Not a failure. Re-run if you need it complete.
14Log [api-fallback] <id>A detail page timed out; the in-page API fetch was triedInformational.
15Log Listing page load timed out, proceeding anyway.Slow listing pageThe run continues; check the final count.
16Sharply fewer events than an earlier identical runMeetup is throttling this exit IPSet the proxy group to RESIDENTIAL and re-run — each run gets a fresh IP.
17No group name, organiser or member countNot returned — eight fields onlyOpen url.
18No keyword/topic/date filter availableThose inputs don't existFilter the dataset, or use Smart Event Scraper.
19Only one city came backcity is a single stringLoop one run per city.
20Run takes several minutesUp to 60 scroll rounds plus detail pagesExpected; use the async endpoint.
21HTTP 408 on run-sync-get-dataset-itemsThe synchronous endpoint has a hard 300-second ceilingUse async POST /runs → poll → fetch.
22Up to 100 connector callsdeliveryMode: "perEvent"Switch to summary or chunked.
23Notion / Airtable timed outOne call carrying every event is too largeUse deliveryMode: "chunked".
24Connector rejected the argumentsmcpArguments doesn't match the tool's schemaRead the Connector [<tool>] expects arguments matching this schema: log line.
25Slack / Notion received nothingDelivery needs both mcpConnector and mcpToolSet both. The dataset is still written in full.
26Connector fired but the body is emptymcpArguments did not reference {message}Map {message} and populate mcpMessageTemplate.
27Log APIFY_MCP_PROXY_URL is not setYou ran locally; connectors only resolve on the platformDeploy with apify push and run on the platform.
28HTTP 401 / 403 from ApifyMissing, expired or malformed APIFY_TOKENRegenerate in Apify Console → Settings → API & Integrations.
29HTTP 429 from ApifyApify account concurrency / rate limitsRetry with exponential backoff (2s → 4s → 8s).

Known limitations

  • price: "Free" is partly inferred — missing prices are written as Free rather than left unknown.
  • Title deduplication collapses recurring events that share a title.
  • date is not normalised and can be null.
  • Meetup throttles by IP. Apify Proxy is on by default and rotates the exit IP per run; RESIDENTIAL is 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

  1. Read the Listing URL: log line. It shows the exact slug built from your city + country. Most empty runs are explained right there.
  2. Baseline run. {"city":"Chicago","country_code":"us","limit":5,"deliveryMode":"none"} — a dense, well-covered city.
  3. Items returned? If yes, the pipeline is fine and your earlier city/country pair was the problem.
  4. Check the scroll log. [scroll N] new=… intercepted=… shows whether events were arriving at all, and No new events for 5 scrolls means the feed legitimately ran out.
  5. Check the enrichment split. Unique: N | Complete after defaults: X | Need detail: Y tells 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.


Event data, by source

Pick by where the events are listed — or by whether you need filtering.

ActorSourceBest forWhy pick it over this one
This Actormeetup.comCommunity & tech meetups in one cityDeepest pass over Meetup's own city feed, with venue and address enrichment
Smart Event Scraper6 platforms incl. MeetupFiltered multi-platform searchCategory, keyword and date-range filters — the filtering this Actor has none of
Universal Event Scraper5 platforms incl. MeetupOne unified schema across platformsMeetup plus AllEvents, EventsEye, District and Eventbrite in one flat record shape
Eventbrite Scrapereventbrite.comOrganizer-led events, workshops, paid classesThe commercial/ticketed side of the same city
All Events Scraperallevents.inBroad global city discoveryWider consumer coverage than community meetups
Events Eye Scrapereventseye.comTrade shows, B2B exhibitionsProfessional 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-text search_query and a date_from/date_to range. "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

ActorUse it for
Google Maps Business Leads & Sales IntelligenceVenue contact details — email, phone, website — which this Actor never returns
Website Contact ScraperContacts from a venue's or community's own website
LinkedIn Company EmployeesWho works at the companies hosting or sponsoring meetups
Hospitality Lead ScraperBars, 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 venueGoogle 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.
  • price is 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 to url for 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?


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."
}
}