Rave Finder avatar

Rave Finder

Pricing

Pay per usage

Go to Apify Store
Rave Finder

Rave Finder

Find electronic music events around you (currently only in Czechia)

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Kateřina Raszka

Kateřina Raszka

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

7 days ago

Last modified

Categories

Share

Finds upcoming electronic music and DJ events — techno, house, drum & bass, and generally-electronic events that don't name a specific genre — near a Czech city, within a radius you choose. Searches Resident Advisor, event aggregators (GoOut, DnB e-Heard, ColosseumTicket, KdyKde, xTicket, KoncertyPraha, Rave.cz), club websites, and Facebook Events.

v1 scope: Czech Republic only.

How it works

  1. Geocodes the city you give it (OpenStreetMap Nominatim, no API key needed).

  2. Crawls several kinds of sources, in phases (see "Actor-run budget" below):

    • Resident Advisor — the primary source, and the only one that's simultaneously free, genre-tagged at the source, and geographically scoped by the API itself. RA's GraphQL API needs no authentication, so src/crawlers/residentAdvisorCrawler.js calls it directly rather than paying for a scraper Actor: no per-event cost, and it doesn't consume a concurrent-Actor-run slot. Verified live: one country-wide query returns ~114 upcoming Czech events, 111 of them with real street addresses, carrying RA's own genre taxonomy ("Techno", "Progressive House", "Garage", "Electronica"…). RA areas are city/region level — Prague and Brno have their own, smaller towns like Ostrava and Frýdek-Místek don't — so this queries the country-wide "All Czech Republic" area and lets the radius filter do the geography.

    • Aggregators and club sites — both crawled directly with Crawlee (free, no Actor call) through the shared extractors in src/extractors/, in three tiers: schema.org JSON-LD where a site publishes it, then DOM event cards, then dated links.

      This replaced an earlier pipeline that ran each club site through apify/website-content-crawler and parsed the resulting Markdown. That version found 0 events across 13 sites on two separate runs, and the sites were never the problem:

      • Markdown flattening destroyed the card structure tying a date to its title. rokac.cz has 9 upcoming events in plain server-rendered HTML; the line heuristic saw none.
      • Nothing walked nested JSON-LD, so barrak.cz's 67 fully-structured events were invisible — they hang off a LocalBusiness object's events property.
      • It cost a paid Actor call and a concurrent-run slot per site, with 30–150s of container startup each, for no return.

      Crawling directly is free, needs no concurrency slot, and covers all 13 sites in about 1.5 seconds. The JS rendering the Actor charged for was never what was missing; these are ordinary server-rendered pages. Two per-source title rules (locationSuffixMarker, cityFromHashPrefix) parse the venue and town out of titles on the two national genre calendars, which is what makes their events placeable at all.

    • Club sites (Maps-discovered) — the seeded list only covers ~17 venues in a handful of cities, so it contributes nothing for a city outside that list. To cover any Czech city/radius, src/crawlers/mapsDiscoveryCrawler.js searches Google Maps (compass/crawler-google-places, ~$1.50 per 1,000 places — billed through the normal Apify account, not x402) for club-like venues near the geocoded city center, then crawls each discovered venue's website the same way as the seeded list. Capped by maxMapsVenues; set to 0 to disable.

    • Facebook venue pages — the source that actually works for small towns, and on by default (includeFacebookVenuePages). Rather than searching, it reads the events tab of venues already established to be in range. Verified live against Rokáč: its own website yielded nothing under the free cheerio crawler, while facebook.com/rokac.cz/upcoming_hosted_events returned 15 events with dates and coordinates. The plain page URL returns one empty record — the /upcoming_hosted_events tab is the URL shape that works, and startUrls takes plain strings, not { url } objects (the Actor calls url.match() on each entry and crashes otherwise). Cost scales with the number of nearby venues instead of with search noise. Because a venue page publishes the venue's whole programme, events inherit the venue's trustedElectronic (so a DJ night naming no genre survives) but are screened by a non-music filter — a live run otherwise surfaced a wine-and-burčák tasting. The filter drops tastings, workshops, yoga, tournaments and markets while deliberately keeping anything that might be a DJ night.

  3. Classifies each event's genre(s) — see src/genreClassifier.js:

    • Structural tags win outright (e.g. DnB e-Heard's forcedGenre: 'drum_and_bass').
    • A specific genre keyword (techno/house/drum & bass, CZ + EN, word-boundary matched — not a plain substring check, since e.g. "techno" as a substring would false-positive on "technologie"/"technické") wins next.
    • Otherwise, sources already known to be dedicated to electronic music (trustedElectronic: true in seedSources.js — Rave.cz, GoOut's electronic-music category, and the seed clubs branded as electronic-only venues) still keep the event, tagged generically as electronic. This is what makes a branded event like "Beats for Love Experience w/ KANINE" or a local party name survive even though its title names no genre — the source itself is the guarantee.
    • Everywhere else (general ticketing aggregators, mixed-programming clubs, Facebook venue pages), an event only survives if its own text carries a generic electronic/DJ signal. Strong signals ("dj", "DJane", "b2b", "dj set", "dubstep", "trance") count anywhere including the description; weak ones ("bass", "beat", "party", "disco", "afterparty") count only in the title, because in a rock band's description "bass" is a guitar and a metal gig advertises an afterparty just as readily. This is what keeps rock/jazz/theatre listings on those same sources from flooding the output.
  4. Geocodes each venue and filters by distance from the city center — cached in the key-value store, except Maps-discovered venues and Resident Advisor events, which already carry their own coordinates or a street address.

    Club sites are also distance-pre-filtered before being crawled, not just after: every seeded venue has a known city, so geocoding that city (cached, free) rules out the ones that can't possibly be in range. Searching Návsí used to spend one Actor call each on Cross Club, Roxy, Ankali, MeetFactory, Lucerna and the rest of the Prague/Brno list — all ~350km away, every resulting event discarded by the radius filter anyway.

  5. Filters by requested genres and date range, then dedupes events that show up on more than one source.

  6. Pushes the results to the default dataset.

Searching a specific date window

dateRangeDays can only ever mean "the next N days", which is the wrong shape for the most common reason to run this — a trip. "I'm in Prague 12–15 September" isn't expressible at all: you'd have to wait until the 12th and pass 3.

dateFrom / dateTo override it, and either works on its own:

GivenWindow searched
dateFrom + dateToexactly that, both bounds inclusive
dateFrom onlythat date, for dateRangeDays afterwards
dateTo onlytoday until that date
neithertoday for dateRangeDays — the original behaviour, unchanged

Bad input fails loudly rather than being silently misread: 12.9.2026 is rejected with the expected format, and a dateTo before dateFrom is an error. A window entirely in the past warns that every source here lists upcoming events only.

The window is resolved once and passed into the Resident Advisor GraphQL filter, rather than RA deriving its own from dateRangeDays — two independent computations of the same thing would have drifted the moment explicit dates existed. Bounds stay as plain YYYY-MM-DD strings compared lexicographically, which avoids the timezone bugs that come from turning "the 12th" into an instant.

This is also where Resident Advisor earns its place. It's Prague-centric (105 of ~115 Czech events), so it contributes nothing for a village search — but for a Prague trip it's the best source here, with real street addresses and RA's own genre taxonomy. A live run for Praha / 15km / 12–15 Sept returned 17 events across Cross Club, Roxy, Ankali, MeetFactory and a dozen others. Scoping the window also shrinks the RA request itself: 24 events fetched instead of 137.

Adding a promoter page

Around small towns the promoter creates the event and only tags the venue, so a venue's own Facebook page often won't list it. A live example: a Jablunkov event the Actor missed was nowhere on Rock Café's page (its Upcoming started the following day and its Past stopped two weeks earlier), nowhere in any wired source, and not indexed by DuckDuckGo or Bing. Facebook's own search is the only place it exists, and that needs a login this Actor doesn't have.

So facebookPages lets you close that gap yourself, without a code change or redeploy: paste the promoter's page URL into the Actor input and it's scraped alongside the built-in venues.

  • Either shape works — a page URL (https://www.facebook.com/somepromoter) gets the events tab appended automatically, and a single event URL (https://www.facebook.com/events/123/) is passed through untouched. Trailing slashes, query strings and stray whitespace are fine.
  • Entries that aren't facebook.com URLs are skipped with a warning rather than wasting a call.
  • These pages deliberately skip the city pre-filter, since the point is to reach a promoter whose town the Actor can't guess. Nothing is lost by that: Facebook returns real venue coordinates, so the per-event radius filter still decides what's actually nearby.
  • A page already covered by the seed list is deduplicated, so adding one you already have costs nothing.
  • Each page costs one Actor call and shares the maxFacebookEvents budget.

Same-day events are a blind spot. Facebook moves an event out of "Upcoming" once it has started, so an evening run won't see anything happening that same evening — the scraper reads the upcoming tab only. Run in the morning for same-day coverage.

Why Facebook's event search was removed

Facebook's event search is keyword matching, not a location filter, so the place name in a query is advisory at best. It was tried three ways and each failed differently:

  • The literal input city ignored the place. "drum and bass Návsí" returned ~150 global D&B events from Coventry, Budapest and Brooklyn.
  • Real nearby towns ignored the genre. "drum and bass Havířov" returned every unrelated event in Havířov — maternity-ward tours, yoga classes, a dog-school race, board-game nights.
  • Enabling it aborts the whole run. This is what settled it. A Návsí search with includeFacebookEvents: true spent the entire 300-second budget inside the scraper and was killed by the platform, so nothing was pushed at all — every other source's results were discarded too. It also drew Facebook's own "Rate limit exceeded" and looped on pagination retries ("Forcing additional scrolling", retryCount past 10).

That run's harvest was three distinct kinds of garbage, none of it near Návsí:

  • Jazz rhythm sections, because the search ORs the words. "drum and bass Karviná" kept returning "Joan Minor featuring The Uli Geissendoerfer Trio with Peppe on Drums and Derek Jones on the Bass" — seven times over, under seven different event ids.
  • The town's unrelated events: a political-party barbecue (GRILOVAČKA ČSSD KARVINÁ), a kids' craft workshop (Lapač slunce), an open day, a photography course.
  • Real D&B on the wrong continent: Budapest Park, Poland, the US.

Every one of those is billed at ~$0.013 before this Actor's genre and radius filters throw it away. A default-off switch wasn't enough protection, since flipping it guarantees a lost run, so the path is deleted — recoverable from git history if a different approach ever warrants it.

None of this is a limitation of Facebook as a source: small promoters genuinely do publish there, just on venue and promoter pages rather than anywhere the event search reaches. That's what crawlFacebookVenuePages does — it asks named, already-in-range venues what they have on, so cost scales with the number of nearby venues instead of with search noise.

Icon

The Actor's icon lives at .actor/logo.png (512x512 PNG) so it's versioned with the project, but Apify does not pick it up from the repoactor.json has no icon property, and the build ignores the file. It has to be uploaded once in Apify Console under Publication → Display information → Actor logo. Re-uploading is only needed when the image itself changes; it survives every rebuild.

Actor-run budget

Apify caps concurrent Actor runs per account (5 on the plan this was built against, including this Actor's own run), and a called Actor's default memory counts against a shared ceiling too. Both limits were hit hard in testing, and failed silently-ish: every club-site crawl in a run aborted with "you will exceed your limit of 5 concurrent Actor runs" while the run itself still reported success and zero results.

Moving the club and aggregator crawls in-process removed most of that pressure — they now cost zero Actor slots. What remains is phased rather than maximally parallel:

PhaseWhat runsActor calls
1Resident Advisor, aggregators, nearby-town lookup, Maps discovery1 (Maps only)
2Club sites (in-process), then Facebook venue pages1 per venue page, 3 at a time

Facebook venue pages take one Actor call per page. Batching them into a single call looks cheaper but isn't: the scraper's maxEvents is a whole-run total, so whichever page it crawls first swallows the entire budget — a run with 6 pages and a cap of 20 returned 16 events all from one venue, and the other five contributed nothing. One call per page with maxEvents / pages each costs the same and actually covers every venue.

Free/keyless sources (Resident Advisor, Nominatim, Overpass) and the in-process Crawlee crawls cost zero Actor slots.

A run that overshoots the timeout publishes nothing. Everything is pushed in one call at the end, so an abort doesn't degrade the output, it destroys it — including work every other source already finished. Geocoding is the only step that can still get there (1 request/second under Nominatim's policy, and a failed lookup is retried three times with backoff — a Návsí run had 17 unplaceable venues at roughly 6s each), so it stops starting new lookups 45s before the assumed 300s limit and publishes what it placed, logging a warning. A normal run finishes in about 130s locally, so this is a safety net rather than something you should hit; because successful lookups are cached in the key-value store, a re-run gets further.

The 300-second default run timeout is the design constraint, not a problem to raise. Note that defaultRunOptions is not a valid actor.json property (see Apify's actor.json reference) — putting it there is silently ignored, which cost one early run an abort. It can only be changed in Apify Console under Settings → Run options. Since the paid per-site crawls are gone, a full run finishes well inside the default, and the earlier deadline-and-skip machinery has been removed. The remaining slow step is geocoding, rate-limited to 1 request per second by Nominatim's usage policy — which is why events are filtered by city first (geocoding each distinct town once, cached) before any venue is geocoded individually.

Input

FieldTypeDefaultDescription
citystring(required)Czech city to search near, e.g. "Brno".
radiusKminteger30Max distance from the city center, in km (1–300).
genresarrayall fourtechno, house, drum_and_bass, electronic (generic — electronic/DJ events with no specific genre named).
dateRangeDaysinteger30Only include events within this many days from now (1–180).
dateFromstring(none)Start of an explicit window, YYYY-MM-DD. Overrides dateRangeDays — see below.
dateTostring(none)End of the window, YYYY-MM-DD, inclusive.
includeFacebookVenuePagesbooleantrueAsk in-range venues' Facebook pages what they have on. The only Facebook path — the event search was removed, see below.
facebookPagesarray[]Extra Facebook page or event URLs to check alongside the built-in venues. See below.
maxFacebookEventsinteger20Total budget for Facebook venue-page events, split evenly across the pages asked, to control cost.
maxMapsVenuesinteger5Caps Maps-discovered venues per search term, to control cost; 0 disables Maps discovery. Low by default — each discovered venue costs an Actor call, and most Maps hits are dance schools and bars, not electronic venues.

See .actor/input_schema.json for the full schema.

Running locally

npm install
apify run

(Or npm start, which just runs node src/main.js directly — apify run additionally sets up local Actor storage under ./storage, which is the more realistic way to test.)

Provide input either by editing storage/key_value_stores/default/INPUT.json after a first apify run, or by running apify run --input '{"city": "Brno"}' (see the Apify CLI docs for details).

Project structure

.actor/actor.json Actor metadata
.actor/logo.png Store/Console icon, 512x512 (upload via Console — see below)
.actor/input_schema.json Input schema
src/main.js Pipeline orchestration
src/sources/seedSources.js Seed list of club sites and aggregators
src/crawlers/ One crawler module per source type
src/extractors/dates.js Czech date formats (ISO, numeric, named month, year-less)
src/extractors/jsonLdEvents.js Recursive schema.org Event extraction, any nesting depth
src/extractors/eventCards.js DOM event-card extraction, for pages without JSON-LD
src/concurrency.js Bounded-concurrency helper for crawling many sites in parallel
src/geocode.js Nominatim geocoding + Haversine distance, KV-cached
src/genreClassifier.js Genre classification: specific keywords, trusted-source fallback
src/dedupe.js Cross-source duplicate detection
Dockerfile apify/actor-node base image

Known limitations (v1)

  • Non-JSON-LD extraction is generic (event cards keyed off a date, plus a dated-link fallback), not a per-site scraper, so it will miss events on unusually-structured pages. This is an accepted trade-off rather than maintaining ~20 bespoke scrapers, but it does fail visibly on some sites: dnbczevents.cz groups listings under day headers, so every event resolves to the weekday above it ("pátek", "sobota"). Dates are read correctly; only the titles need a site-specific selector.
  • Dates written without a year are assumed to be the current year, which means a genuinely next-January event listed as "9. 1." will be dated this January and dropped. Deliberate: the alternative (rolling past months forward) turned dnbeheard.cz's 501 entries into a wall of January-2027 parties that don't exist. A miss is a gap in coverage; a phantom is wrong data in the output.
  • Genre/electronic-music detection is keyword-based for anything not from a trustedElectronic source. The vocabulary covers genre names, lineup notation (w/, b2b, dj set, DJane), the bass/beat/trance word family, and a short list of named brands (Beats for Love, Let It Roll). A listing that names none of those — just a bare artist name — is still missed. The gate is tested against 29 real titles from these sources: all 10 electronic ones match and all 19 rock/metal/community ones don't ("Live Tribute Act To RAMMSTEIN", "Moravský ples", "Taneční kurz pro dospělé").
  • Scope is Czech Republic only.
  • The two national D&B/techno calendars are now the highest-yield sources, both read by the card extractor with a small per-source title rule: jiripetrak.cz (143 upcoming events, all carrying "▼ Venue, Town") and dnbeheard.cz (a full year at ~501, written "#Town Title, Venue", 500 of which parse to a town). Only dnbczevents.cz remains parked in CANDIDATE_AGGREGATORS_NEEDING_CUSTOM_EXTRACTION.
  • Resident Advisor doesn't help outside Prague/Brno. Its Czech coverage is Prague-centric: of ~115 upcoming events, 105 are Prague, 3 Brno. For a search near a smaller town it contributes candidates but no results — the nearest listing to Návsí was Olomouc, ~100km out. Excellent for Prague or Brno searches, irrelevant for a village.
  • GoOut is underused. It's the largest Czech event source, and it does have a real public API — https://goout.net/services/entities/v1/schedules responds with structured schedules (dates, pricing, ticketing state) once you pass the required languages[] parameter. But its filter parameter names still need reverse-engineering: tag and city were both silently ignored in testing, and venue/performer data appears to need an include= parameter. Until that's worked out, GoOut is still just being HTML-scraped like the other aggregators. Worth doing — it's probably the biggest remaining coverage win.
  • A Facebook venue page is only worth seeding once it's confirmed to host events, and most don't. Four researched-but-unvalidated pages (TESLA Production/Třinec, PartyTime and Project Bar, DNB pro Ostravaky) were seeded and every one returned "No event detail URLs found". The slugs were all real; the pages simply host nothing. Checking two by hand showed why, and it's a regional pattern: TESLA's events tab reads "No events to show" with only past entries, and PartyTime's page has no events tab at all. Around here the promoter creates the event and merely tags the venue — TESLA's own feed advertises "Future Control Open Air 2026", hosted by a separate Future Control page. Promoter pages are the better target, but finding them needs a logged-in Facebook search this Actor can't perform, so they have to be added by hand.
  • Slovak and Polish events near the eastern border are dropped, because scope is Czech-only and every place name is geocoded with ", Czech Republic" appended. That's a real gap for a border town rather than a theoretical one: from Návsí, Žilina (SK) is 40km and Cieszyn (PL) 20km, while Prague is 316km — and dnbeheard.cz does list Žilina D&B nights. They fail to geocode and are discarded as unplaceable.
  • Facebook coverage is only as good as the hand-maintained venue list. With the event search gone (see above), the only Facebook path is asking named venue pages what they have on — and finding the right pages needs a logged-in Facebook search this Actor can't do. In the Návsí region the promoter creates the event and merely tags the venue, so promoter pages are the better target and have to be added by hand.