District.in Scraper - Indian City Events, Venues & Prices avatar

District.in Scraper - Indian City Events, Venues & Prices

Pricing

from $0.20 / actor start

Go to Apify Store
District.in Scraper - Indian City Events, Venues & Prices

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

Techforce Global

Maintained by Community

Actor stats

1

Bookmarked

28

Total users

2

Monthly active users

19 hours ago

Last modified

Share

District.in Event Scraper — Indian City Events, Venues & Ticket Prices from Zomato's District

Apify Actor MCP Ready Source Geo Proxy

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 / CapabilityManual browsing / naive scraperThis 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 selectionManual 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
SpeedOne page at a time5 concurrent browser pages, images/fonts/trackers blocked
Structured outputCopy-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 District
hides the location picker; from a non-Indian IP the event feed comes back
empty. Requires a paid Apify plan. This is a functional requirement, not a
performance 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: 50
hard_cap: 200
free_plan_cap: 5
no_filters: >
There is no category, date-range, price or availability filter. The Actor
returns whatever District's city feed shows, in feed order. Filter after the
fact 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: 200
cities_per_run: 1
free_plan_cap: 5
concurrency: "5 browser pages"
max_scrolls: 100
pricing:
model: "pay-per-result"
note: "exact rate is configured on the Store listing - see the Actor's Pricing tab"
when_to_use: >
You want the current district.in event feed for one Indian city - to power an
event listing, research venue programming and entry price points, or watch a
city's feed on a schedule.
when_not_to_use: >
You need to book tickets, need category/date filtering at the source, need
several cities in one run, need event descriptions or lineups, or you are
outside 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

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

  1. 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"].
  2. 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 to IN; don't drop it.
  3. Residential proxies need a paid Apify plan. Without one this Actor cannot reliably work at all. That's a plan constraint, not a bug.
  4. location is required, and it's one city per run. There is no multi-city input; loop from your orchestration layer.
  5. India only. Non-Indian city names return nothing — District doesn't serve them.
  6. maxItems is 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.
  7. 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.
  8. 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).
  9. venue can be null. It's the one genuinely nullable field.
  10. date falls 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.
  11. price falls 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.
  12. date format 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.
  13. A run can return fewer than maxItems simply because the city's feed is smaller. There is no total-match count.
  14. 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.
  15. deliveryMode defaults to perEvent, not none. With a connector selected, a 200-event run makes 200 connector calls. Use summary or chunked.
  16. Use chunked for 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: none
platform: "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

FieldTypeRequiredDefaultAllowed Values / FormatExample
locationstringYes"Mumbai"One Indian city name as District lists it"Bengaluru"
maxItemsinteger⬜ No501200; free plans capped at 5200
proxyConfigurationobject⬜ NoRESIDENTIAL + INApify proxy editor object — see belowsee below

The proxy block — leave this alone

{
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "IN"
}
}

This is the schema default, and both parts matter:

SettingIf you change itSymptom
apifyProxyGroups: ["RESIDENTIAL"]Datacenter / auto groupsPage loads, location picker never appears, zero events
apifyProxyCountry: "IN"Any other country, or unsetPicker opens, feed comes back empty — the log names the actual egress country
useApifyProxy: falseNo proxy at allThe 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.

ParameterTypeDefaultDescription
mcpConnectorstring""Authorized MCP connector on your Apify account (Notion, Slack, Airtable, Sheets, …). Leave empty for dataset-only runs.
deliveryModeenumperEventperEvent (one call per event) · summary (one call with everything) · chunked (split across calls) · none.
mcpToolstring""Tool name on the connector: create_page, send_message, append_row, create_record. Run once with a connector selected — the log lists the connector's available tools.
mcpArgumentsobject{}Arguments passed to the tool. String leaves support {placeholders}.
mcpMessageTemplatestring""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

FieldTypeNullable / fallbackMeaning
titlestringnever empty — an event with no title is skipped, not savedEvent name (JSON-LD name, else the page <h3>)
datestringfalls back to "Open Dated"Start date. ISO 8601 from JSON-LD, or District's own display string from the selector fallback
venuestring⚠️ null when District exposed noneVenue name (JSON-LD location.name, else a selector)
pricestringfalls back to "N.A."Starting price as a display string with currency (e.g. "₹499", "INR499") — not a number
event_urlstringnever emptyDirect District event page — use as the dedupe key
citystringnever emptyThe city you asked for, echoed back for grouping

⚠️ Three different missing-value conventions in one record. venue is null, date is the string "Open Dated", and price is 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 re
from datetime import datetime
def price_value(event):
raw = event.get("price") or ""
if raw in ("", "N.A."):
return None # not published - NOT free
digits = re.sub(r"[^\d.]", "", raw)
return float(digits) if digits else None
def start_datetime(event):
raw = event.get("date") or ""
if raw == "Open Dated":
return None # no start date published
try:
return datetime.fromisoformat(raw) # JSON-LD path
except 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 messageMeaningAction
Could not load https://www.district.in/events/ after 3 attempts …District never responded on any attemptRe-run; confirm RESIDENTIAL + IN
District blocked this run's IP, so the '<city>' location picker never loaded.Datacenter IP — the classic misconfigurationSet apifyProxyGroups: ["RESIDENTIAL"]
District did not show the location picker for '<city>' even on a residential proxy.Unlucky or rate-limited residential IPRe-run; try another country/session
District returned no events because this run's traffic exited from <XX>, not India.Wrong egress countrySet apifyProxyCountry: "IN"
District loaded but returned no events for '<city>'. The exit IP is likely rate-limited …Picker worked, feed emptyRe-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 Itemsleave 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, re
from apify_client import ApifyClient
client = 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().items
print(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 None
digits = 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().items
if 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`);

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 limit
curl -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 status
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/actor-runs/<runId>'
# 3. Retrieve when SUCCEEDED
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'

cURL — synchronous (small 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 CodeDetection ConditionUnderlying CauseDeterministic Agent Action
AUTH_INVALIDHTTP 401 from ApifyMissing or malformed APIFY_TOKENAbort & Prompt User: request a valid Apify API token.
RATE_LIMITEDHTTP 429 from ApifyApify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointBrowser runs over a residential IP routinely exceed 300sReconfigure: use async POST /runs + polling.
MISSING_LOCATIONInput validation rejectionlocation is requiredModify Input: send one Indian city.
DATACENTER_IP_BLOCKED0 items; status message District blocked this run's IP, so the '<city>' location picker never loaded.Not on a residential group — District hides the pickerReconfigure: apifyProxyGroups: ["RESIDENTIAL"].
WRONG_EGRESS_COUNTRY0 items; status message names a country other than IndiaResidential exit IP outside India — District serves an empty feedReconfigure: apifyProxyCountry: "IN".
PROXY_UNAVAILABLELog Could not set up Apify Proxy (…). Continuing without a proxyResidential groups require a paid Apify planPrompt User: upgrade; this Actor cannot work reliably without it.
NAV_FAILED0 items; status message Could not load https://www.district.in/events/ after 3 attemptsDistrict never responded — throttling or a dead exit IPRetry once after a few minutes with RESIDENTIAL + IN.
PICKER_BLOCKED_RESIDENTIAL0 items; status message says the picker didn't open even on a residential proxyUnlucky or rate-limited residential IPRetry: a new run gets a different exit IP.
EMPTY_FEED_THROTTLED0 items; status message says loaded but empty, egress country is INDistrict throttling this exit IPRetry, then verify the city has events on the site.
GRID_NEVER_RENDEREDLog Only N '/events/' link(s) after 3 scrollsPage shell served without the event gridNot actionable in-run — the Actor already retries on a fresh IP.
WRONG_CITY_SUSPECTEDAgent decides to "fix the spelling" after a zero-event runAlmost never the cause — it's the IPDo not re-spell the city first; fix the proxy, then re-check the city.
NON_INDIAN_CITY0 items with a correct residential IN proxyDistrict is India-onlyAbort & Prompt User: Indian cities only.
FREE_PLAN_CAPitemCount stops at 5, upgrade banner in the logFree Apify plan caps the run at 5 eventsPrompt User: upgrade for full-volume runs.
SIZE_CLAMPEDAsked for more than 200, got 200maxItems is hard-capped at 200Fix Expectation: 200 per run per city.
FILTER_UNSUPPORTEDAgent passes category, dateFrom, maxPrice, …No such inputs exist on this ActorRe-route: Smart Event Scraper for category / keyword / date-range (District included as a source). For price, run unfiltered and filter the dataset.
MULTI_CITY_UNSUPPORTEDAgent passes a city arrayOne city per runLoop: one run per city.
VENUE_NULLTypeError on venuevenue is genuinely null sometimesFix Code: null-guard it.
DATE_OPEN_DATEDDate parsing failsdate is the string "Open Dated"Fix Code: treat as "no start date", not an error.
DATE_FORMAT_MIXEDSome dates parse as ISO, others don'tJSON-LD gives ISO 8601; the selector fallback gives a display stringFix Code: try ISO, fall back to keeping the text.
PRICE_NOT_NUMERICNaN from arithmetic on priceprice is "₹499" or "N.A."Fix Code: strip non-digits; treat "N.A." as unknown — not free.
PRICE_MISREADQuoted price doesn't match checkoutprice is the starting priceFix Expectation: present it as "from ₹X".
NO_EVENT_DETAILNo description, image, lineup or categorySix fields onlyFix Expectation: open event_url.
NO_INVENTORY_DATANo seat map or tickets-remainingNot extractedFix Expectation: open event_url.
WRONG_VERTICALUser asked about District dining / restaurants / moviesThis Actor scrapes /events/ onlyAbort & Prompt User: events only.
DELIVERY_SKIPPEDLog no tool name was providedmcpConnector set but mcpTool emptyModify Input: set mcpTool; the dataset is already saved.
DELIVERY_TOOL_UNKNOWNLog Tool 'x' is not available on this connectorWrong tool name for that connectorModify Input: pick a name from the Available tools: list in the same log line.
DELIVERY_EMPTYConnector called, body emptymcpArguments omitted the {message} placeholderModify Input: map {message} inside mcpArguments.
DELIVERY_FLOODUp to 200 connector calls per rundeliveryMode defaults to perEventReconfigure: use summary or chunked.
DELIVERY_TIMEOUTNotion / Airtable times out on one callOne payload carrying every event is too largeReconfigure: deliveryMode: "chunked".
MCP_PROXY_MISSINGLog APIFY_MCP_PROXY_URL is not setRunning locally instead of on the platformReconfigure: apify push and run on the platform.
CHARGE_LIMITRun aborted before maxItems reachedmaxTotalChargeUsd spend threshold reachedPrompt User: confirm a higher spend cap.

Reading run status: SUCCEEDED with an empty dataset is a valid outcome here, not a failure — only a FAILED status 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; the 401/408/429 rows are standard Apify API responses.


🗣️ Natural Language → Actor Mapping

User saysIntentConstructed 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-cityLoop one run per city: Mumbai, Delhi, Bengaluru, Hyderabad, Pune, …
"Track what District is promoting in Chennai"MonitoringSchedule {"location":"Chennai","maxItems":100} daily
"What's on at a particular venue?"Venue researchRun the city, then filter on venue
"Compare ticket prices between Mumbai and Delhi"Pricing researchOne 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 deliverySchedule + {"deliveryMode":"summary","mcpTool":"send_message"}
"Why did this return zero events?"DiagnosisRead 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 city
DO 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 driverWhy it matters on this Actor
A real Chromium browser5 concurrent pages, up to 100 scroll passes, one page load per event
Residential proxy GBMetered per gigabyte, and every request in the run goes through it
Up to 3 load attemptsA blocked or throttled IP can cost 3 × a 90-second navigation before any event is scraped
A failed run still costs usageProxy 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: 5 on 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 maxItems to 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/chunked delivery over perEvent — 1 connector call instead of up to 200.
  • Cap the spend with maxTotalChargeUsd on any agent-driven run.

🔍 Companion machine-readable files

FilePurpose
.actor/actor.jsonIdentity, title, description, version, build tag, and the input / output / dataset schema references
.actor/input_schema.jsonAuthoritative typed input contract — location (required), maxItems (1–200), the RESIDENTIAL + IN proxy default, and the 5 delivery fields
.actor/dataset_schema.jsonConsole Output tab view — title, date, venue, price, event URL, city (with column labels)
.actor/output_schema.jsonDeclares where results are stored
src/main.pyProxy wiring, retry/navigation logic, city picker, scroll harvesting, event-URL filtering, JSON-LD-first extraction, page pool, failure diagnostics, MCP delivery
src/connector.pyMCP connector session handling through the Apify MCP Proxy
DockerfileBuilt 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 seeMost likely causeFix
10 events; status message says the location picker never loadedYou are not on a residential proxy — District hides the picker from datacenter IPsSet apifyProxyGroups: ["RESIDENTIAL"].
20 events; status message names a country other than IndiaThe residential exit IP is outside India; District serves it an empty feedSet apifyProxyCountry: "IN".
30 events; picker didn't open even on residentialUnlucky or rate-limited exit IPRe-run — each run gets a different IP.
40 events; Could not load … after 3 attemptsDistrict never respondedWait a few minutes and re-run.
50 events; loaded and picker worked, feed empty, egress is INDistrict throttling this IPRe-run, then check the city on the site.
6Log Could not set up Apify Proxy … Continuing without a proxyResidential groups need a paid Apify planUpgrade — the Actor can't work reliably without it.
7You changed the city spelling and it still returns 0The city name is almost never the causeFix the proxy first (rows 1–2), then revisit the city.
8Exactly 5 events every runFree Apify plan capUpgrade; the log prints the upgrade banner.
9Asked for 500, got 200maxItems is hard-capped at 200200 per run per city; loop for more.
10Fewer events than maxItemsThe city's feed is smallerExpected — there's no total-match count.
11Log Only N '/events/' link(s) after 3 scrollsPage shell rendered without the event gridThe Actor already retries on a fresh IP; if all 3 fail see rows 1–2.
12TypeError on venuevenue is genuinely null sometimesNull-guard it.
13date is "Open Dated"District published no start dateNot an error — open-dated experience.
14Some dates parse, others don'tJSON-LD gives ISO 8601; the fallback gives a display stringTry ISO, else keep the text.
15price is "N.A."No starting price publishedNot free. Treat as unknown.
16Arithmetic on price gives NaNIt's "₹499" — a display stringStrip non-digits first.
17Quoted price doesn't match checkoutprice is the starting pricePresent as "from ₹X".
18No description, image, category or lineupSix fields onlyOpen event_url.
19Category / date / price filter ignoredThose inputs don't exist on this ActorUse Smart Event Scraper for category / keyword / date range; filter the dataset for price.
20Only one city came backOne city per runLoop one run per city.
21Runs take several minutesReal browser + residential IP + up to 3 attemptsExpected; use the async endpoint.
22HTTP 408 on run-sync-get-dataset-itemsThe synchronous endpoint has a hard 300-second ceilingUse async POST /runs → poll → fetch.
23Up to 200 connector calls per rundeliveryMode defaults to perEventSwitch to summary or chunked.
24Notion / Airtable timed outOne call carrying every event is too largeUse deliveryMode: "chunked".
25Slack / Notion received nothingDelivery needs both mcpConnector and mcpToolSet both. The dataset is still written in full.
26Connector fired but the message body is emptymcpArguments did not reference {message}Map {message} in mcpArguments and populate mcpMessageTemplate.
27Log APIFY_MCP_PROXY_URL is not setYou ran locally; connectors only resolve on the platformDeploy with apify push and run on the platform.
28HTTP 401 / 403 from ApifyMissing, expired or malformed APIFY_TOKENRegenerate in Apify Console → Settings → API & Integrations.
29HTTP 429 from ApifyApify account concurrency / rate limitsRetry with exponential backoff (2s → 4s → 8s).

Diagnostic checklist before opening an issue

  1. 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.
  2. Confirm both proxy settings. apifyProxyGroups: ["RESIDENTIAL"] and apifyProxyCountry: "IN". One without the other still fails, differently.
  3. 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.
  4. 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.
  5. 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.


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.

ActorSourceCoverageWhy pick it over this one
This Actordistrict.in (Zomato)India, one city per runDeepest pass over District's own feed — scrolls it to exhaustion, up to 200 events
Smart Event Scraper6 platforms incl. DistrictGlobalCategory, keyword and date-range filters — the filtering this Actor has none of, plus five other sources in the same run
All Events Scraperallevents.inGlobal, incl. IndiaMulti-city discovery, and no residential-proxy requirement — start here if District's setup is a blocker
Eventbrite Scrapereventbrite.comGlobalOrganizer-led events, workshops, meetups and free events
Eventim Scrapereventim.deGermanyLive stock and price signals for German ticketed events
Events Eye Scrapereventseye.comGlobalTrade 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-text search_query, and a date_from/date_to range — with a per-source limit and 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

ActorUse it for
Google Maps Business Leads & Sales IntelligenceVenue contact details — email, phone, website — which this Actor never returns
Hospitality Lead ScraperBars, hotels and restaurants near a venue, for event-driven local outreach
Advanced Website Crawling ActorCrawl 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_url for 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?


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