Universal Event Scraper
Pricing
Pay per event + usage
Universal Event Scraper
The Universal Event Scraper is an Apify Actor that combines four specialized scrapers into a single run. Allevents.in Eventseye.com District.in Meetup.com. It automatically handles dynamic content and produces clean structured data that can be exported or integrated into other systems.
Pricing
Pay per event + usage
Rating
4.4
(5)
Developer
Techforce Global
Maintained by CommunityActor stats
1
Bookmarked
25
Total users
1
Monthly active users
11 days ago
Last modified
Categories
Share
Universal Event Scraper — 5 Event Platforms, One Unified Schema, One Run
Scrape AllEvents.in, EventsEye.com, District.in, Meetup.com and Eventbrite for one city in a single run, and get every result back in exactly the same 8-field record — no per-source shape to reconcile, no five separate Actors to orchestrate. Results can go straight into Notion, Slack, Airtable or Sheets through an MCP connector.
ℹ️ The unified schema is the whole point. Each source has a completely different underlying structure — AllEvents renders HTML, EventsEye is a static trade-show directory, District needs a browser and a location picker, Meetup ships GraphQL/SSR payloads, Eventbrite exposes a browse JSON. All five are normalised to
source · title · url · date · city · venue · venue_address · price, so one downstream consumer handles all of them.
Running five separate Actors vs. this one
| Feature / Capability | Five separate Actors | This Actor |
|---|---|---|
| Runs to orchestrate | 5 runs, 5 inputs, 5 result sets | ✅ One run, one input, one dataset |
| Output shape | ⚠️ 5 different schemas to map and merge | ✅ 8 identical fields across every source, verified per-normaliser |
| Source identification | ⚠️ Implicit — you track which dataset came from where | ✅ Explicit source field on every record |
| Engine per source | You pick and maintain each | ✅ Chosen per source: HTTP for the static directory, browser only where required |
| Missing-value handling | ⚠️ Mixed null / "" / absent keys | ✅ Consistent "N/A" string everywhere |
| Adding a source later | New Actor, new integration | ✅ One more value in the source array |
| Delivery to your tools | 5 integrations | ✅ One MCP connector delivery for the merged set |
| Cost model | 5 bills to reason about | ✅ Pay-per-event, one event per record |
🤖 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/universal-event-scraper"one_line: "Scrapes events for one city from up to five platforms - AllEvents.in, EventsEye.com, District.in, Meetup.com and Eventbrite - in a single run, normalising every source into the same 8-field record."primary_purpose: "discover"secondary_purpose: "aggregate"tertiary_purpose: "deliver"entity: "events across consumer event platforms, meetup groups and B2B trade-show directories"data_sources:allevents: "allevents.in city listing - Playwright + BeautifulSoup"eventseye: "eventseye.com trade-show directory - httpx + BeautifulSoup (no browser)"district: "district.in - Playwright with a location picker and a 5-page pool (India only)"meetup: "meetup.com - Playwright, GraphQL interception plus server-rendered payload parsing"eventbrite: "eventbrite.com public browse JSON"invocation_modes: ["standard Actor run", "MCP tool"]supported_tasks:- "scrape-one-city-across-several-event-platforms-in-one-run"- "return-a-single-unified-record-shape-for-every-source"- "compare-coverage-of-the-same-city-across-platforms"- "collect-b2b-trade-shows-alongside-consumer-events"- "collect-meetup-groups-in-person-or-online"- "deliver-merged-events-to-mcp-connector-or-crm"unsupported_tasks:- "filter-by-date-range (the dateRange input is accepted and IGNORED)"- "filter-by-keyword (the keywords input is accepted and IGNORED)"- "filter-by-category-or-price"- "scrape-several-cities-in-one-run"- "buy-reserve-or-hold-tickets"- "return-event-descriptions-images-organisers-or-lineups"- "return-ticket-availability-seat-maps-or-inventory"- "deduplicate-the-same-event-found-on-two-platforms"- "use-a-proxy (NO proxy input exists - see known limitations)"search_keywords:- "universal event scraper"- "multi platform event scraper"- "event aggregator actor"- "allevents scraper"- "eventseye scraper"- "meetup scraper"- "eventbrite scraper"- "district.in scraper"- "trade show data"- "city event feed"- "unified event schema"synonyms:- "combined event scraper"- "5-in-1 event scraper"- "cross-platform event extractor"related_concepts:["event aggregation", "city event feeds", "trade show research", "community/meetup discovery", "event data normalisation"]input_entities:- "source (array; REQUIRED; 1-5 of allevents | eventseye | district | meetup | eventbrite)"- "location (one city name, shared by every source)"- "limit (per source; 1-50, or 0 for no limit)"- "country_code (Meetup only)"- "eventType (Meetup only: inPerson | online)"output_entities: ["source", "title", "url", "date", "city", "venue", "venue_address", "price"]input_rules:required_fields: ["source"]one_city_per_run: "location is a single city string shared by all selected sources"limit_semantics:per_source: truerange: [1, 50]zero_means_unlimited: trueallevents_exception: "the allevents source caps at 100 even when limit is 0"free_plan_cap: 2sources_run_sequentially: >Sources are executed one after another, deliberately: several launch their ownChromium and running them concurrently starves the lazy-loaded listing pages.Expect run time to be the SUM of the selected sources.ignored_inputs:- "dateRange - accepted, never read"- "keywords - accepted, never read"- "eventbriteApiKey - accepted, never read"- "eventbriteSearch - accepted, never read"- "eventbriteErrorHandling - accepted, never read"output_conventions:unified_shape: "all five sources emit exactly the same 8 keys, in the same order"missing_values: "the STRING 'N/A' - never null, never an absent key"price_availability: >Only district, meetup and eventbrite ever populate price. allevents andeventseye hardcode it to 'N/A'.venue_address_availability: >Only eventseye, meetup and eventbrite ever populate venue_address. alleventsand district hardcode it to 'N/A'.date_format: "NOT normalised - a display string on some sources, ISO 8601 on others"no_deduplication: "the same real-world event found on two platforms yields two records"limits:events_per_source: 50unlimited_option: "limit 0 (allevents still caps at 100)"cities_per_run: 1free_plan_cap: "2 per source"known_limitations:no_proxy_support: >There is no proxy input. The district source in particular fingerprints thecaller's IP and typically blocks datacenter ranges, so it often returnsnothing from an Apify container.no_date_or_keyword_filter: "those inputs exist in the schema but are not implemented"date_not_normalised: "compare dates per source, not across sources"pricing:model: "pay-per-event, one event per record pushed"events_charged: ["premium-scrape"]note: >The code also defines a 'basic-scrape' event but every source maps to'premium-scrape', so basic-scrape never fires. Exact rates are configured onthe Store listing - see the Actor's Pricing tab.when_to_use: >You want one city's events from several platforms at once, in one consistentshape - for an aggregated feed, cross-platform coverage comparison, or a mixedconsumer-plus-trade-show dataset.when_not_to_use: >You need date, keyword, category or price filtering; you need several cities perrun; you need deduplication across platforms; or you only care about oneplatform - in which case the dedicated single-source Actor is better.alternatives:- "Use techforce.global/smart-event-scraper for the SAME multi-platform idea PLUS category, keyword and date-range filters"- "Use techforce.global/all-events-scraper, techforce.global/eventbrite-scraper, techforce.global/district-event or techforce.global/events-eye-scraper for depth on one platform"- "Use techforce.global/eventim-scraper or techforce.global/ticketmaster-fr-es-scraper for ticketed events with venue-grade data"
What this Actor does
- Runs up to five scrapers in one execution and writes every result into a single dataset, tagged with its
source. - Normalises all five into an identical 8-field record —
source,title,url,date,city,venue,venue_address,price. Verified: all five normalisers emit exactly these keys, in this order. - Uses the right engine per source rather than a browser everywhere: EventsEye is fetched with plain HTTP (
httpx), while District, Meetup and AllEvents get Playwright because their listings genuinely need rendering. Meetup additionally intercepts GraphQL responses and parses the server-rendered payload. - Runs the sources sequentially, on purpose. Several launch their own Chromium; running them concurrently starved the lazy-loaded listing pages (District would report zero events). Sequential means each behaves like its standalone version — at the cost of run time being the sum of the selected sources.
- Caps per source, not per run —
limitapplies to each selected source independently, so five sources at 50 is up to 250 records. - Applies a consistent
"N/A"wherever a source doesn't publish a field, so downstream code never has to handle three different flavours of "missing". - Optionally pushes the merged set 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. The dataset is always saved regardless of delivery settings.
The five sources
source value | Platform | Engine | Typical content | Populates price? | Populates venue_address? |
|---|---|---|---|---|---|
allevents | allevents.in | Playwright + BeautifulSoup | Broad consumer city listings | ❌ always N/A | ❌ always N/A |
eventseye | eventseye.com | httpx (no browser) | B2B trade shows & exhibitions | ❌ always N/A | ✅ yes |
district | district.in | Playwright + 5-page pool | Indian ticketed events | ✅ yes | ❌ always N/A |
meetup | meetup.com | Playwright + GraphQL intercept | Community groups, in-person or online | ✅ yes (often Free) | ✅ yes |
eventbrite | eventbrite.com | Public browse JSON | Organizer-led events, workshops | ✅ yes (incl. Free) | ✅ yes |
⚠️
districtwill often return nothing from Apify. This Actor has no proxy input, and District fingerprints the caller's IP — it withholds its location picker from datacenter ranges, which is what an Apify container uses. If you specifically need District data, the dedicated District.in Event Scraper supports the residential-IN proxy that source actually requires.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| One city's events from several platforms, in one consistent shape | ✅ Strong (the core use case) |
| Comparing how well platforms cover the same city | ✅ Strong — group by source |
| A mixed dataset of consumer events and B2B trade shows | ✅ Strong — allevents + eventseye |
| Community/meetup discovery alongside ticketed events | ✅ Strong — meetup + eventbrite |
| Feeding a merged feed into Notion / Airtable / Slack | ✅ Strong (deliveryMode) |
| Trade shows only, no browser cost | ✅ Strong — ["eventseye"] alone is HTTP-only |
| Filtering by date range or keyword | ❌ Those inputs exist but are not implemented — ➡️ Smart Event Scraper |
| Filtering by category or price | ❌ No such input — ➡️ Smart Event Scraper |
| Several cities in one run | ❌ One location per run — loop from your side |
| Deduplicating an event that appears on two platforms | ❌ Not done — you get two records |
| Only one platform matters to you | ⚠️ A dedicated single-source Actor gives more fields and options |
| District.in data specifically | ⚠️ No proxy support here — use District.in Event Scraper |
Comparing date values across sources | ⚠️ date is not normalised — formats differ per source |
| Event descriptions, images, organisers, lineups | ❌ Eight fields only |
| Ticket availability, seat maps, inventory | ❌ Not returned |
| Buying or reserving tickets | ❌ Read-only |
⚡ Execution Rules (Avoid Common Agent Failures)
sourceis required and is an array. Pass one to five ofallevents,eventseye,district,meetup,eventbrite. An unrecognised value is skipped with a warning, not an error — so a typo yields a quietly empty run.limitis per source, not per run. Five sources at 50 is up to 250 records, and 250 billable events.limit: 0means no limit — it bypasses the schema's maximum of 50 entirely. Use a spend cap with it. Note the exception: thealleventssource caps at 100 even whenlimitis 0.- Free Apify plans are capped at 2 records per source — so a 5-source run returns up to 10 items. That's a successful run.
dateRangeandkeywordsare accepted and silently ignored. No source filters by either. If you need that filtering, use Smart Event Scraper; otherwise filter the dataset afterwards.- Three more inputs are inert:
eventbriteApiKey,eventbriteSearchandeventbriteErrorHandling. Selectingeventbriteinsourceis what enables that scraper. - Sources run sequentially, so run time is the sum of them. Five sources with browsers is a multi-minute run — use the async endpoint, not the 300s synchronous one.
- Every missing value is the string
"N/A", nevernull.if (event.price)is always truthy; test=== "N/A"explicitly. dateis not normalised across sources. Meetup can give ISO 8601 with a timezone; AllEvents and District give display strings. Parse persource, don't sort the merged set by rawdateand trust it.priceis only ever populated bydistrict,meetupandeventbrite.alleventsandeventseyehardcode"N/A"— that is not a scraping failure.venue_addressis only ever populated byeventseye,meetupandeventbrite.alleventsanddistricthardcode"N/A".- No deduplication. The same real-world event on two platforms produces two records with different
urls. Dedupe ontitle+dateyourself if you need to. districtusually returns nothing here — no proxy support, and District blocks datacenter IPs. Don't read an empty district set as a bug in your input.country_codeandeventTypeaffect Meetup only, andcountry_codemust match yourlocation's country or Meetup returns nothing (location: "Dubai"needscountry_code: "ae", not the default"in").- One city per run.
locationis a single string shared by every source. deliveryModedefaults tosummary, notnone— one connector call per run.perEventon a 250-event run makes 250 calls.
dependencies:- rule: "source is required and must contain a known value"on_violation: "unknown values are skipped with a warning; an all-typo input yields an empty successful run"- rule: "limit is per source and 0 disables the cap"on_violation: "volume and cost under-estimated by a factor of the source count"- rule: "allevents caps at 100 even when limit is 0"note: "the only source that ignores unlimited"- rule: "dateRange, keywords, eventbriteApiKey, eventbriteSearch and eventbriteErrorHandling are accepted but never read"on_violation: "agents believe they applied a filter that was silently dropped"- rule: "sources run sequentially"on_violation: "run-time estimates that assume parallelism; sync-endpoint timeouts"- rule: "absent values are the string 'N/A', never null"on_violation: "null checks pass while values are unusable"- rule: "date is not normalised across sources"on_violation: "sorting or filtering the merged set by raw date silently misorders it"- rule: "price and venue_address are hardcoded 'N/A' on some sources"on_violation: "a source-level omission mistaken for a scrape failure"- rule: "no cross-platform deduplication"on_violation: "duplicate events double-counted, and double-billed"- rule: "no proxy input exists; district blocks datacenter IPs"on_violation: "an empty district result read as a configuration error"- rule: "country_code must match the location's country (Meetup)"on_violation: "Meetup returns nothing for a valid city"- 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_sites: "no account, login or cookie required for any of the five sources"
📥 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 |
|---|---|---|---|---|---|
source | array | ✅ Yes | ["eventseye"] | 1–5 of allevents, eventseye, district, meetup, eventbrite | ["allevents","meetup"] |
location | string | ⬜ No | "Dubai" | One city name, shared by all sources | "mumbai" |
limit | integer | ⬜ No | 50 | 1–50 per source, or 0 for no limit | 25 |
country_code | string | ⬜ No | "in" | ISO 3166-1 alpha-2 — Meetup only | "ae" |
eventType | enum | ⬜ No | "inPerson" | inPerson, online — Meetup only | "online" |
⚠️ Inputs that exist but do nothing
These are present in the schema and accepted by the API, but no code reads them. They are documented here so an agent doesn't believe a filter was applied.
| Field | What you might expect | What actually happens |
|---|---|---|
dateRange | Date-range filtering | Ignored. Filter the dataset's date field afterwards, or use Smart Event Scraper |
keywords | Keyword filtering | Ignored. Filter the dataset's title field afterwards |
eventbriteApiKey | Authenticated Eventbrite access | Ignored. The Eventbrite source reads the public browse JSON and does not authenticate |
eventbriteSearch | Enable/disable the Eventbrite source | Ignored. Selecting eventbrite in source is what enables it |
eventbriteErrorHandling | Toggle error handling | Ignored. Eventbrite errors are always caught and logged |
Examples
Broadest sweep — everything for one city
{"source": ["allevents", "eventseye", "meetup", "eventbrite"],"location": "mumbai","limit": 50,"country_code": "in","deliveryMode": "none"}
Trade shows only — HTTP-only, cheapest and fastest
{"source": ["eventseye"],"location": "berlin","limit": 50,"deliveryMode": "none"}
Meetup in a non-Indian city — country_code must match
{"source": ["meetup"],"location": "Dubai","country_code": "ae","eventType": "inPerson","limit": 50,"deliveryMode": "none"}
Online communities only
{"source": ["meetup"],"location": "london","country_code": "gb","eventType": "online","limit": 50,"deliveryMode": "none"}
Cheap validation run before committing
{"source": ["allevents", "eventbrite"],"location": "ahmedabad","limit": 2,"deliveryMode": "none"}
Consumer + B2B side by side
{"source": ["allevents", "eventseye"],"location": "dubai","limit": 50,"deliveryMode": "none"}
MCP Delivery Configuration (Optional)
Delivery pushes event data into your own tools. It never buys, reserves 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. |
mcpArguments | object | {} | Arguments passed to the tool. String leaves support {placeholders}. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Summary & chunked placeholders: {location}, {sources} (comma-separated list of the sources that actually returned data), {eventCount}, {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 of the merged run:
{"source": ["allevents", "meetup", "eventbrite"],"location": "mumbai","limit": 50,"mcpConnector": "<your-authorized-slack-connector>","deliveryMode": "summary","mcpTool": "send_message","mcpArguments": { "channel": "#events", "text": "{message}" },"mcpMessageTemplate": "📅 {eventCount} events in {location} from {sources}:\n\n{events}"}
A large merged set split across Notion sub-pages:
{"source": ["allevents", "eventseye", "meetup", "eventbrite"],"location": "berlin","limit": 50,"mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "chunked","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": { "title": "Events in {location} — part {part}/{partCount}" },"content": "{events}"}]}}
One Airtable row per event, keeping the source tag:
{"source": ["eventseye"],"location": "dubai","limit": 50,"mcpConnector": "<your-authorized-airtable-connector>","deliveryMode": "perEvent","mcpTool": "create_record","mcpArguments": {"fields": {"Platform": "{source}","Event": "{title}","Date": "{date}","City": "{city}","Venue": "{venue}","Address": "{venue_address}","Price": "{price}","Link": "{url}"}}}
📤 Output Contract & Data Structure
Storage: Apify Dataset — one JSON object per event, pushed as each source scrapes.
Pagination: limit & offset on the dataset items endpoint.
Duplicates: not deduplicated across sources. The same event on two platforms is two records. Dedupe on url, or on title + date for cross-platform matching.
The unified record — 8 fields, identical for every source
| Field | Type | Empty value | Meaning | Populated by |
|---|---|---|---|---|
source | string | never empty | Which platform produced this record: allevents, eventseye, district, meetup, eventbrite | all |
title | string | "N/A" | Event or exhibition name | all |
url | string | "N/A" | Link to the event on its platform — the dedupe key | all |
date | string | "N/A" | Start date/time — format differs per source, not normalised | all |
city | string | "N/A" | City (echoed from your location on most sources) | all |
venue | string | "N/A" | Venue or location name | all |
venue_address | string | "N/A" | Street address | ⚠️ eventseye, meetup, eventbrite only |
price | string | "N/A" | Price as a display string, or Free | ⚠️ district, meetup, eventbrite only |
⚠️
"N/A"is a string, notnull, and it has two different meanings. Onpriceandvenue_addressit may mean this source never provides that field (see the column above) rather than this event didn't publish one. Checksourcebefore concluding anything about a gap.
There is no description, image, category, organiser, lineup, availability or ID field. Eight fields is the whole contract.
Example — eventseye (trade show, has an address, never a price)
{"source": "eventseye","title": "GULFOOD","url": "https://www.eventseye.com/fairs/f-gulfood-1234.html","date": "Feb 2027","city": "Dubai","venue": "Dubai World Trade Centre","venue_address": "Sheikh Zayed Road, Dubai, United Arab Emirates","price": "N/A"}
Example — meetup (ISO date, price often Free)
{"source": "meetup","title": "Mumbai Python User Group — Monthly Meetup","url": "https://www.meetup.com/mumbai-python/events/301234567/","date": "2026-09-18T18:30:00+00:00","city": "mumbai","venue": "WeWork Enam Sambhav","venue_address": "C-20, G Block, Bandra Kurla Complex, Mumbai, MH","price": "Free"}
Example — allevents (display date, no price, no address by design)
{"source": "allevents","title": "Sunburn Arena ft. Martin Garrix","url": "https://allevents.in/mumbai/sunburn-arena/1234567890","date": "Sat, 12 Sep, 7:00 pm","city": "Mumbai","venue": "NSCI Dome, Worli","venue_address": "N/A","price": "N/A"}
Working with a merged set
from collections import Counter# Always branch on `source` - "N/A" can mean "this source never provides it".print(Counter(e["source"] for e in items)) # coverage per platformpriced = [e for e in items if e["price"] != "N/A"] # district / meetup / eventbrite onlyaddressed = [e for e in items if e["venue_address"] != "N/A"]# `date` is NOT normalised - only parse within a source you know the format of.meetup_dated = [e for e in items if e["source"] == "meetup" and e["date"] != "N/A"]# No cross-platform dedup is done for you.seen, unique = set(), []for e in items:key = (e["title"].strip().lower(), e["date"])if key not in seen:seen.add(key)unique.append(e)print(f"{len(items)} records → {len(unique)} after cross-platform dedupe")
What a successful run looks like
{"status": "SUCCEEDED","defaultDatasetId": "<datasetId>","stats": { "itemCount": 137 }}
itemCount below limit × len(source) is normal — some sources return less, and district frequently returns nothing at all without a proxy. itemCount: 0 is a valid empty result. Read the per-source log lines to tell the causes apart:
| Log line | Meaning |
|---|---|
Running scraper: '<source>' | That source started — one line per selected source |
Unknown source: '<value>' — skipping. | A typo or unsupported value; nothing ran for it |
[allevents] Input 'location' is missing! | location was empty |
[allevents] Failed to load page | The listing page didn't load |
[<source>] ✔ Scraped N events | That source's own count |
Free plan detected — capping results to 2 per source. | Free-plan cap applied |
Charge failed for '<event>' | Billing event failed; the record was still pushed |
▶️ Invocation & Integration
Lifecycle: Discover → Understand → Configure → Execute → Retrieve → Validate. Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor → Input tab → select one or more Sources → type a Location → set Max Events (per source) → (for Meetup) set Country code to match the city → (optional) pick an MCP connector, tool and template → Start → export from the Dataset tab (JSON, CSV, Excel, XML).
Model Context Protocol (MCP)
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/universal-event-scraper"
Python (apify-client)
import osfrom collections import Counterfrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))ACTOR = "techforce.global/universal-event-scraper"run = client.actor(ACTOR).call(run_input={"source": ["allevents", "eventseye", "meetup", "eventbrite"],"location": "mumbai","limit": 50, # PER SOURCE → up to 200 records here"country_code": "in", # Meetup only; must match the city's country"deliveryMode": "none",})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(f"{len(items)} records")print(Counter(e["source"] for e in items)) # which platforms actually delivered# "N/A" is a STRING sentinel, and on price/venue_address it may simply mean# "this source never provides that field".for event in items[:10]:price = event["price"] if event["price"] != "N/A" else "—"print(f'[{event["source"]:<10}] {event["title"][:45]}')print(f'{"":<13} {event["date"]} · {event["venue"]} · {price}')
Multi-city sweep — one run per city, since location is a single string:
CITIES = ["mumbai", "delhi", "bengaluru"]all_events = []for city in CITIES:run = client.actor(ACTOR).call(run_input={"source": ["allevents", "eventbrite"],"location": city,"limit": 50,"deliveryMode": "none",})all_events += client.dataset(run["defaultDatasetId"]).list_items().itemsprint(f"{len(all_events)} events across {len(CITIES)} cities")
JavaScript / TypeScript (apify-client)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const ACTOR = 'techforce.global/universal-event-scraper';const run = await client.actor(ACTOR).call({source: ['eventseye', 'eventbrite'],location: 'berlin',limit: 50,deliveryMode: 'none',});const { items } = await client.dataset(run.defaultDatasetId).listItems();// Group by platform - the `source` tag is what makes a merged set usable.const bySource = items.reduce<Record<string, number>>((acc, e) => {acc[e.source] = (acc[e.source] ?? 0) + 1;return acc;}, {});console.log(bySource);// price is only ever real on district / meetup / eventbriteconst priced = items.filter((e) => e.price !== 'N/A');console.log(`${priced.length} of ${items.length} records carry a price`);
cURL — asynchronous with a spend cap (recommended)
Sources run sequentially, so a multi-source run takes minutes. 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 '{"source":["allevents","eventseye","meetup","eventbrite"],"location":"mumbai","limit":50,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~universal-event-scraper/runs?maxTotalChargeUsd=2.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 (single source, small limit only)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"source":["eventseye"],"location":"dubai","limit":20,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~universal-event-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 | Sources run sequentially; multi-source runs exceed 300s | Reconfigure: async POST /runs + polling. |
MISSING_SOURCE | Input validation rejection | source is required | Modify Input: supply an array with at least one known value. |
UNKNOWN_SOURCE | SUCCEEDED, 0 items, log Unknown source: '<x>' — skipping. | Typo or unsupported platform name | Modify Input: use allevents, eventseye, district, meetup, eventbrite. |
MISSING_LOCATION | Log [allevents] Input 'location' is missing! | location empty | Modify Input: supply a city. |
VOLUME_UNDERESTIMATED | Far more records than expected | limit is per source | Fix Expectation: total ≈ limit × source count. |
UNLIMITED_RUNAWAY | Very large run, high spend | limit: 0 removes the cap entirely | Reconfigure: set a real limit and maxTotalChargeUsd. |
ALLEVENTS_CAPPED_100 | allevents stops at 100 with limit: 0 | That source caps at 100 regardless | Fix Expectation: documented behaviour. |
FREE_PLAN_CAP | 2 records per source, upgrade banner in the log | Free Apify plan cap | Prompt User: upgrade for full-volume runs. |
DATE_FILTER_IGNORED | dateRange set, results outside it | Not implemented — the input is read by nothing | Re-route: Smart Event Scraper, or filter the dataset. |
KEYWORD_FILTER_IGNORED | keywords set, unrelated results | Not implemented | Re-route or filter the dataset. |
EVENTBRITE_TOGGLE_IGNORED | eventbriteSearch: false yet Eventbrite ran | The toggle is inert; source controls it | Modify Input: remove eventbrite from source. |
DISTRICT_EMPTY | district selected, zero district records | No proxy support; District blocks datacenter IPs | Re-route: District.in Event Scraper with a residential-IN proxy. |
MEETUP_EMPTY | meetup selected, zero meetup records | country_code doesn't match the location's country | Modify Input: set country_code correctly (e.g. ae for Dubai). |
SOURCE_EMPTY | One source returns nothing, others work | That platform has no coverage for the city, or its layout changed | Accept, or drop that source. Check its [<source>] log lines. |
NA_NOT_NULL | Null checks pass but values unusable | Missing values are the string "N/A" | Fix Code: compare against "N/A". |
FIELD_NEVER_POPULATED | price or venue_address always "N/A" | That source never provides it — not a failure | Fix Expectation: check the source-capability table. |
DATE_FORMAT_MIXED | Date parsing fails on some records | date is not normalised across sources | Fix Code: branch on source before parsing. |
DUPLICATE_ACROSS_SOURCES | The same event twice | No cross-platform dedup is performed | Fix Code: dedupe on title + date. |
MULTI_CITY_UNSUPPORTED | Agent passes a city array | location is a single string | Loop: one run per city. |
NO_EVENT_DETAIL | No description, image, organiser or lineup | Eight fields only | Fix Expectation: open url. |
CHARGE_FAILED | Log Charge failed for '<event>' | The billing event call failed | Not data loss — the record was still pushed. Report to support if persistent. |
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_EMPTY | Connector called, body empty | mcpArguments omitted the {message} placeholder | Modify Input: map {message} inside mcpArguments. |
DELIVERY_FLOOD | Hundreds of 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 before all sources finished | 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 source-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 |
|---|---|---|
| "Get me all the events in Mumbai from every platform" | Multi-source sweep | {"source":["allevents","eventseye","meetup","eventbrite"],"location":"mumbai","limit":50} |
| "What trade shows are happening in Dubai?" | B2B only | {"source":["eventseye"],"location":"dubai","limit":50} |
| "Find tech meetups in Bengaluru" | Community | {"source":["meetup"],"location":"bengaluru","country_code":"in"} then filter title |
| "Online meetups I can join from anywhere" | Online only | {"source":["meetup"],"location":"london","country_code":"gb","eventType":"online"} |
| "Workshops and classes in Berlin" | Organizer-led | {"source":["eventbrite"],"location":"berlin","limit":50} |
| "Compare how many events each platform lists for Delhi" | Coverage analysis | run all sources, then group by source |
| "Consumer events and business expos side by side" | Mixed dataset | {"source":["allevents","eventseye"],"location":"<city>"} |
| "Which of these events are free?" | Price filter | run, then filter price == "Free" (only 3 sources populate price) |
| "Build an event feed for three cities" | Multi-city | Loop one run per city — location is a single string |
| "Push the merged feed into Notion" | Delivery | {"mcpConnector":"notion","deliveryMode":"chunked","mcpTool":"notion-create-pages"} |
| "Post a daily summary to Slack" | Digest delivery | schedule + {"deliveryMode":"summary","mcpTool":"send_message"} |
| "Only events next weekend" | Date filter | ⚠️ dateRange is ignored — ➡️ Smart Event Scraper, or filter date afterwards |
| "Only events matching 'AI'" | Keyword filter | ⚠️ keywords is ignored — filter title afterwards |
| "Why did District return nothing?" | Diagnosis | ⚠️ No proxy support here — ➡️ District.in Event Scraper |
| "Remove the duplicates across platforms" | Dedup | ⚠️ Not done for you — dedupe on title + date |
| "Get me the event description and image" | Detail | ❌ Eight fields only — open url |
| "How many tickets are left?" | Inventory | ❌ Not returned |
| "Book two tickets" | Transaction | ❌ Read-only |
Should NOT route here: date-range, keyword, category or price filtering at the source · several cities in one run · cross-platform deduplication · District.in data specifically (no proxy support) · event descriptions, images, organisers or lineups · ticket availability, seat maps or inventory · buying or reserving tickets · depth on a single platform where the dedicated Actor offers more fields.
🧭 Agent Execution & Routing Logic
[Input User Query]│▼1. Does the user need FILTERING at the source (date, keyword, category, price)?│ YES → Route to Smart Event Scraper. This Actor's dateRange/keywords inputs│ are ACCEPTED AND IGNORED — promising them here is wrong│ NO▼2. Do they care about only ONE platform?│ YES → prefer the dedicated Actor (more fields, more options):│ allevents.in → All Events Scraper│ eventbrite → Eventbrite Scraper│ district.in → District.in Event Scraper (has the proxy it needs)│ eventseye → Events Eye Scraper│ NO (they want several platforms in one shape) → continue▼3. Pick the sources. Note per-source capability:├── price → district | meetup | eventbrite ONLY├── venue_address → eventseye | meetup | eventbrite ONLY├── trade shows → eventseye (and it is HTTP-only, so cheapest)└── district → will likely return NOTHING here (no proxy support)▼4. One city per run. Several cities → loop.▼5. Set limit PER SOURCE (1-50; 0 = unlimited, allevents still caps at 100)│ ⚠️ total ≈ limit × source count, and that is the billable count▼6. Using meetup? country_code MUST match the location's country(Dubai → "ae", London → "gb", Mumbai → "in")▼7. Deliver to a connector? YES → mcpConnector + mcpTool + {message}│ summary (1 call) / chunked / perEvent (1 per event!)│ NO → set deliveryMode "none" (it defaults to summary)▼8. Sources run SEQUENTIALLY → run time is the SUM. Use the ASYNC endpoint▼[Execute]│├──► SUCCEEDED, items > 0 ──► handle the merged contract:│ ALWAYS branch on `source` first│ "N/A" is a STRING, and on price/venue_address may mean│ "this source never provides it" — not a scrape failure│ `date` is NOT normalised → parse per source, never sort the merged set raw│ NO cross-platform dedup → dedupe on title + date yourself│└──► SUCCEEDED, items == 0 ──► READ THE PER-SOURCE LOG LINES:"Unknown source: '<x>' — skipping." → fix the source value"[allevents] Input 'location' missing" → supply a citydistrict selected but empty → expected: no proxy supportmeetup empty → country_code mismatch
💰 Cost Control & Pricing Transparency
Pay-per-event: one charge per record pushed to the dataset, with no monthly subscription. The exact rate is on the Actor's Pricing tab in the Apify Store.
Free Apify plans are capped at 2 records per source, so a five-source run returns up to 10 items — enough to validate a configuration before committing.
Autonomous spend caps: pass maxTotalChargeUsd as a query parameter on the run endpoint for a hard per-execution ceiling.
⚠️ Verify before publishing: the code charges an event named
premium-scrapefor every source. It also defines abasic-scrapeevent, but no source maps to it, sobasic-scrapecan never fire. There is no.actor/pay_per_event.jsonin the repo, so both events are configured only on the Store listing — confirm the live event configuration (and whetherbasic-scrapeshould exist at all) before publishing.
The multiplier to watch: limit is per source
| Sources selected | limit | Maximum records (= billable events) |
|---|---|---|
1 (eventseye) | 50 | 50 |
| 2 | 50 | 100 |
| 4 | 50 | 200 |
| 5 | 50 | 250 |
| Any | 0 (unlimited) | Unbounded — allevents still caps at 100, the rest do not |
| Any | 2 (free plan) | 2 × source count |
Platform usage is billed on top, and it varies sharply by source:
| Source | Engine | Relative platform cost |
|---|---|---|
eventseye | httpx, no browser | 💚 Lowest — plain HTTP |
eventbrite | Public browse JSON | 💚 Low |
allevents | Playwright | 🟠 Browser cost |
meetup | Playwright + GraphQL intercept | 🟠 Browser cost |
district | Playwright + 5-page pool | 🔴 Highest — and usually returns nothing here |
Cost levers:
- Drop
districtunless you have a specific reason. It's the most expensive source and, without proxy support, typically yields nothing. - Start with
["eventseye"]or["eventbrite"]if cost matters — both avoid a browser entirely. - Trim the source list. Every extra source multiplies both the record count and the run time.
- Never use
limit: 0withoutmaxTotalChargeUsd. It removes the cap completely. - Validate at
limit: 2before a full sweep. - Dedupe before charging your own downstream systems — the same event on two platforms is billed twice here.
- Prefer
summary/chunkeddelivery overperEvent— 1 connector call instead of hundreds.
🔍 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 — the 5-value source enum, location, limit, the Meetup-only country_code/eventType, the 5 delivery fields, and the 5 inputs explicitly marked as not implemented |
.actor/dataset_schema.json | Full JSON Schema for the record (a oneOf branch per source, so each platform's variant is described individually) plus the Console Output view |
.actor/output_schema.json | Declares where results are stored |
my_actor/main.py | All five scrapers, the five normalisers, the per-source free-plan cap, charging, and MCP delivery |
my_actor/connector.py | MCP connector session handling through the Apify MCP Proxy |
Note: this Actor has no pay_per_event.json — the premium-scrape (and unused basic-scrape) events are configured on the Store listing.
If any table on this page disagrees with the schema files, the schema files win.
🛠️ Troubleshooting
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | 0 items, log Unknown source: '<x>' — skipping. | Typo in a source value | Use allevents, eventseye, district, meetup, eventbrite. |
| 2 | Log [allevents] Input 'location' is missing! | location empty | Supply a city name. |
| 3 | Far more records than expected | limit is per source | Total ≈ limit × source count. Trim sources. |
| 4 | Exactly 2 per source | Free Apify plan cap | Upgrade; the log prints the upgrade banner. |
| 5 | allevents stopped at 100 with limit: 0 | That source caps at 100 regardless | Documented behaviour. |
| 6 | dateRange had no effect | Not implemented | Filter the dataset's date, or use Smart Event Scraper. |
| 7 | keywords had no effect | Not implemented | Filter the dataset's title. |
| 8 | Eventbrite ran despite eventbriteSearch: false | That toggle is inert | Remove eventbrite from source. |
| 9 | district returned nothing | No proxy support; District blocks datacenter IPs | Use District.in Event Scraper with a residential-IN proxy. |
| 10 | meetup returned nothing | country_code doesn't match the city's country | Set it correctly — ae for Dubai, gb for London. |
| 11 | One source empty, others fine | No coverage for that city, or a layout change | Check that source's [<source>] log lines. |
| 12 | price always "N/A" | Only district, meetup, eventbrite populate it | Expected for allevents/eventseye. |
| 13 | venue_address always "N/A" | Only eventseye, meetup, eventbrite populate it | Expected for allevents/district. |
| 14 | Null checks never fire | Missing values are the string "N/A" | Compare against "N/A". |
| 15 | Date parsing fails on some rows | date is not normalised across sources | Branch on source before parsing. |
| 16 | Sorting the merged set by date misorders it | Mixed formats — display strings vs ISO 8601 | Normalise per source first. |
| 17 | The same event appears twice | No cross-platform dedup | Dedupe on title + date. |
| 18 | Run takes several minutes | Sources run sequentially, several with browsers | Expected; use the async endpoint. |
| 19 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard 300-second ceiling | Use async POST /runs → poll → fetch. |
| 20 | Log Charge failed for '<event>' | The billing call failed | The record was still saved — no data loss. |
| 21 | Only one city came back | location is a single string | Loop one run per city. |
| 22 | Hundreds of 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 | Slack / Notion received nothing | Delivery needs both mcpConnector and mcpTool | Set both. The dataset is still written in full. |
| 25 | Connector fired but the body is empty | mcpArguments did not reference {message} | Map {message} and populate mcpMessageTemplate. |
| 26 | 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. |
| 27 | HTTP 401 / 403 from Apify | Missing, expired or malformed APIFY_TOKEN | Regenerate in Apify Console → Settings → API & Integrations. |
| 28 | HTTP 429 from Apify | Apify account concurrency / rate limits | Retry with exponential backoff (2s → 4s → 8s). |
Known limitations
- No proxy support. There is no proxy input at all. This mainly hurts the
districtsource, which needs a residential Indian IP to work. dateRangeandkeywordsare not implemented, along witheventbriteApiKey,eventbriteSearchandeventbriteErrorHandling. All five are accepted and ignored.dateis not normalised across sources — a display string on some, ISO 8601 on others.- No cross-platform deduplication, so the same event on two platforms is two records.
- Eight fields only — no description, image, category, organiser, lineup, availability or ID.
- One city per run, and sources run sequentially, so wall-clock time is the sum of the selected sources.
Diagnostic checklist before opening an issue
- Single-source baseline.
{"source":["eventseye"],"location":"dubai","limit":5,"deliveryMode":"none"}— HTTP-only, no browser, fastest to prove the pipeline works. - Items returned? If yes, the pipeline is fine — the problem is specific to another source or city.
- Read the per-source log lines. Each source logs
Running scraper: '<x>'and its own[<source>]messages; that tells you which one produced nothing. - Rule out the known-empty cases first.
districtwithout a proxy, andmeetupwith a mismatchedcountry_code, are the two most common "it returned nothing" reports. - Confirm the counting model. Unexpected volume is almost always
limitbeing per-source rather than per-run.
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 which source fell short.
❓ FAQ
Scope & sources
What does "universal" actually mean here?
Five platforms in one run, normalised to one record shape. It isn't a generic any-website event scraper — the five sources are fixed and each has purpose-built extraction.
Do I need accounts or API keys for the sources?
No. All five read publicly available listings. (The eventbriteApiKey field in the schema is a leftover and is not used.)
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/universal-event-scraper"
Which source should I pick if I only want one?
Then use the dedicated Actor instead — All Events, Eventbrite, District.in or Events Eye. Each returns more fields and more options than the unified 8-field record.
Why does the district source return nothing?
District fingerprints the caller's IP and hides its location picker from datacenter ranges. This Actor has no proxy input, so it can't get past that. Use the District.in Event Scraper, which supports the residential-IN proxy that source requires.
Why did Meetup return nothing for a real city?
country_code has to match the city's country — it defaults to in. Dubai needs ae, London needs gb.
Filtering
Can I filter by date or keyword?
Not here. Both inputs exist in the schema but no code reads them — they're documented as not implemented. Use Smart Event Scraper, which does filter by category, keyword and date range across six platforms, or filter this Actor's dataset afterwards.
Can I scrape several cities in one run?
No — location is a single string. Loop one run per city.
Output
Is the output really identical across sources?
Yes — all five normalisers emit exactly the same eight keys in the same order. What differs is which of them a given source can actually fill.
Why is price always "N/A" for some sources?
Because those sources don't provide it: allevents and eventseye hardcode "N/A". Only district, meetup and eventbrite populate it. Same story for venue_address, which only eventseye, meetup and eventbrite fill.
Why "N/A" instead of null?
That's the Actor's convention for every absent value. Test == "N/A" rather than relying on null checks.
Can I sort the merged set by date?
Not on the raw value — date is not normalised. Meetup can return ISO 8601 with a timezone while AllEvents returns something like Sat, 12 Sep, 7:00 pm. Parse per source first.
Will the same event appear twice?
Yes, if two platforms list it. There's no cross-platform deduplication; dedupe on title + date.
Pricing
How is this billed?
Pay-per-event — one charge per record pushed. The current rate is on the Actor's Pricing tab. Apify platform usage is billed separately and depends heavily on which sources you pick (eventseye is HTTP-only and cheapest; district is the most expensive).
How do I keep a run cheap?
Fewer sources, prefer eventseye/eventbrite, drop district, and never use limit: 0 without a spend cap.
How do I guarantee an agent never overspends?
Pass maxTotalChargeUsd as a query parameter on the run endpoint.
🔗 Related Actors
This Actor's place in the event family
| Actor | Sources | Filters? | Why pick it over this one |
|---|---|---|---|
| This Actor | 5 platforms | ❌ None | One unified 8-field schema across five platforms in one run |
| Smart Event Scraper | 6 platforms | ✅ Category, keyword, date range | ⭐ Same multi-platform idea plus the filtering this Actor lacks, with per-source limits |
| All Events Scraper | allevents.in | ❌ | Depth on one platform — more fields than the unified record |
| Eventbrite Scraper | eventbrite.com | ⚠️ Some | Depth on Eventbrite, incl. category and location handling |
| District.in Event Scraper | district.in | ❌ | Has the residential-proxy support District requires |
| Events Eye Scraper | eventseye.com | ⚠️ Some | Depth on trade shows and exhibitions |
| Eventim Scraper | eventim.de | ✅ Search, city, category, date | German ticketed events with live price and stock |
| Ticketmaster FR/ES Scraper | Ticketmaster API | ⚠️ Category only | French and Spanish ticketed events via the official API |
⭐ Smart Event Scraper is the closer sibling — compare them before choosing. It covers six platforms (including District) and takes a
category, a free-textsearch_queryand adate_from/date_torange, with a per-source limit and one standardised output schema. If you want filtering, go there.Choose this Actor when you specifically want these five sources normalised into one flat 8-field record and don't need filtering at the source.
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 an organiser's or venue's own website |
| Hospitality Lead Scraper | Bars, hotels and restaurants near a venue, for event-driven local outreach |
Suggested pipeline patterns
📊 Platform coverage audit
Run all sources for one city → group by source → see which platform actually covers that market before committing to a single-source Actor.
🌍 Mixed consumer + B2B feed
["allevents","eventseye"] → one dataset holding both consumer events and trade shows, already in one shape.
💸 Cheap-first two-pass
Pass 1 ["eventseye","eventbrite"] (no browsers) → if coverage is thin, pass 2 adds ["allevents","meetup"].
🎯 Event-driven local sales Large upcoming events in a city → Google Maps Business Leads or Hospitality Lead Scraper for nearby venues → pitch catering, transport or accommodation.
🏢 Trade-show prospecting
["eventseye"] per target city → Website Contact Scraper on exhibitor sites → a contactable exhibitor list.
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 AllEvents, EventsEye, District, Zomato, Meetup, Eventbrite, or any venue, organiser or performer named in its output. All trademarks are the property of their respective owners.
It reads publicly available event listings from five platforms. It does not log in, does not accept credentials, and never buys, reserves or contacts anything.
The output is mostly commercial event data — titles, venues, dates, prices, public URLs. Three things still deserve care:
- Meetup and Eventbrite listings can carry personal data. Community meetups are often organised by named individuals, and a group or event title can identify a person. 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 as a marketing list simply because it was public.
- Each source has its own terms of service, and you are bound by all five you enable. They are not interchangeable: a use that's acceptable for a public trade-show directory may not be for a ticketing platform. Review the terms of each
sourceyou select. - Prices and availability are volatile and time-stamped by the run. Republishing them as current commits you to a refresh cadence; always attribute the source and link through to
urlfor the live listing. Note also thatpriceis absent by design on two of the five sources, so a merged feed will have systematic gaps.
Because sources run sequentially at modest volume, the footprint on each site is light — don't defeat that by running many parallel executions against the same source and city. You remain responsible for complying with every enabled platform's terms of service and all applicable data-protection and consumer-protection law in the markets you operate in.
🆘 Support & Custom Pipeline Engineering
Need date and keyword filtering wired in, proxy support for the District source, cross-platform deduplication, additional platforms, or a scheduled multi-city event 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": "Universal Event Scraper — 5 Event Platforms, One Unified Schema","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Scrapes events for one city from up to five platforms — AllEvents.in, EventsEye.com, District.in, Meetup.com and Eventbrite — in a single run, normalising every source into the same eight-field record: source, title, url, date, city, venue, venue_address and price. Each source uses a purpose-built engine (plain HTTP for the EventsEye trade-show directory, Playwright for the rendered listings, GraphQL interception for Meetup, public browse JSON for Eventbrite), and results can be delivered into Notion, Slack, Airtable, Google Sheets or any authorized MCP connector. Date-range and keyword filtering are not supported.","url": "https://apify.com/techforce.global/universal-event-scraper","offers": {"@type": "Offer","priceCurrency": "USD","description": "Pay-per-event, charged once per record returned. The current rate is listed on the Actor's Pricing tab."}}


