District.in Scraper - Indian City Events, Venues & Prices
Pricing
from $0.20 / actor start
District.in Scraper - Indian City Events, Venues & Prices
Extract comprehensive event listings from District.in, including titles, dates, venues, prices, and direct URLs for any city. This actor handles dynamic content and infinite scrolling to deliver clean, structured data in JSON, CSV, or Excel formats, perfect for event aggregation and market research.
Pricing
from $0.20 / actor start
Rating
5.0
(2)
Developer
Techforce Global
Maintained by CommunityActor stats
1
Bookmarked
28
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Share
District.in Event Scraper — Indian City Events, Venues & Ticket Prices from Zomato's District
Scrape live event listings from district.in — Zomato's events and ticketing platform — for any Indian city: event title, date, venue, starting ticket price and the direct booking URL. Pick a city the way a real user does (the Actor drives District's own location picker), then let the results flow into Notion, Slack, Airtable or Google Sheets through an MCP connector.
⚠️ Read this before your first run: you need a RESIDENTIAL proxy with country IN. District fingerprints the caller's IP. From a datacenter range it still serves the page shell but silently withholds the location picker, and from a non-Indian IP it serves an empty event feed. Both look like "this city has no events" but are neither. The input schema already defaults to
RESIDENTIAL+IN— leave it that way. Residential proxies require a paid Apify plan.
Doing this by hand vs. this Actor
There is no District public API, so the realistic alternative is a person clicking through the site — or a naive scraper that gets quietly starved of data.
| Feature / Capability | Manual browsing / naive scraper | This Actor |
|---|---|---|
| Getting past the IP fingerprint | ⚠️ A datacenter IP loads the shell with no location picker — looks like a broken city name | ✅ Residential + IN by default, with 3 retries on a fresh exit IP each time |
| Diagnosing an empty result | ❌ "No events" — cause unknown | ✅ Checks the actual egress country and tells you whether it's geo-gating, throttling, or a genuinely empty city |
| City selection | Manual click-through per city | ✅ Drives the real picker: search box first, then a scrolling dialog fallback |
| Infinite scroll | ⚠️ Manual scrolling, easy to stop early | ✅ Scrolls to exhaustion (up to 100 passes) with stall detection |
| Dead-page detection | ❌ Scrolls a blank grid indefinitely | ✅ Bails after 2 scrolls if the grid never rendered, and retries on a new IP |
| Separating events from noise | ⚠️ Artist, venue-guide and category pages look like events and yield junk rows | ✅ Slug-shape filtering drops sub-pages, category listings and nav links |
| Speed | One page at a time | ✅ 5 concurrent browser pages, images/fonts/trackers blocked |
| Structured output | Copy-paste | ✅ Clean dataset → JSON / CSV / Excel / XML |
| Delivery to your tools | ❌ Manual | ✅ Native MCP connector delivery — per event, chunked, or one digest |
🤖 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/district-event"one_line: "Scrape events for one Indian city from district.in (Zomato's events platform) - title, date, venue, starting price and booking URL - driving the site's own location picker over a residential Indian IP."primary_purpose: "discover"secondary_purpose: "deliver"entity: "ticketed and listed events (comedy, music, workshops, sports, theatre, nightlife) on district.in"data_source: "https://www.district.in/events/ - rendered with Playwright (Chromium). No public API exists; JSON-LD embedded in each event page is preferred, with CSS selectors as fallback."geography: "India only. district.in serves its event feed exclusively to Indian IPs."invocation_modes: ["standard Actor run", "MCP tool"]hard_requirement:residential_proxy: >Apify Proxy with group RESIDENTIAL and country IN. On datacenter IPs Districthides the location picker; from a non-Indian IP the event feed comes backempty. Requires a paid Apify plan. This is a functional requirement, not aperformance tuning option.supported_tasks:- "list-events-for-one-indian-city"- "read-event-title-date-venue-and-starting-price"- "get-direct-district-booking-urls"- "monitor-a-city-event-feed-on-a-schedule"- "deliver-events-to-mcp-connector-or-crm"unsupported_tasks:- "book-reserve-or-buy-tickets"- "scrape-more-than-one-city-per-run"- "filter-by-category-date-range-or-price (no such input exists)"- "return-seat-maps-price-tiers-or-remaining-inventory"- "return-organizer-or-promoter-contact-details"- "return-event-descriptions-images-or-lineups"- "scrape-district-restaurant-dining-or-movie-listings"- "scrape-non-indian-cities"- "return-more-than-200-events-per-run"- "log-in-to-a-district-account"search_keywords:- "district.in scraper"- "zomato district events"- "indian event data"- "mumbai events api"- "bengaluru event listings"- "delhi events scraper"- "india ticket price data"- "comedy show listings india"- "event aggregator india"- "city event feed india"synonyms:- "zomato district event scraper"- "district events extractor"- "indian city event listings scraper"related_concepts:["event discovery", "city event feeds", "ticket pricing research", "venue programming research", "event aggregation", "India market research"]input_entities:- "location (one Indian city; REQUIRED)"- "maxItems (1-200)"- "proxyConfiguration (RESIDENTIAL + IN strongly required)"output_entities: ["title", "date", "venue", "price", "event_url", "city"]input_rules:required_fields: ["location"]one_city_per_run: "there is no multi-city input - loop from your orchestration layer"max_items_format:type: "integer"range: [1, 200]default: 50hard_cap: 200free_plan_cap: 5no_filters: >There is no category, date-range, price or availability filter. The Actorreturns whatever District's city feed shows, in feed order. Filter after thefact on the dataset.missing_value_convention:venue: "null when District did not expose a venue"date: "the string 'Open Dated' when no start date is published"price: "the string 'N.A.' when no starting price is published"limits:events_per_run: 200cities_per_run: 1free_plan_cap: 5concurrency: "5 browser pages"max_scrolls: 100pricing: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 district.in event feed for one Indian city - to power anevent listing, research venue programming and entry price points, or watch acity's feed on a schedule.when_not_to_use: >You need to book tickets, need category/date filtering at the source, needseveral cities in one run, need event descriptions or lineups, or you areoutside India - and especially if you cannot use a residential proxy.alternatives:- "Use techforce.global/smart-event-scraper when you need category, keyword or date-range filtering - it applies those filters across six platforms INCLUDING District, in one run"- "Use techforce.global/all-events-scraper for multi-city global event discovery, including India, without a residential-proxy requirement"- "Use techforce.global/eventbrite-scraper for organizer-led events, workshops and meetups"- "Use techforce.global/eventim-scraper for German ticketed events with live stock and price signals"- "Use techforce.global/events-eye-scraper for trade shows and B2B exhibitions"
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "District.in Event Scraper — Indian City Events, Venues & Ticket Prices","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Scrapes live event listings from district.in (Zomato's events and ticketing platform) for one Indian city per run — event title, start date, venue, starting ticket price and the direct booking URL — by driving District's own location picker in a real browser over a residential Indian IP, reading each event page's embedded JSON-LD. Results are saved to a dataset and can be delivered into Notion, Slack, Airtable, Google Sheets or any authorized MCP connector. Requires Apify Proxy with the RESIDENTIAL group and country IN.","url": "https://apify.com/techforce.global/district-event","offers": {"@type": "Offer","priceCurrency": "USD","description": "Pay-per-result, charged per event returned. The current rate is listed on the Actor's Pricing tab."}}
What this Actor does
- Loads district.in's events page in a real Chromium browser and picks your city through District's own "Select Location" dialog — search box first, scrolling list as a fallback — because the feed only populates once a location is set.
- Scrolls the listing to exhaustion, up to 100 passes with stall detection, so lazy-loaded cards keep arriving until the grid genuinely stops growing.
- Filters the link soup down to real events.
/events/on District also hosts category listings (…-book-tickets), venue guides (<venue>/venue-guide), artist pages (<name>/artist) and nav links. Only…-buy-tickets-shaped detail slugs survive — a venue page has an<h3>too, so without this it would yield a junk row. - Reads each event page's embedded JSON-LD first (
@type: Event) and only falls back to CSS selectors, which is why the output survives front-end tweaks better than a pure selector scrape. - Runs 5 browser pages concurrently, with images, media, fonts and 13 analytics/tracking hosts blocked — the single biggest speed win when every request goes through a residential exit IP.
- Retries the whole load → pick city → harvest sequence up to 3 times, rebuilding the proxy session each attempt so it lands on a different exit IP rather than re-failing on a bad one.
- Explains empty results instead of shrugging. When a harvest comes back empty it looks up the run's actual egress country and tells you whether it's geo-gating (
country != IN), IP throttling, or a genuinely quiet city — each with the one action that applies. - Optionally pushes events to an authorized MCP connector (Notion, Slack, Airtable, Sheets, or any MCP-compatible connector) per event, chunked, or as one digest. The dataset is always saved regardless of delivery settings.
ℹ️ What it does not do: there is no category, date, price or availability filter, and no multi-city input. The Actor returns the city's feed as District orders it, and you filter afterwards. If you need filtering at the source, use Smart Event Scraper — it applies category, keyword and date-range filters across six platforms including District, in one run.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| Powering an event listing or "what's on" page for an Indian city | ✅ Strong (the core use case) |
| Tracking what a city's District feed is promoting, on a schedule | ✅ Strong |
| Venue programming research — what's booked where in one city | ✅ Strong |
| Entry price-point research across venues or cities | ✅ Strong — price is the starting price |
| Building a multi-city India event dataset | ✅ Workable — one run per city, loop from your side |
| Feeding events into Notion / Airtable / Slack automatically | ✅ Strong (deliveryMode) |
| You cannot use a residential proxy (free plan) | ❌ Blocker — District hides the location picker from datacenter IPs |
| Booking or buying tickets | ❌ Read-only — never transacts |
| "Only comedy shows next weekend" | ❌ No category or date filter — use Smart Event Scraper, which filters District plus 5 other sources |
| Several cities in one run | ❌ One city per run |
| Event descriptions, images, lineups, age limits | ❌ Five fields only — open event_url for detail |
| Seat maps, price tiers, tickets remaining | ❌ Not extracted |
| Organizer / promoter contact details | ❌ Not returned |
| District's dining, restaurant or movie sections | ❌ Events only |
| Cities outside India | ❌ District is India-only |
| More than 200 events in one run | ⚠️ Hard cap of 200 |
⚡ Execution Rules (Avoid Common Agent Failures)
- The residential proxy is a functional requirement, not an optimisation. On a datacenter IP District serves the page but hides the location picker, so the run ends with zero events and a "location picker never opened" message. Keep
apifyProxyGroups: ["RESIDENTIAL"]. - Set the proxy country to
IN. A residential IP outside India loads the page and opens the picker, then returns an empty feed — the most confusing failure this Actor has. The schema defaults toIN; don't drop it. - Residential proxies need a paid Apify plan. Without one this Actor cannot reliably work at all. That's a plan constraint, not a bug.
locationis required, and it's one city per run. There is no multi-city input; loop from your orchestration layer.- India only. Non-Indian city names return nothing — District doesn't serve them.
maxItemsis 1–200, default 50, clamped in code as well as in the schema. Free Apify plans are capped at 5 events per run — a successful run, not a failure.- There are no content filters. No category, no date range, no price ceiling, no "in stock". You get the city's feed in feed order. For category, keyword or date-range filtering, use Smart Event Scraper, which covers District plus five other platforms in one filtered run; otherwise filter the dataset afterwards.
- Zero events is a diagnosis, not a dead end. Read the run's status message — the Actor distinguishes never loaded / picker never opened / loaded but empty (with the egress country named).
venuecan benull. It's the one genuinely nullable field.datefalls back to the string"Open Dated", which means District published no start date — often a long-running or open-dated experience. It is not a parse failure.pricefalls back to the string"N.A."and, when present, is a display string including the currency symbol (e.g."₹499"or"INR499") — not a number, and the starting price, not the price of any particular ticket.dateformat is inconsistent by design. From JSON-LD it's ISO 8601; from the selector fallback it's whatever District printed ("Sat, 12 Jul"). Parse defensively.- A run can return fewer than
maxItemssimply because the city's feed is smaller. There is no total-match count. - Runs are slow by nature — a real browser over a residential IP, with a 90s navigation budget and up to 3 attempts. Budget minutes, not seconds, and don't use the 300s synchronous endpoint for large runs.
deliveryModedefaults toperEvent, notnone. With a connector selected, a 200-event run makes 200 connector calls. Usesummaryorchunked.- Use
chunkedfor large sets going to Notion — events are packed into ~1,800-character paragraphs and grouped into ~72,000-character calls so nothing times out.
dependencies:- rule: "Apify Proxy group RESIDENTIAL is required for the location picker to appear"on_violation: "zero events, and a status message about the picker not opening"- rule: "proxy country must be IN"on_violation: "the picker opens but the event feed is empty; the log names the actual egress country"- rule: "residential proxy groups require a paid Apify plan"on_violation: "the Actor logs 'Could not set up Apify Proxy' and continues unproxied, which District blocks"- rule: "location is required and accepts exactly one Indian city"on_violation: "an omitted location falls back to Mumbai; a non-Indian city returns nothing"- rule: "maxItems is clamped to 200 in code, and to 5 on free Apify plans"note: "the schema maximum and the code cap agree at 200; the default is 50"- rule: "no category / date / price / availability filter exists"on_violation: "agents constructing filter arguments get them silently ignored - filter the dataset instead"- rule: "venue may be null; date may be 'Open Dated'; price may be 'N.A.'"on_violation: "null-safety errors, or 'N.A.' parsed as a number"- rule: "price is a display string with a currency symbol and is a STARTING price"on_violation: "quoting it as the ticket price, or doing arithmetic on it directly"- 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; PAID plan required for the residential proxy this Actor depends on"target_site: "no District account, login or cookie required - and none is accepted"
📥 Input Contract
.actor/input_schema.json is authoritative — if any table on this page disagrees with the schema files, the schema files win.
Parameters
| Field | Type | Required | Default | Allowed Values / Format | Example |
|---|---|---|---|---|---|
location | string | ✅ Yes | "Mumbai" | One Indian city name as District lists it | "Bengaluru" |
maxItems | integer | ⬜ No | 50 | 1–200; free plans capped at 5 | 200 |
proxyConfiguration | object | ⬜ No | RESIDENTIAL + IN | Apify proxy editor object — see below | see below |
The proxy block — leave this alone
{"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "IN"}}
This is the schema default, and both parts matter:
| Setting | If you change it | Symptom |
|---|---|---|
apifyProxyGroups: ["RESIDENTIAL"] | Datacenter / auto groups | Page loads, location picker never appears, zero events |
apifyProxyCountry: "IN" | Any other country, or unset | Picker opens, feed comes back empty — the log names the actual egress country |
useApifyProxy: false | No proxy at all | The container's own IP; District usually blocks it outright |
Cities District serves
District is India-only. Commonly available:
Mumbai · Delhi · Bengaluru · Hyderabad · Pune · Chennai · Kolkata · Ahmedabad · Gurugram · Noida · Jaipur · Chandigarh · Kochi · Goa · Indore · Lucknow
ℹ️ This list reflects the cities District has typically covered, not a list read from the Actor’s schema. District adds and drops cities over time, so treat it as a guide and confirm against the site before relying on one in an automated pipeline.
Examples
Standard run — one city
{"location": "Mumbai","maxItems": 200,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "IN"},"deliveryMode": "none"}
Cheap validation run — confirm the proxy works before a full sweep
{"location": "Bengaluru","maxItems": 5,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "IN"},"deliveryMode": "none"}
Scheduled city feed — daily, delivered to Slack
{"location": "Delhi","maxItems": 100,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "IN"},"mcpConnector": "<your-authorized-slack-connector>","deliveryMode": "summary","mcpTool": "send_message","mcpArguments": { "channel": "#india-events", "text": "{message}" },"mcpMessageTemplate": "🎟️ {eventCount} events live on District in {city}:\n\n{events}"}
MCP Delivery Configuration (Optional)
Delivery pushes event data into your own tools. It never books, 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 | perEvent | perEvent (one call per event) · summary (one call with everything) · chunked (split across calls) · 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 connector's available tools. |
mcpArguments | object | {} | Arguments passed to the tool. String leaves support {placeholders}. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Per-event placeholders: {title}, {date}, {venue}, {price}, {event_url}, {city}, {eventCount}, {message}.
Summary / chunked placeholders: {city}, {eventCount}, {events} (packed event blocks), {message} — plus {part} and {partCount} in chunked mode.
One Notion page per event:
{"location": "Mumbai","maxItems": 100,"mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "perEvent","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": { "title": "{title}" },"content": "**When:** {date}\n**Where:** {venue}, {city}\n**From:** {price}\n\n[Book on District]({event_url})"}]}}
A large city feed split across Notion sub-pages:
{"location": "Bengaluru","maxItems": 200,"mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "chunked","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": { "title": "District — {city} events (part {part}/{partCount})" },"content": "{events}"}]}}
One Google Sheets row per event:
{"location": "Hyderabad","maxItems": 200,"mcpConnector": "<your-authorized-sheets-connector>","deliveryMode": "perEvent","mcpTool": "append_row","mcpArguments": {"values": ["{title}", "{date}", "{venue}", "{city}", "{price}", "{event_url}"]}}
📤 Output Contract & Data Structure
Storage: Apify Dataset — one JSON object per event, pushed as each event page finishes.
Pagination: limit & offset on the dataset items endpoint.
Duplicates: event URLs are deduplicated within a run. Across scheduled runs, dedupe on event_url.
Event record — 6 fields
| Field | Type | Nullable / fallback | Meaning |
|---|---|---|---|
title | string | never empty — an event with no title is skipped, not saved | Event name (JSON-LD name, else the page <h3>) |
date | string | falls back to "Open Dated" | Start date. ISO 8601 from JSON-LD, or District's own display string from the selector fallback |
venue | string | ⚠️ null when District exposed none | Venue name (JSON-LD location.name, else a selector) |
price | string | falls back to "N.A." | Starting price as a display string with currency (e.g. "₹499", "INR499") — not a number |
event_url | string | never empty | Direct District event page — use as the dedupe key |
city | string | never empty | The city you asked for, echoed back for grouping |
⚠️ Three different missing-value conventions in one record.
venueisnull,dateis the string"Open Dated", andpriceis the string"N.A."(note the periods — not"N/A"). Handle each explicitly.
There is no description, image, category, lineup, age limit, seat map or inventory field. Open event_url for anything beyond these six.
Example dataset item
{"title": "Zakir Khan — Papa Yaar Tour","date": "2026-09-12T20:00:00+05:30","venue": "Jio World Garden, BKC","price": "₹999","event_url": "https://www.district.in/events/zakir-khan-papa-yaar-tour-sep12-2026-buy-tickets","city": "Mumbai"}
An open-dated experience with no published price:
{"title": "Sky Jumper Trampoline Park — Day Pass","date": "Open Dated","venue": null,"price": "N.A.","event_url": "https://www.district.in/events/sky-jumper-trampoline-park-buy-tickets","city": "Mumbai"}
Parsing price and date safely
import refrom datetime import datetimedef price_value(event):raw = event.get("price") or ""if raw in ("", "N.A."):return None # not published - NOT freedigits = re.sub(r"[^\d.]", "", raw)return float(digits) if digits else Nonedef start_datetime(event):raw = event.get("date") or ""if raw == "Open Dated":return None # no start date publishedtry:return datetime.fromisoformat(raw) # JSON-LD pathexcept ValueError:return None # District display string - keep as text
What a successful run looks like
{"status": "SUCCEEDED","defaultDatasetId": "<datasetId>","stats": { "itemCount": 87 }}
itemCount below maxItems is normal — the city's feed was simply smaller. itemCount: 0 is a valid outcome with a specific cause, and this Actor names it in the run's status message:
| Status message | Meaning | Action |
|---|---|---|
Could not load https://www.district.in/events/ after 3 attempts … | District never responded on any attempt | Re-run; confirm RESIDENTIAL + IN |
District blocked this run's IP, so the '<city>' location picker never loaded. | Datacenter IP — the classic misconfiguration | Set apifyProxyGroups: ["RESIDENTIAL"] |
District did not show the location picker for '<city>' even on a residential proxy. | Unlucky or rate-limited residential IP | Re-run; try another country/session |
District returned no events because this run's traffic exited from <XX>, not India. | Wrong egress country | Set apifyProxyCountry: "IN" |
District loaded but returned no events for '<city>'. The exit IP is likely rate-limited … | Picker worked, feed empty | Re-run; verify the city has events on the site |
Useful log lines during a healthy run: ✓ Loaded …, ✓ City '<city>' selected successfully, Loaded events: N (every 5 scrolls), 📊 Total valid event URLs found: N, 💾 Saved [n/max]: <title>.
▶️ 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 Location (one Indian city) → set Max Items → leave Proxy Configuration on RESIDENTIAL + IN → (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/district-event"
Python (apify-client)
import os, refrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))IN_RESIDENTIAL = {"useApifyProxy": True,"apifyProxyGroups": ["RESIDENTIAL"], # required - District hides the picker otherwise"apifyProxyCountry": "IN", # required - non-IN exits get an empty feed}run = client.actor("techforce.global/district-event").call(run_input={"location": "Mumbai","maxItems": 200, # ceiling; free plans stop at 5"proxyConfiguration": IN_RESIDENTIAL,"deliveryMode": "none",})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(f'{len(items)} events — run status message: {run.get("statusMessage")}')def price_value(event):raw = event.get("price") or ""if raw in ("", "N.A."):return Nonedigits = re.sub(r"[^\d.]", "", raw)return float(digits) if digits else None# All three fallbacks matter: venue is null, date is "Open Dated", price is "N.A.".priced = [(e, price_value(e)) for e in items]cheapest = sorted((p for p in priced if p[1] is not None), key=lambda p: p[1])[:5]for event, value in cheapest:venue = event.get("venue") or "(venue not listed)"print(f'{value:>8.0f} {event["date"]:<28} {event["title"][:45]}')print(f' {venue} · {event["event_url"]}')print(f'{sum(1 for _, v in priced if v is None)} events published no starting price')print(f'{sum(1 for e in items if e.get("venue") is None)} events with no venue')print(f'{sum(1 for e in items if e.get("date") == "Open Dated")} open-dated events')
Multi-city sweep — one run per city, because there is no multi-city input:
CITIES = ["Mumbai", "Delhi", "Bengaluru", "Hyderabad", "Pune"]all_events = []for city in CITIES:run = client.actor("techforce.global/district-event").call(run_input={"location": city,"maxItems": 200,"proxyConfiguration": IN_RESIDENTIAL,"deliveryMode": "none",})rows = client.dataset(run["defaultDatasetId"]).list_items().itemsif not rows:# Read the status message: it distinguishes a blocked IP from an empty city.print(f'{city}: 0 events — {run.get("statusMessage")}')all_events.extend(rows)print(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 run = await client.actor('techforce.global/district-event').call({location: 'Bengaluru',maxItems: 200,proxyConfiguration: {useApifyProxy: true,apifyProxyGroups: ['RESIDENTIAL'],apifyProxyCountry: 'IN',},deliveryMode: 'none',});const { items } = await client.dataset(run.defaultDatasetId).listItems();// venue is genuinely null; date and price use string sentinels instead.const dated = items.filter((e) => e.date !== 'Open Dated');const withVenue = items.filter((e) => e.venue !== null);const priced = items.filter((e) => e.price !== 'N.A.');console.log(`${items.length} events · ${dated.length} dated · ${withVenue.length} with venue · ${priced.length} priced`);
cURL — asynchronous with a spend cap (recommended)
Runs take minutes — a real browser over a residential IP, with up to 3 load attempts. 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 '{"location":"Mumbai","maxItems":200,"proxyConfiguration":{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"],"apifyProxyCountry":"IN"},"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~district-event/runs?maxTotalChargeUsd=1.00'# 2. Poll — and read statusMessage, not just statuscurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/actor-runs/<runId>'# 3. Retrieve when SUCCEEDEDcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'
cURL — synchronous (small runs only)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"location":"Mumbai","maxItems":10,"proxyConfiguration":{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"],"apifyProxyCountry":"IN"},"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~district-event/run-sync-get-dataset-items'
Scheduling & workflow tools
A daily or twice-daily Apify schedule per city is the natural pattern. Consume the dataset from n8n, Make, Zapier or a webhook — or skip the glue and let mcpConnector deliver into Slack or Notion directly.
⚠️ Autonomous Agent Error Handling & Resolution Matrix
| Error Code | Detection Condition | Underlying Cause | Deterministic Agent Action |
|---|---|---|---|
AUTH_INVALID | HTTP 401 from Apify | Missing or malformed APIFY_TOKEN | Abort & Prompt User: request a valid Apify API token. |
RATE_LIMITED | HTTP 429 from Apify | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Browser runs over a residential IP routinely exceed 300s | Reconfigure: use async POST /runs + polling. |
MISSING_LOCATION | Input validation rejection | location is required | Modify Input: send one Indian city. |
DATACENTER_IP_BLOCKED | 0 items; status message District blocked this run's IP, so the '<city>' location picker never loaded. | Not on a residential group — District hides the picker | Reconfigure: apifyProxyGroups: ["RESIDENTIAL"]. |
WRONG_EGRESS_COUNTRY | 0 items; status message names a country other than India | Residential exit IP outside India — District serves an empty feed | Reconfigure: apifyProxyCountry: "IN". |
PROXY_UNAVAILABLE | Log Could not set up Apify Proxy (…). Continuing without a proxy | Residential groups require a paid Apify plan | Prompt User: upgrade; this Actor cannot work reliably without it. |
NAV_FAILED | 0 items; status message Could not load https://www.district.in/events/ after 3 attempts | District never responded — throttling or a dead exit IP | Retry once after a few minutes with RESIDENTIAL + IN. |
PICKER_BLOCKED_RESIDENTIAL | 0 items; status message says the picker didn't open even on a residential proxy | Unlucky or rate-limited residential IP | Retry: a new run gets a different exit IP. |
EMPTY_FEED_THROTTLED | 0 items; status message says loaded but empty, egress country is IN | District throttling this exit IP | Retry, then verify the city has events on the site. |
GRID_NEVER_RENDERED | Log Only N '/events/' link(s) after 3 scrolls | Page shell served without the event grid | Not actionable in-run — the Actor already retries on a fresh IP. |
WRONG_CITY_SUSPECTED | Agent decides to "fix the spelling" after a zero-event run | Almost never the cause — it's the IP | Do not re-spell the city first; fix the proxy, then re-check the city. |
NON_INDIAN_CITY | 0 items with a correct residential IN proxy | District is India-only | Abort & Prompt User: Indian cities only. |
FREE_PLAN_CAP | itemCount stops at 5, upgrade banner in the log | Free Apify plan caps the run at 5 events | Prompt User: upgrade for full-volume runs. |
SIZE_CLAMPED | Asked for more than 200, got 200 | maxItems is hard-capped at 200 | Fix Expectation: 200 per run per city. |
FILTER_UNSUPPORTED | Agent passes category, dateFrom, maxPrice, … | No such inputs exist on this Actor | Re-route: Smart Event Scraper for category / keyword / date-range (District included as a source). For price, run unfiltered and filter the dataset. |
MULTI_CITY_UNSUPPORTED | Agent passes a city array | One city per run | Loop: one run per city. |
VENUE_NULL | TypeError on venue | venue is genuinely null sometimes | Fix Code: null-guard it. |
DATE_OPEN_DATED | Date parsing fails | date is the string "Open Dated" | Fix Code: treat as "no start date", not an error. |
DATE_FORMAT_MIXED | Some dates parse as ISO, others don't | JSON-LD gives ISO 8601; the selector fallback gives a display string | Fix Code: try ISO, fall back to keeping the text. |
PRICE_NOT_NUMERIC | NaN from arithmetic on price | price is "₹499" or "N.A." | Fix Code: strip non-digits; treat "N.A." as unknown — not free. |
PRICE_MISREAD | Quoted price doesn't match checkout | price is the starting price | Fix Expectation: present it as "from ₹X". |
NO_EVENT_DETAIL | No description, image, lineup or category | Six fields only | Fix Expectation: open event_url. |
NO_INVENTORY_DATA | No seat map or tickets-remaining | Not extracted | Fix Expectation: open event_url. |
WRONG_VERTICAL | User asked about District dining / restaurants / movies | This Actor scrapes /events/ only | Abort & Prompt User: events only. |
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 | Up to 200 connector calls per run | deliveryMode defaults to perEvent | Reconfigure: use summary or chunked. |
DELIVERY_TIMEOUT | Notion / Airtable times out on one call | One payload carrying every event is too large | Reconfigure: deliveryMode: "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 maxItems reached | maxTotalChargeUsd spend threshold reached | Prompt User: confirm a higher spend cap. |
Reading run status:
SUCCEEDEDwith an empty dataset is a valid outcome here, not a failure — only aFAILEDstatus is a platform-level problem. Every Actor-side row, quoted message and field condition in the table above comes from this Actor’s own behaviour; the401/408/429rows are standard Apify API responses.
🗣️ Natural Language → Actor Mapping
| User says | Intent | Constructed Actor input |
|---|---|---|
| "What's happening in Mumbai this week?" | City discovery | {"location":"Mumbai","maxItems":200} + residential IN proxy |
| "Show me events in Bengaluru" | City discovery | {"location":"Bengaluru","maxItems":200} |
| "Comedy shows in Delhi" | Category intent | ⚠️ No category filter — ➡️ Smart Event Scraper, or run {"location":"Delhi"} and filter title afterwards |
| "Cheap events in Pune under ₹500" | Price intent | ⚠️ No price filter anywhere — run, then filter parsed price |
| "Events this weekend in Hyderabad" | Date intent | ⚠️ No date filter — ➡️ Smart Event Scraper (date_from/date_to), or filter parsed date afterwards |
| "Build me an India-wide event feed" | Multi-city | Loop one run per city: Mumbai, Delhi, Bengaluru, Hyderabad, Pune, … |
| "Track what District is promoting in Chennai" | Monitoring | Schedule {"location":"Chennai","maxItems":100} daily |
| "What's on at a particular venue?" | Venue research | Run the city, then filter on venue |
| "Compare ticket prices between Mumbai and Delhi" | Pricing research | One run per city, compare parsed price |
| "Push tonight's events into Notion" | Delivery | {"mcpConnector":"notion","deliveryMode":"chunked","mcpTool":"notion-create-pages"} |
| "Post the Bengaluru feed to Slack every morning" | Digest delivery | Schedule + {"deliveryMode":"summary","mcpTool":"send_message"} |
| "Why did this return zero events?" | Diagnosis | Read the run's status message — it names the exact cause |
| "Book me two tickets" | Transaction | ❌ Read-only — open event_url |
| "How many tickets are left?" | Inventory | ❌ Not extracted |
| "Give me the full event description and lineup" | Detail | ❌ Six fields only — open event_url |
| "Same thing for Dubai / Singapore" | Non-India | ❌ District is India-only |
| "Get me the organizer's phone number" | Contact data | ❌ Not returned |
| "Show me restaurants on District" | Wrong vertical | ❌ Events only |
Should NOT route here: booking, reserving or buying tickets · category, keyword or date-range filtering at the source (➡️ Smart Event Scraper) · several cities in one run · event descriptions, images, lineups or age limits · seat maps or remaining inventory · organizer / promoter contact details · District's dining, restaurant or movie sections · any city outside India · any run where a residential proxy is unavailable.
🧭 Agent Execution & Routing Logic
[Input User Query]│▼1. Is this about events in an INDIAN city on district.in? NO → Route elsewhere│ YES (All Events Scraper covers other geographies)▼2. Does the user want to TRANSACT (book / buy / reserve)? YES → Abort. Read-only. Hand them event_url│ NO▼2b. Did they ask for a CATEGORY or DATE-RANGE filter?│ YES → Route to Smart Event Scraper: it filters by category, keyword and│ date range across six platforms INCLUDING District, in one run│ NO▼3. ── PRECONDITION ── Is a RESIDENTIAL proxy available (paid Apify plan)?│ NO → Abort and say why: District hides the location picker from│ datacenter IPs, so the run would return zero events│ YES → proxyConfiguration = {RESIDENTIAL, country IN} ← BOTH parts required▼4. Still here with a filter requirement (e.g. price)?│ YES → no Actor filters on price. Run unfiltered, then filter the dataset▼5. One city, or several?├── one → location = "<city>"└── several → ONE RUN PER CITY, looped from the orchestration layer▼6. Set maxItems ≤ 200 (default 50, free plans 5)▼7. First run against this city/proxy combination?│ YES → validate with maxItems: 5 before a full sweep▼8. Deliver to a connector? YES → mcpConnector + mcpTool + {message}│ summary (1 call) / chunked (large feeds) / perEvent (≤200 calls)│ NO → set deliveryMode "none" (it defaults to perEvent)▼9. Use the ASYNC endpoint — browser + residential IP runs regularly exceed 300s▼[Execute]│├──► SUCCEEDED, items > 0 ──► handle the field contract:│ venue may be null → null-guard│ date may be "Open Dated" → no start date, not an error│ date format is MIXED → try ISO 8601, else keep the text│ price may be "N.A." → unknown, NOT free│ price is a STARTING price with a currency symbol → strip before arithmetic│ dedupe on event_url│└──► SUCCEEDED, items == 0 ──► READ THE STATUS MESSAGE. It names the cause:"...location picker never loaded" → NOT residential → fix the group"...exited from XX, not India" → fix the country to IN"...even on a residential proxy" → unlucky IP → re-run"Could not load ... after 3 attempts" → District throttling → wait, re-run"...loaded but returned no events" → re-run, then verify the cityDO NOT start by second-guessing the city name — that is almost never it.
💰 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 — but note that a free plan also has no residential proxy access, which this Actor functionally requires. Treat a paid plan as the entry requirement.
Autonomous spend caps: pass maxTotalChargeUsd as a query parameter on the run endpoint for a hard per-execution ceiling.
ℹ️ Always confirm the live rate on the Actor’s Apify Store page before committing to a large run. The Store listing is what bills you; the figures above are a worked example at the advertised rate. Apify platform usage is billed separately.
Platform usage is billed on top, and here it is the cost that actually bites. Two multipliers stack:
| Cost driver | Why it matters on this Actor |
|---|---|
| A real Chromium browser | 5 concurrent pages, up to 100 scroll passes, one page load per event |
| Residential proxy GB | Metered per gigabyte, and every request in the run goes through it |
| Up to 3 load attempts | A blocked or throttled IP can cost 3 × a 90-second navigation before any event is scraped |
| A failed run still costs usage | Proxy traffic and compute are consumed even when the result set is empty |
The Actor already mitigates the biggest slice: images, media, fonts and 13 analytics/tracking hosts are blocked, and it waits for domcontentloaded rather than full load.
Cost levers:
- Validate with
maxItems: 5on any new city or proxy setup. A misconfigured proxy costs a full run's proxy traffic for zero events — the cheapest possible way to discover that is a 5-item run. - Set
maxItemsto what you'll actually use. Every event is a separate page load over a residential IP. - Don't over-schedule. A city feed doesn't turn over hourly; daily or twice-daily captures the same information at a fraction of the proxy spend.
- Prefer
summary/chunkeddelivery overperEvent— 1 connector call instead of up to 200. - Cap the spend with
maxTotalChargeUsdon any agent-driven run.
🔍 Companion machine-readable files
| File | Purpose |
|---|---|
.actor/actor.json | Identity, title, description, version, build tag, and the input / output / dataset schema references |
.actor/input_schema.json | Authoritative typed input contract — location (required), maxItems (1–200), the RESIDENTIAL + IN proxy default, and the 5 delivery fields |
.actor/dataset_schema.json | Console Output tab view — title, date, venue, price, event URL, city (with column labels) |
.actor/output_schema.json | Declares where results are stored |
src/main.py | Proxy wiring, retry/navigation logic, city picker, scroll harvesting, event-URL filtering, JSON-LD-first extraction, page pool, failure diagnostics, MCP delivery |
src/connector.py | MCP connector session handling through the Apify MCP Proxy |
Dockerfile | Built on apify/actor-python-playwright:3.12 — the browser is required here, not incidental |
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: over 90% of "no events" reports are the proxy, not the city. Read the run's status message before changing anything else.
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | 0 events; status message says the location picker never loaded | You are not on a residential proxy — District hides the picker from datacenter IPs | Set apifyProxyGroups: ["RESIDENTIAL"]. |
| 2 | 0 events; status message names a country other than India | The residential exit IP is outside India; District serves it an empty feed | Set apifyProxyCountry: "IN". |
| 3 | 0 events; picker didn't open even on residential | Unlucky or rate-limited exit IP | Re-run — each run gets a different IP. |
| 4 | 0 events; Could not load … after 3 attempts | District never responded | Wait a few minutes and re-run. |
| 5 | 0 events; loaded and picker worked, feed empty, egress is IN | District throttling this IP | Re-run, then check the city on the site. |
| 6 | Log Could not set up Apify Proxy … Continuing without a proxy | Residential groups need a paid Apify plan | Upgrade — the Actor can't work reliably without it. |
| 7 | You changed the city spelling and it still returns 0 | The city name is almost never the cause | Fix the proxy first (rows 1–2), then revisit the city. |
| 8 | Exactly 5 events every run | Free Apify plan cap | Upgrade; the log prints the upgrade banner. |
| 9 | Asked for 500, got 200 | maxItems is hard-capped at 200 | 200 per run per city; loop for more. |
| 10 | Fewer events than maxItems | The city's feed is smaller | Expected — there's no total-match count. |
| 11 | Log Only N '/events/' link(s) after 3 scrolls | Page shell rendered without the event grid | The Actor already retries on a fresh IP; if all 3 fail see rows 1–2. |
| 12 | TypeError on venue | venue is genuinely null sometimes | Null-guard it. |
| 13 | date is "Open Dated" | District published no start date | Not an error — open-dated experience. |
| 14 | Some dates parse, others don't | JSON-LD gives ISO 8601; the fallback gives a display string | Try ISO, else keep the text. |
| 15 | price is "N.A." | No starting price published | Not free. Treat as unknown. |
| 16 | Arithmetic on price gives NaN | It's "₹499" — a display string | Strip non-digits first. |
| 17 | Quoted price doesn't match checkout | price is the starting price | Present as "from ₹X". |
| 18 | No description, image, category or lineup | Six fields only | Open event_url. |
| 19 | Category / date / price filter ignored | Those inputs don't exist on this Actor | Use Smart Event Scraper for category / keyword / date range; filter the dataset for price. |
| 20 | Only one city came back | One city per run | Loop one run per city. |
| 21 | Runs take several minutes | Real browser + residential IP + up to 3 attempts | Expected; use the async endpoint. |
| 22 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard 300-second ceiling | Use async POST /runs → poll → fetch. |
| 23 | Up to 200 connector calls per run | deliveryMode defaults to perEvent | Switch to summary or chunked. |
| 24 | Notion / Airtable timed out | One call carrying every event is too large | Use deliveryMode: "chunked". |
| 25 | Slack / Notion received nothing | Delivery needs both mcpConnector and mcpTool | Set both. The dataset is still written in full. |
| 26 | Connector fired but the message body is empty | mcpArguments did not reference {message} | Map {message} in mcpArguments and populate mcpMessageTemplate. |
| 27 | Log APIFY_MCP_PROXY_URL is not set | You ran locally; connectors only resolve on the platform | Deploy with apify push and run on the platform. |
| 28 | HTTP 401 / 403 from Apify | Missing, expired or malformed APIFY_TOKEN | Regenerate in Apify Console → Settings → API & Integrations. |
| 29 | HTTP 429 from Apify | Apify account concurrency / rate limits | Retry with exponential backoff (2s → 4s → 8s). |
Diagnostic checklist before opening an issue
- Read the status message. This Actor writes a terminal status message that names the exact failure. Everything below is only for when that isn't enough.
- Confirm both proxy settings.
apifyProxyGroups: ["RESIDENTIAL"]andapifyProxyCountry: "IN". One without the other still fails, differently. - Confirm you're on a paid plan. Residential proxy access is a paid feature; without it the log says
Could not set up Apify Proxy. - Baseline run.
{"location":"Mumbai","maxItems":5}with the default proxy block. Mumbai is District's largest market — if it returns nothing, the problem is the proxy, not the city. - Re-run once. Each run gets a different exit IP; a single unlucky IP is a common and self-correcting cause.
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 status message you saw.
❓ FAQ
Setup & access
Why do I need a residential proxy? Can I skip it?
No, not in practice. District fingerprints the caller's IP: from a datacenter range it serves the page shell but withholds the "Select Location" dialog, and the event feed never populates without a location. Residential access requires a paid Apify plan — that's the real entry requirement for this Actor.
Why does the proxy country have to be IN?
district.in serves its event feed only to Indian IPs. From a residential IP elsewhere the page loads, the picker opens, and the feed comes back empty — indistinguishable from a quiet city unless you know to look. The Actor checks the actual egress country when a harvest is empty and tells you.
Do I need a District account or login?
No — and none is accepted. Only public listing pages are read.
Is there a District API I could use instead?
Not a public one. That's why this Actor drives a real browser and prefers each event page's embedded JSON-LD, which is the most stable structured data District exposes.
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/district-event"
Coverage & filtering
Which cities work?
Indian cities District serves — Mumbai, Delhi, Bengaluru, Hyderabad, Pune, Chennai, Kolkata, Ahmedabad and others. location is free text, not an enum, so check district.in/events if a city returns nothing on a correctly configured proxy.
Can I scrape several cities in one run?
No — one city per run. Loop from your orchestration layer; the Python example above shows the pattern.
Can I filter by category, date or price?
Not at the source — those inputs don't exist here. Use Smart Event Scraper instead: it takes a category, a free-text search_query and a date_from/date_to range, and applies them across six platforms including District in one run. Otherwise, run this Actor unfiltered and filter the dataset — note that no Actor in the family filters on price, since District only publishes a starting price.
Does it get every event in the city?
It scrolls the feed to exhaustion and takes up to maxItems (200 max). Whether that's "every event" depends on what District's feed surfaces for that city — treat it as the public feed, not a guaranteed complete inventory.
Output
Why is venue sometimes null?
Because District didn't expose one on that event page. It's the only genuinely nullable field.
What does date: "Open Dated" mean?
No start date was published — typical for long-running experiences and attractions rather than one-night shows.
What does price: "N.A." mean?
No starting price was published. It does not mean free.
Is price the price I'll pay?
It's the starting price for the event, as a display string with the currency symbol. Actual checkout depends on tier, seat and fees — present it as "from ₹X".
Why are the date formats inconsistent?
JSON-LD gives ISO 8601; when JSON-LD is absent the selector fallback returns District's own display string. Parse ISO first and keep the raw text otherwise.
Can I get descriptions, images, lineups or seat availability?
No — the output is six fields. Open event_url for anything more.
Pricing
How is this billed?
Pay-per-result — charged per event returned, with no monthly subscription. The current rate is on the Actor's Pricing tab.
Why is platform usage higher than for other scrapers?
Because a real browser and residential proxy traffic are both required here. The Actor blocks images, fonts, media and trackers to cut it down, but it can't be as cheap as an HTTP-only scraper.
A run returned nothing — was I still charged?
You aren't charged per result when there are none, but Apify platform usage (compute and residential proxy GB) is consumed by any run, including a blocked one. That's why validating with maxItems: 5 is worth it.
How do I guarantee an agent never overspends?
Pass maxTotalChargeUsd as a query parameter on the run endpoint.
🔗 Related Actors
Event data, by source
Pick by where the events are listed — or, if you need filtering rather than a single source, by the aggregator that carries District alongside five others.
| Actor | Source | Coverage | Why pick it over this one |
|---|---|---|---|
| This Actor | district.in (Zomato) | India, one city per run | Deepest pass over District's own feed — scrolls it to exhaustion, up to 200 events |
| Smart Event Scraper | 6 platforms incl. District | Global | ✅ Category, keyword and date-range filters — the filtering this Actor has none of, plus five other sources in the same run |
| All Events Scraper | allevents.in | Global, incl. India | Multi-city discovery, and no residential-proxy requirement — start here if District's setup is a blocker |
| Eventbrite Scraper | eventbrite.com | Global | Organizer-led events, workshops, meetups and free events |
| Eventim Scraper | eventim.de | Germany | Live stock and price signals for German ticketed events |
| Events Eye Scraper | eventseye.com | Global | Trade shows, B2B exhibitions and conferences |
⭐ Need filters? Use Smart Event Scraper instead. It carries District as one of six sources and adds the three filters this Actor lacks —
category(6 categories, or any), a free-textsearch_query, and adate_from/date_torange — with a per-sourcelimitand one standardised output schema across all platforms. "Comedy in Bengaluru next weekend" is a single run there; here it's a full-feed scrape plus your own filtering.Come back to this Actor when you want depth on District specifically — it scrolls the city's feed to exhaustion for up to 200 events, where the aggregator caps each source at 100.
ℹ️ If you want Indian events without the residential-proxy dependency, start with All Events Scraper. It covers Indian cities too, with a much lower setup bar. Note that Smart Event Scraper's District source hits the same IP fingerprinting this Actor does — so if the residential proxy is your blocker, the aggregator's other five sources are the way around it, not its District 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 |
| Hospitality Lead Scraper | Bars, hotels and restaurants near a venue, for event-driven local outreach |
| Advanced Website Crawling Actor | Crawl a venue's or promoter's own site alongside the District listing |
Suggested pipeline patterns
📅 City event feed
Daily schedule per city → deliveryMode: "chunked" into Notion → a self-maintaining "what's on in Mumbai" page.
🇮🇳 Multi-city India dataset
Loop 5–8 cities, one run each → merge on event_url → a national view of what District is promoting, grouped by city.
📊 Entry price-point research
Same run across several cities → parse price → compare what comedy, music and experiences are priced at by market.
🎯 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 ahead of the date.
🌍 Complete city coverage Smart Event Scraper for filtered breadth across six platforms → this Actor for depth on District's own feed → dedupe on title + date + venue. Ticketed District listings and the grassroots long tail in one feed.
Browse all Actors by Techforce Global at scraper.techforce.global.
🔐 Compliance & Data Privacy
This Actor is an independent tool. It is not affiliated with, endorsed by, or sponsored by District, Zomato Limited, Eternal Limited, or any venue, artist or promoter named in its output. All trademarks are the property of their respective owners.
It reads publicly available event listing pages on district.in. It does not log in, does not accept credentials, and does not access anything behind authentication. It never books, reserves or buys tickets.
The output is commercial event data, not personal data — event titles, venues, dates, starting prices and public URLs. Performer names that appear inside title are stage and act names published commercially, so the privacy exposure here is minimal compared with a contact-data scraper. Three things still apply:
- Prices are starting prices, captured at run time. Republishing them as current — in an app, a listing site or a newsletter — commits you to a refresh cadence. Stale or misleading price displays can attract obligations under India's Consumer Protection Act 2019 and its e-commerce rules; always attribute the source and link through to
event_urlfor the live price. - Respect the platform's own limits. This Actor already blocks unnecessary resources, caps concurrency at 5 pages and backs off between retries. Don't defeat that by running many parallel runs against the same city — that's both an anti-abuse and a cost problem.
- Residential proxy use carries its own terms. You are responsible for using Apify Proxy within Apify's terms and applicable law.
You remain responsible for ensuring your use complies with District's terms of service, applicable data-protection law (India's DPDP Act 2023, GDPR / UK GDPR / CCPA where relevant), and consumer-protection rules in the markets you operate in.
🆘 Support & Custom Pipeline Engineering
Need a multi-city India sweep on a schedule, change detection between runs, deeper per-event enrichment, or a full event-intelligence pipeline?
- 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": "District.in Event Scraper — Indian City Events, Venues & Ticket Prices","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Scrapes live event listings from district.in (Zomato's events and ticketing platform) for one Indian city per run — event title, start date, venue, starting ticket price and the direct booking URL — by driving District's own location picker in a real browser over a residential Indian IP, reading each event page's embedded JSON-LD. Results are saved to a dataset and can be delivered into Notion, Slack, Airtable, Google Sheets or any authorized MCP connector. Requires Apify Proxy with the RESIDENTIAL group and country IN.","url": "https://apify.com/techforce.global/district-event","offers": {"@type": "Offer","priceCurrency": "USD","description": "Pay-per-result, charged per event returned. The current rate is listed on the Actor's Pricing tab."}}
{"@context": "https://schema.org","@type": "FAQPage","mainEntity": [{"@type": "Question","name": "Why do I need a residential proxy? Can I skip it?","acceptedAnswer": {"@type": "Answer","text": "No, not in practice. District fingerprints the caller's IP: from a datacenter range it serves the page shell but withholds the \"Select Location\" dialog, and the event feed never populates without a location. Residential access requires a paid Apify plan - that's the real entry requirement for this Actor."}},{"@type": "Question","name": "Why does the proxy country have to be IN?","acceptedAnswer": {"@type": "Answer","text": "district.in serves its event feed only to Indian IPs. From a residential IP elsewhere the page loads, the picker opens, and the feed comes back empty - indistinguishable from a quiet city unless you know to look. The Actor checks the actual egress country when a harvest is empty and tells you."}},{"@type": "Question","name": "Do I need a District account or login?","acceptedAnswer": {"@type": "Answer","text": "No - and none is accepted. Only public listing pages are read."}},{"@type": "Question","name": "Is there a District API I could use instead?","acceptedAnswer": {"@type": "Answer","text": "Not a public one. That's why this Actor drives a real browser and prefers each event page's embedded JSON-LD, which is the most stable structured data District exposes."}},{"@type": "Question","name": "Can I plug it into Claude, Cursor, or a LangChain agent?","acceptedAnswer": {"@type": "Answer","text": "Yes - it is a native MCP tool:"}},{"@type": "Question","name": "Which cities work?","acceptedAnswer": {"@type": "Answer","text": "Indian cities District serves - Mumbai, Delhi, Bengaluru, Hyderabad, Pune, Chennai, Kolkata, Ahmedabad and others. location is free text, not an enum, so check district.in/events if a city returns nothing on a correctly configured proxy."}},{"@type": "Question","name": "Can I scrape several cities in one run?","acceptedAnswer": {"@type": "Answer","text": "No - one city per run. Loop from your orchestration layer; the Python example above shows the pattern."}},{"@type": "Question","name": "Can I filter by category, date or price?","acceptedAnswer": {"@type": "Answer","text": "Not at the source - those inputs don't exist here. Use Smart Event Scraper instead: it takes a category, a free-text search_query and a date_from/date_to range, and applies them across six platforms including District in one run. Otherwise, run this Actor unfiltered and filter the dataset - note that no Actor in the family filters on price, since District only publishes a starting price."}},{"@type": "Question","name": "Does it get every event in the city?","acceptedAnswer": {"@type": "Answer","text": "It scrolls the feed to exhaustion and takes up to maxItems (200 max). Whether that's \"every event\" depends on what District's feed surfaces for that city - treat it as the public feed, not a guaranteed complete inventory."}},{"@type": "Question","name": "Why is venue sometimes null?","acceptedAnswer": {"@type": "Answer","text": "Because District didn't expose one on that event page. It's the only genuinely nullable field."}},{"@type": "Question","name": "What does date: \"Open Dated\" mean?","acceptedAnswer": {"@type": "Answer","text": "No start date was published - typical for long-running experiences and attractions rather than one-night shows."}},{"@type": "Question","name": "What does price: \"N.A.\" mean?","acceptedAnswer": {"@type": "Answer","text": "No starting price was published. It does not mean free."}},{"@type": "Question","name": "Is price the price I'll pay?","acceptedAnswer": {"@type": "Answer","text": "It's the starting price for the event, as a display string with the currency symbol. Actual checkout depends on tier, seat and fees - present it as \"from X\"."}},{"@type": "Question","name": "Why are the date formats inconsistent?","acceptedAnswer": {"@type": "Answer","text": "JSON-LD gives ISO 8601; when JSON-LD is absent the selector fallback returns District's own display string. Parse ISO first and keep the raw text otherwise."}},{"@type": "Question","name": "Can I get descriptions, images, lineups or seat availability?","acceptedAnswer": {"@type": "Answer","text": "No - the output is six fields. Open event_url for anything more."}},{"@type": "Question","name": "How is this billed?","acceptedAnswer": {"@type": "Answer","text": "Pay-per-result - charged per event returned, with no monthly subscription. The current rate is on the Actor's Pricing tab."}},{"@type": "Question","name": "Why is platform usage higher than for other scrapers?","acceptedAnswer": {"@type": "Answer","text": "Because a real browser and residential proxy traffic are both required here. The Actor blocks images, fonts, media and trackers to cut it down, but it can't be as cheap as an HTTP-only scraper."}},{"@type": "Question","name": "A run returned nothing - was I still charged?","acceptedAnswer": {"@type": "Answer","text": "You aren't charged per result when there are none, but Apify platform usage (compute and residential proxy GB) is consumed by any run, including a blocked one. That's why validating with maxItems: 5 is worth it."}},{"@type": "Question","name": "How do I guarantee an agent never overspends?","acceptedAnswer": {"@type": "Answer","text": "Pass maxTotalChargeUsd as a query parameter on the run endpoint."}}]}