# Eventbrite Scraper — Events, Organizers & Ticket Prices (`scrapersdelight/eventbrite-scraper`) Actor

Scrape Eventbrite events by city, keyword, category, date window or free-only - or paste any eventbrite.com search or event URL. One row per event: venue + full address, geo, organizer name & URL, ticket price range. No login, no API key.

- **URL**: https://apify.com/scrapersdelight/eventbrite-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Lead generation, Marketing
- **Features:** Limited permissions
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.50 / 1,000 per event returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Eventbrite Scraper — Events, Organizers & Ticket Prices

Scrape [Eventbrite](https://www.eventbrite.com) and get **one row per event**, carrying
**name, summary, start_date, start_time, end_date, end_time, timezone, venue_name, venue_address,
venue_city, venue_region, venue_postal_code, venue_latitude, venue_longitude, organizer_name,
organizer_url, organizer_id, price_min, price_max, price_currency, is_free, ticket_availability,
categories, subcategories, formats, keywords, image** and **tickets_url**.

Search by city, keyword, one of Eventbrite's 21 categories, a date window, or free-events-only — or
just paste an eventbrite.com search or event URL and skip the filters entirely.
**No login. No API key. No CAPTCHA solving.** Everything this Actor reads is JSON that Eventbrite
serves to its own pages.

**Measured scope, 2026-08-13:** Eventbrite reports `page_count = 49` on an Austin search — identically
on `all-events`, `music` and `business` — but it still serves **page 50**, and page 51 comes back empty
with `object_count` collapsing to exactly `1000`. So **one search tops out at ~1,000 events** — a full
50-page Austin walk on 2026-08-13 returned **981 unique events** from 1,000 cards in 147 seconds — and
this Actor walks that 50th page rather than trusting the reported 49. That is a source cap, not an Actor
limit, and it is why the category / keyword / date filters exist: they are how you get past 1,000 in a
city. Measured live on Austin (`all-events` + `music`), London and New York — not quoted from a
marketing page.

**The wedge, with its number: `organizer_name`, `organizer_url` and `price_min`/`price_max` are filled
on 100% of rows — measured on 120 contiguous Austin events on 2026-10-04.** Neither the organizer's name
nor the ticket price exists on an Eventbrite listing page; both come from Eventbrite's own event records,
which this Actor reads for you in batches of 20 events per request.

### Quick start — this is the input it ships with

```json
{
  "location": "Austin, TX",
  "maxResults": 20
}
```

Click **Try for free** and hit **Start**. That is the literal prefilled input; it needs zero edits.
Measured on 2026-10-04 it returned **20 rows in 6 seconds** (1 listing fetch + 1 batch of event records).

1. Create a free Apify account.
2. Open the Actor and press **Start** — or type a different city into **City** first.
3. Watch the log: it prints rows per page, duplicates dropped, and this run's own measured field fill.
4. Open the **Dataset** tab and switch between the **Events**, **Organizer leads** and **Venues & geo** views.
5. Export CSV / JSON / Excel, or pull it from the API.

Signup to downloaded dataset is about 3 minutes at the default 20-row cap.

### Copy this to your AI assistant

```text
scrapersdelight/eventbrite-scraper on Apify.
Call: ApifyClient("TOKEN").actor("scrapersdelight/eventbrite-scraper").call(run_input={...}),
then client.dataset(run["defaultDatasetId"]).list_items().items for results.
Key inputs: startUrls (array of {url}, eventbrite /d/ /b/ /e/ pages; wins over all filters),
location (string, "Austin, TX" or slug "tx--austin"/"united-kingdom--london"; prefilled in the UI as
"Austin, TX" but NOT defaulted in code — an API call must send location, startUrls or onlineOnly),
locations (array of strings), keyword (string -> ?q=), categories (array of 21 enum slugs),
freeOnly (bool), onlineOnly (bool), startDate/endDate ("YYYY-MM-DD"),
maxResults (int, default 20, = cost cap), maxPages (int, default 3, max 50),
fetchDetails (bool, default true), concurrency (int, default 6), proxyConfiguration.
Billing: $0.0045 per event returned, event id "event-scraped", no actor-start charge.
Full actor spec: GET https://api.apify.com/v2/acts/scrapersdelight~eventbrite-scraper (Bearer TOKEN).
Get token: https://console.apify.com/account/integrations
```

### Who buys this, and the field they monetize

| Who | What they do with it |
|---|---|
| **Event-sponsorship sales** | `organizer_name` + `organizer_url` + `organizer_id` — the person who signs the sponsorship cheque. Listing pages give you an organizer *id* and nothing else; this is the name. |
| **AV, staging, catering & production vendors** | `venue_name` + `venue_address` + `start_date` — who needs gear, where, on which date, 4–12 weeks out. |
| **Ticketing / event-tech SaaS** | `price_min`, `price_max`, `is_free` — organizers already selling paid tickets are the ICP; 87.5% of the 2026-08-12 120-row Austin sample were paid. |
| **Venue & hospitality sales teams** | `venue_name` frequency across a run tells you which rooms are actually booked and which organizers repeat. |
| **DevRel and community teams** | `categories: ["Science & Technology"]` / `["Business & Professional"]` plus `organizer_url` — the list of local meetups to sponsor or speak at. |
| **City guides, local media, event aggregators** | `image`, `tickets_url`, `summary`, `venue_latitude`/`venue_longitude` — everything a listings page or a map needs, refreshed on an Apify Schedule. |

### Read this before you buy rows

1. **A single search caps at ~1,000 events.** 50 pages × 20 events per page. Eventbrite *reports*
   `page_count = 49`, but page 50 serves 20 more and page 51 is empty — measured on Austin, London and
   New York. If you need a whole city, run it once per category (21 of them), or slice by date window,
   or by keyword. `maxPages` above 50 just stops early.
2. **`is_free` and `price_min: 0` are not the same thing.** `is_free: true` means *every* ticket tier is
   $0. `price_min: 0` means there is *a* free tier — in the 2026-08-12 120-row sample, 22 rows had `price_min: 0`
   but only 9 were `is_free: true`. The other 13 have a free tier plus paid tiers.
3. **Turning `fetchDetails` off costs you the organizer and the price, and nothing else.** It saves one
   request per 20 events, and **the price per event is identical either way** — you are billed per row
   returned, not per fetch.
4. **Online events have no address.** With `onlineOnly` on, `venue_city`, `venue_address`,
   `venue_latitude` and `venue_longitude` come back `null` (measured 2026-08-13: 20 of 20). That is
   Eventbrite, not a bug.
5. **`keyword` is Eventbrite's own fuzzy search, not an exact match, and you pay for what it returns.**
   Measured 2026-08-13: the nonsense query `underwater basket weaving qwerty` still came back with a
   full page of 20 Austin events — a basket-weaving class, a crochet-basket class, then loosely related
   ones. Check the first page at a low `maxResults` before you raise the cap.
6. **Eventbrite's own data has typos in it.** One of the 120 Austin rows carries
   `venue_city: "Austi"`. We pass the source through rather than silently "correcting" it.

### What you get — one row per event

| Group | Fields |
|---|---|
| **Identity** | `id` (numeric Eventbrite event id, stable — the dedupe key), `name`, `url`, `summary`, `image`, `tickets_url` |
| **Schedule** | `start_date`, `start_time`, `end_date`, `end_time`, `timezone` (local), plus `start_datetime` / `end_datetime` (ISO-8601 with the event's offset, e.g. `2026-08-23T16:00:00-05:00`; see the note below) |
| **Venue** | `venue_name`, `venue_address`, `venue_city`, `venue_region`, `venue_postal_code`, `venue_country`, `venue_latitude`, `venue_longitude`, `is_online_event` |
| **Organizer** | `organizer_name`, `organizer_url`, `organizer_id` |
| **Money** | `price_min`, `price_max`, `price_currency`, `is_free`, `ticket_availability` (`InStock` / `SoldOut`) |
| **Taxonomy** | `categories`, `subcategories`, `formats` (Eventbrite's own tags), `keywords` (the organizer's own tags) |
| **Provenance** | `source` (always `"Eventbrite"`), `scraped_at` (ISO-8601 UTC) |

Prices are numbers in `price_currency` units. Dates in `start_date`/`end_date` are local `YYYY-MM-DD`.
`start_datetime`/`end_datetime` are built from Eventbrite's own event record — the local date, time and
time zone — as full ISO-8601 with the event's offset (120 of 120 Austin rows on 2026-10-04). An event the
batch records cannot describe exactly (a ticket state other than on sale or sold out) is read from its
event page instead, and there the value is passed through as the page's JSON-LD publishes it, which on
some events is a bare `YYYY-MM-DD` end date. Parse defensively, or use `end_date` + `end_time`, which are
always split fields. Absent values are `null`, never dropped, so your CSV columns stay stable run to run.

### Field fill — measured on 120 contiguous Austin events, 2026-10-04

Austin, TX · all categories · pages 1–6 walked contiguously · `fetchDetails: true` · concurrency 6 ·
120 rows in 19 seconds.

| Field | Fill |
|---|---|
| `id`, `name`, `url`, `start_date`, `start_time`, `end_date`, `end_time`, `timezone`, `is_online_event` | **100.0%** |
| `venue_name`, `venue_address`, `venue_city`, `venue_region`, `venue_country` | **100.0%** |
| `venue_postal_code` | 99.2% |
| `venue_latitude`, `venue_longitude` | **100.0%** |
| `organizer_id`, `image`, `tickets_url`, `summary` | 100.0% |
| **`categories`**, `formats` | 100.0% |
| **`organizer_name`**, **`organizer_url`**, **`price_min`**, **`price_max`**, **`price_currency`**, **`is_free`**, `ticket_availability`, `start_datetime`, `end_datetime` | **100.0%** |
| `subcategories` | 96.7% |
| `keywords` (organizer's own tags) | 95.0% |

**The number that could mislead you: 100% on organizer and price is one measured run, not a promise.**
All 120 events came from Eventbrite's batch event records. An event the records leave out, or whose
ticket state is neither on sale nor sold out, is read from its event page instead — and on that path,
in a 2026-08-12 measurement, 6 of 120 page requests failed after three attempts and 3 pages published
`"name": null` for the organizer. `organizer_id` is 100% either way.

With `fetchDetails: false`, everything detail-derived — `organizer_name`, `organizer_url`, `price_min`,
`price_max`, `price_currency`, `is_free`, `ticket_availability`, `start_datetime`, `end_datetime` — is
`null` by construction, and every other field above is unchanged.

**Every run prints its own measured fill in the log.**

### How to run it

#### Search a city

```json
{ "location": "Austin, TX", "maxResults": 60, "maxPages": 3 }
```

`location` takes `"Austin, TX"` or an Eventbrite place slug. **Slugs are not US-only** — verified live,
`united-kingdom--london` returns 20 rows/page exactly like a US city. Use `locations` for several cities.

#### Filter it

```json
{
  "location": "Austin, TX",
  "keyword": "yoga",
  "categories": ["health"],
  "freeOnly": true,
  "startDate": "2026-08-20",
  "endDate": "2026-09-20",
  "maxResults": 10
}
```

Measured 2026-08-12: that exact input returned 10 rows, **10 of 10 `is_free: true`**, all tagged
`Health & Wellness`, every `start_date` inside the window. Filters run inside Eventbrite's search, so a
filtered run costs **fewer** fetches, not more.

#### Paste URLs instead

```json
{
  "startUrls": [
    { "url": "https://www.eventbrite.com/d/ny--new-york/all-events/" },
    { "url": "https://www.eventbrite.com/b/tx--austin/music/" },
    { "url": "https://www.eventbrite.com/e/from-day-one-austin-2026-tickets-1435465258229" }
  ],
  "maxResults": 45
}
```

Three shapes are supported, all three verified live in one run on 2026-08-12:

- `https://www.eventbrite.com/d/<place>/<category>/?…` — a search page. Any query string you copied
  is preserved and paged through, up to `maxPages`.
- `https://www.eventbrite.com/b/<place>/<category>/` — a browse page. Eventbrite's `/b/` pages carry
  **no** search data, so the Actor rewrites them to the `/d/` path that does, rather than returning nothing.
- `https://www.eventbrite.com/e/<slug>-tickets-<id>` — one event, one row, built from that page alone.

Organizer `/o/` pages are **rejected with an error** naming the supported shapes, instead of silently
producing nothing. When `startUrls` is set, the city / keyword / category / date / free filters are
ignored and the run logs a warning saying so.

#### Online events only

```json
{ "onlineOnly": true, "categories": ["business"], "maxResults": 50 }
```

Measured 2026-08-13: **20 of 20** rows came back `is_online_event: true`, with `venue_city`,
`venue_latitude` and `venue_longitude` all `null`.

#### Cheap mode

```json
{ "location": "Austin, TX", "maxResults": 200, "maxPages": 10, "fetchDetails": false }
```

One request fewer per 20 events. You keep name, schedule, full venue address, lat/lon, categories,
image and tickets URL. You give up organizer name/URL and ticket price. **Same price per event.**

### Sample row — a real record from the 2026-08-12 Austin run

```jsonc
{
  "id": "1986659414789",
  "name": "No Man's Land Film Festival Presents: Seoul to Sol",
  "url": "https://www.eventbrite.com/e/no-mans-land-film-festival-presents-seoul-to-sol-tickets-1986659414789",
  "summary": "Join us for the premiere of documentary Seoul to Sol, followed by a conversation on rock climbing, community, and belonging in the outdoors.",

  "start_date": "2026-08-23",
  "start_time": "16:00",
  "end_date": "2026-08-23",
  "end_time": "19:30",
  "timezone": "America/Chicago",
  "start_datetime": "2026-08-23T16:00:00-05:00",
  "end_datetime": "2026-08-23T19:30:00-05:00",

  "is_online_event": false,
  "venue_name": "AFS Cinema",
  "venue_address": "6259 Middle Fiskville Road, Austin, TX 78752",
  "venue_city": "Austin",
  "venue_region": "TX",
  "venue_postal_code": "78752",
  "venue_country": "US",
  "venue_latitude": 30.3243646,
  "venue_longitude": -97.7086944,

  "organizer_id": "121212027087",
  "organizer_name": "Seoul to Sol Film",
  "organizer_url": "https://www.eventbrite.com/o/seoul-to-sol-film-121212027087",

  "price_min": 10.38,
  "price_max": 10.38,
  "price_currency": "USD",
  "is_free": false,
  "ticket_availability": "InStock",

  "categories": ["Film, Media & Entertainment"],
  "subcategories": ["Film"],
  "formats": ["Screening"],
  "keywords": ["Movie", "Sports", "Filmfestival", "Filmscreening", "Rockclimbing",
               "Koreanculture", "Nomansland", "indiefilmscreen", "asianfilmshowcase", "womanclimber"],

  "image": "https://img.evbuc.com/…",
  "tickets_url": "https://www.eventbrite.com/checkout-external?eid=1986659414789",
  "source": "Eventbrite",
  "scraped_at": "2026-08-13T01:50:23.501Z"
}
```

Three things people misread:

- `price_min == price_max` means one ticket tier, not a missing range. In the 2026-08-12 120-row sample the median
  `price_min` was $30.87 and the highest $2,787.22.
- `keywords` are the **organizer's** free-text tags; `categories`/`subcategories`/`formats` are
  **Eventbrite's** controlled taxonomy. Only the latter are safe to group by.
- `organizer_id` is present even when `organizer_name` is not — it comes from the listing page, the name
  from Eventbrite's event record.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `startUrls` | array (URLs) | — | Paste `/d/`, `/b/` or `/e/` eventbrite.com URLs. **Wins over every filter below.** |
| `location` | string | `"Austin, TX"` *(prefilled)* | City, or an Eventbrite place slug (`tx--austin`, `united-kingdom--london`). |
| `locations` | array | — | Several cities in one run; replaces `location`. Each city costs its own listing pages. |
| `onlineOnly` | boolean | `false` | Search Eventbrite's `online` place instead of a city. No venue/geo on those rows. |
| `keyword` | string | — | Free text, appended as `?q=`. |
| `categories` | multi-select | *(none = all)* | Eventbrite's 21 categories. **Each one you tick is a separate search** — 3 categories × 3 pages = 9 listing fetches. |
| `freeOnly` | boolean | `false` | Searches Eventbrite's free-events path. See the honest limit below about "paid only". |
| `startDate` | date | — | `YYYY-MM-DD`; events starting on or after. A malformed date stops the run. |
| `endDate` | date | — | `YYYY-MM-DD`; events starting on or before. |
| `maxResults` | integer | `20` *(prefilled)* | **Your hard cost cap.** Duplicates are dropped before billing and never count against it. |
| `maxPages` | integer | `3` (max 50) | Listing pages per (city × category), ~20 events each. |
| `fetchDetails` | boolean | `true` | Organizer + price from Eventbrite's event records, one request per 20 events (an event page for any event the records cannot answer). Off = one request fewer per 20 events, same price per row. |
| `concurrency` | integer | `6` | Parallel event-record and event-page requests. 6 is what the published throughput was measured at. |
| `proxyConfiguration` | proxy | Apify **RESIDENTIAL**, country `US` | Overridable. The default is measured, not cautious — see the ladder below. |

#### Category slugs

`music` · `business` · `food-and-drink` · `community` · `arts` · `film-and-media` ·
`sports-and-fitness` · `health` · `science-and-tech` · `travel-and-outdoor` · `charity-and-causes` ·
`spirituality` · `family-and-education` · `holiday` · `government` · `fashion` · `home-and-lifestyle` ·
`auto-boat-and-air` · `hobbies` · `school-activities` · `other`

All 21 were walked live on 2026-08-12 and each returned 19–20 rows. Note that Eventbrite's *display*
names differ from its slugs: `arts` is "Performing & Visual Arts", `health` is "Health & Wellness",
`spirituality` is "Religion & Spirituality". A slug that is not on this list stops the run with an
error that lists the valid ones, rather than quietly returning nothing.

### Pricing

**$0.0045 per event returned — $4.50 per 1,000.** Charged on the `event-scraped` event.
**There is no charge to start a run**, and no monthly platform fee from this Actor.

You are charged for **rows delivered**. You are never charged for a listing page, never for an
enrichment request that failed, never for a retry, never for our proxy budget, and never twice for the same event —
duplicates are dropped on the Eventbrite event id *before* anything is billed. Rows are charged as they
are pushed, so if you hit a budget cap you get whole rows and stop, not a half-billed dataset.

| Run | Events | Cost |
|---|---|---|
| The prefilled default | 20 | **$0.09** |
| One city, 3 pages | 60 | $0.27 |
| One city, all events, to the source cap (measured) | 981 | $4.41 |
| Three categories, 3 pages each (measured) | 167 | $0.75 |
| Ten cities × 1,000 | 10,000 | $45.00 |

`maxResults` is the dial: it is a hard cap on rows and therefore a hard cap on spend.

### Honest limits

- **~1,000 events per search, full stop.** Eventbrite reports `page_count = 49` at 20 results per page,
  measured identically on Austin `all-events`, `music` and `business` — but the reported count is one
  short: page 50 serves 20 more events, and page 51 returns nothing at all with `object_count` reading
  exactly `1000` (verified 2026-08-13 on Austin, London and New York). This Actor walks all 50. There is
  no page 51, and no parameter that produces one. Slice by category, keyword or date window instead.
- **There is no "paid events only" filter, deliberately.** Eventbrite's `/paid--events/` path returned
  the same unfiltered result set as the open search (2 of 8 sampled rows were free), and the `?price=`
  query parameter is a no-op — on 2026-08-12 `?price=free` and `?price=paid` returned the same 8 events
  in the same order with the same prices. `freeOnly` uses the `/free--events/` path, which **does**
  work (8 of 8 sampled rows at $0). For paid-only, filter `is_free` after the run.
- **An enrichment request can fail and stay failed.** The event-page path (used only for events the
  batch records cannot answer) lost 6 of 120 requests after three attempts each on 2026-08-12; on
  2026-10-04 all 120 Austin events came from the batch records. A row whose enrichment failed still
  ships, with the listing fields at 100% and organizer/price `null`.
- **No email addresses, and none can be produced.** Eventbrite publishes no organizer email anywhere on
  these surfaces. `organizer_url` (100.0% on 2026-10-04) is the handle — the organizer's public Eventbrite page.
  Anyone selling you "Eventbrite organizer emails" generated them somewhere else.
- **No attendees, no ticket-buyer data, no private events.** Attendee lists live behind the organizer's
  own login. This Actor does not log in, so it cannot and does not touch them.
- **Past events are not covered.** Eventbrite's search surface serves upcoming events; `startDate` in
  the past does not resurrect them.
- **Online rows have no geography**, and `keywords` (95.0%) are absent on any event whose organizer
  never tagged it.
- **No CAPTCHA solving, no challenge-breaking.** When a request is challenged, the Actor retries twice
  and then reports the failure. That is the whole mitigation.

### How it works, and the measured transport ladder

Three surfaces, all plain JSON that Eventbrite serves to its own pages — no browser, no key:

1. **Listing page** `/d/<place>/<category>/?page=N` embeds `window.__SERVER_DATA__` →
   `search_data.events.results[]` (~20 events) plus `pagination.page_count`. A brace-balanced extractor
   walks the blob rather than regexing it, so a `}` inside a string value cannot truncate it.
2. **Event records** `/api/v3/destination/events/?event_ids=…` — the batch endpoint Eventbrite's own
   front end reads — return the organizer name/URL, the ticket price range and ticket state, and the local
   start/end with time zone for up to 20 events per request.
3. **Event page** `/e/<slug>-tickets-<id>` carries a schema.org `application/ld+json` Event block with
   the same fields. It is read for pasted `/e/` URLs and for any event the batch records cannot answer
   exactly. Measured 2026-10-04 on 33 Austin events read both ways: organizer, price, currency,
   `is_free`, `ticket_availability` and `start_datetime` identical on all 33; `end_datetime` identical on
   25, and on the other 8 the page published a bare end date where the record carries the full time.

A note on the category path: `/d/<place>/music--events/` **301s to `/b/<place>/music/`, which carries no
search data at all and yields 0 rows.** The live search path is the bare slug, `/d/<place>/music/`. That
is why this Actor builds — and rewrites pasted `/b/` URLs to — the `/d/` form.

**Transport ladder — 20 distinct Austin listing pages, one attempt each, no retries, 2026-08-12:**

| Rung | Result |
|---|---|
| Direct, no proxy, home broadband | 20/20 — **but not a shippable rung**; no Apify container has a residential home IP |
| Apify proxy, auto/datacenter, rotating IP | **4/20 usable** — 15 × HTTP 405 (AWS-WAF), 1 transport error |
| Apify **RESIDENTIAL (US)**, fresh session per request | **18/20 usable** — 1 × 405, 1 timeout |

An earlier measurement on 2026-07-30 put datacenter at ~87% challenged and residential at 16/16 usable;
today's numbers are in the same place. Both were measured **through Apify**, which is the only
measurement that means anything here.

**Sustained load, same day:** the 120-row Austin run made 126 content requests through residential
proxies at concurrency 6 and got 120 usable = **95.2%**, with retries. All 6 listing fetches succeeded;
6 of 120 detail fetches did not. **2026-10-04:** the same 120-row walk made 12 content requests (6 listing
pages + 6 batches of event records), all served.

### Uniqueness and duplicates

- **Contiguous walk, Austin pages 1–6, 2026-08-12: 120 cards → 120 unique ids, 0 duplicates.**
  Sparse-offset sampling would prove nothing here, so the pages were walked back to back.
- **Across categories, duplicates are real: 180 cards → ~167 unique, 12–13 dropped = 6.7–7.2%.**
  Measured twice on Austin `music` + `arts` + `community`, 3 pages each: 12 of 180 on 2026-08-12 and 13
  of 180 on 2026-08-13. The mechanism is simple — an event tagged in two Eventbrite categories appears
  in both searches.
- Dedupe happens on the numeric `id` **before** anything is fetched or billed, so the dataset never
  holds the same event twice and your bill never counts it twice. The run log prints the number dropped.
- Across runs, dedupe on `id` yourself. Two daily runs of the same city will overlap heavily; a
  narrower date window or a `startDate` of tomorrow is the cheap way to pull only what is new.

### When a run fails

- **Zero rows throws.** The error names the scope and the HTTP counts (`0 rows from N/M successful
  listing fetches`) instead of shipping a green-but-empty dataset. There is no legitimate zero here.
- **A bad category slug throws**, listing all 21 valid slugs.
- **A malformed `startDate`/`endDate` throws** before any request is made.
- **An unsupported Start URL throws**, naming the three shapes that are supported.
- **A failed enrichment request does not fail the run** — the row ships with the listing fields and
  `null` organizer/price, and the run's log prints how many events were enriched and from where.

### Related Actors from the same publisher

| Actor | What it is | Why you would use it instead |
|---|---|---|
| [The Bash Vendor Scraper](https://apify.com/scrapersdelight/thebash-scraper) | DJs, bands, photographers and other event vendors | You want the people the event *hires*, not the event |
| [Roaming Hunger Food Truck Scraper](https://apify.com/scrapersdelight/roaminghunger-scraper) | Food trucks with cuisines and contacts | You are catering the event rather than selling to it |

Eventbrite is where you go for the **organizer and the ticket price**; those two are where you go for
the **suppliers**.

### FAQ

**Does this need an Eventbrite account, login or API key?**
No. Every surface it reads is public JSON that Eventbrite serves to any visitor's browser. The Actor
never authenticates.

**Why not just use the official Eventbrite API?**
Eventbrite's public API no longer exposes event *search*; you can only read events you own. Public
discovery by city and category is exactly the gap this fills.

**My run returned 0 results — what happened?**
It would have thrown rather than returned 0, and the error names the scope. The usual cause is a city
slug Eventbrite does not recognise. Use `"Austin, TX"` form, or copy the slug straight out of an
eventbrite.com URL (`/d/united-kingdom--london/…` → `united-kingdom--london`).

**Can I get the whole city in one run?**
Not in one search — the source caps at ~1,000 events. Tick several categories, or run date windows, to
go deeper. Ticking 21 categories × 50 pages is the maximum extraction a single city allows.

**Can I get organizer email addresses?**
No, and nothing on these pages can produce one. `organizer_url` (100.0% fill on 2026-10-04) is the
public organizer page; enrich from there with your own tooling.

**Can I scrape attendees, ticket buyers or sales numbers?**
No. That is behind the organizer's login and this Actor does not log in.

**Why are some `organizer_name` and `price_min` values null?**
On 2026-10-04 none were (120 of 120). A null comes from an event read from its event page — one the
batch records cannot answer — whose request failed after three attempts, or whose page publishes a null
organizer name. Every listing-derived field is unaffected at 100%.

**Do I get charged for rows I filter out afterwards?**
You are charged per row *returned* by the Actor. Anything you filter in your own spreadsheet afterwards
was already billed, so push filters into the input — `categories`, `keyword`, `freeOnly` and the date
window all run inside Eventbrite's search.

**Two runs of the same city — will I get duplicates?**
Within a run, no (0 of 120 on a contiguous walk; 12–13 of 180 dropped across categories). Across runs,
dedupe on `id`, or move `startDate` forward so you only pull what is new.

**Does it need a proxy?**
Yes, and the default is right: Apify datacenter IPs were usable on 4 of 20 requests, residential on 18
of 20, measured 2026-08-12. You can override it in the input if your own testing disagrees.

**Can I run it on a schedule?**
Yes — add an Apify Schedule. A daily 20-row pull costs $0.09. Pair it with `startDate` = tomorrow to
keep each run mostly new events.

**Does it work outside the US?**
Yes. `united-kingdom--london` was verified returning a full page of rows; any Eventbrite place slug
works. Prices come back in the event's own `price_currency`.

### Legal & fair use

This Actor reads **publicly available** Eventbrite pages — the same HTML and JSON any visitor's
browser receives. It does not log in, does not solve CAPTCHAs, does not circumvent any anti-bot challenge (a
challenged request is simply retried twice and then reported as failed), and collects nothing behind
authentication.

Some fields — `organizer_name`, `organizer_url` — are **personal data** where the organizer is an
individual. You are responsible for complying with Eventbrite's Terms of Service and with how you use
the data, including GDPR, CCPA, CAN-SPAM and PECR for any outreach.

Eventbrite® is a trademark of its owner. This Actor is an independent tool and is not affiliated with,
endorsed by, or sponsored by Eventbrite.

### Feedback

Found a missing field or want a new filter? Open an issue on the **Issues** tab.

# Actor input Schema

## `startUrls` (type: `array`):

Supported shapes: a search page 'https://www.eventbrite.com/d/tx--austin/all-events/' (any query string is kept and paged through, up to Max listing pages), a browse page 'https://www.eventbrite.com/b/tx--austin/music/' (rewritten to the /d/ search path, which is the one that actually carries data), and a single event page 'https://www.eventbrite.com/e/<slug>-tickets-<id>' (one row, built from that page alone — no lat/lon and no Eventbrite category tags, those only exist on search results). Organizer /o/ pages are rejected with an error rather than silently returning nothing.

## `location` (type: `string`):

A city as 'Austin, TX' or an Eventbrite place slug. Slugs are the safest input and are not US-only: 'tx--austin', 'ny--new-york', 'united-kingdom--london', 'australia--sydney'. Verified live: /d/united-kingdom--london/all-events/ returns 20 rows/page exactly like a US city.

## `locations` (type: `array`):

Scrape several cities in one run, e.g. \['Austin, TX', 'Miami, FL', 'united-kingdom--london']. When set, this REPLACES the single City field. Each city costs its own listing pages — the run walks (cities × categories × Max listing pages) search pages before it enriches anything.

## `onlineOnly` (type: `boolean`):

Ignore the city and search Eventbrite's 'online' place instead. Measured 2026-08-12: /d/online/all-events/ returned 20 of 20 rows with is_online_event = true. Online events have no venue address and no lat/lon, so those columns come back null.

## `keyword` (type: `string`):

Free-text search, added to the URL as ?q=. Example: 'yoga' in Austin returned 20 yoga-titled events on page 1. Combine with a category to search inside it (music + 'jazz').

## `categories` (type: `array`):

Eventbrite's own 21 categories. Pick none for ALL events. Each category you tick is a SEPARATE search — 3 categories × 3 pages = 9 listing fetches, so tick the ones you will actually use. All 21 slugs were verified live on 2026-08-12 (19-20 rows each on /d/tx--austin/<slug>/).

## `freeOnly` (type: `boolean`):

Search Eventbrite's free-events path instead of the open one. Measured 2026-08-12 on Austin: 8 of 8 sampled rows came back price_min = 0 / is_free = true. There is deliberately NO 'paid only' switch — Eventbrite's /paid--events/ path returned the same unfiltered result set (2 of 8 sampled rows were free), so a paid toggle would be a lie. Filter on is_free yourself after the run.

## `startDate` (type: `string`):

Only events starting on or after this date. Added to the URL as ?start_date=. Measured: a 2026-09-01 → 2026-09-07 window returned rows whose start_date all fell inside it, versus 2026-08-14 → 2026-11-08 unfiltered.

## `endDate` (type: `string`):

Only events starting on or before this date. Added to the URL as ?end_date=. Set both dates for a window; a run with a bad format stops with an error instead of quietly ignoring it.

## `maxResults` (type: `integer`):

Hard cap on events per run, and therefore your hard cost cap: 20 events = $0.09, 1,000 = $4.50. Duplicate cards are dropped before billing and never count against it. Raise the run timeout for anything over ~1,000.

## `maxPages` (type: `integer`):

Listing pages to walk per (city × category). Each page carries ~20 events. Eventbrite reports page_count = 49 but still serves page 50, and returns nothing on page 51 — measured 2026-08-13 on Austin, London and New York — so 50 pages (~1,000 events) is the real ceiling per search and values above it just stop early.

## `fetchDetails` (type: `boolean`):

ON (default): adds organizer_name, organizer_url, price_min, price_max, price_currency, is_free, ticket_availability and the ISO start/end datetimes — none of which exist on the listing page — from Eventbrite's own event records, one request per 20 events (an event's own page for any event the records cannot answer). OFF: one request fewer per 20 events, and you keep every other field (name, schedule, full venue address, lat/lon, categories, image). The price per event is IDENTICAL either way.

## `concurrency` (type: `integer`):

How many event-record and event-page requests to run in parallel during enrichment. 6 is what the published throughput numbers were measured at; going much higher raises the share of requests the WAF challenges.

## `proxyConfiguration` (type: `object`):

Apify RESIDENTIAL (US) with a fresh session per request, and that default is measured, not cautious: on 2026-07-30 a datacenter run was AWS-WAF challenged on ~87% of requests, while residential was usable on 16 of 16 listing fetches. You can override it here if your own testing disagrees — the Actor does not solve challenges, it just retries twice and then reports the failure.

## Actor input object example

```json
{
  "location": "Austin, TX",
  "onlineOnly": false,
  "freeOnly": false,
  "maxResults": 20,
  "maxPages": 3,
  "fetchDetails": true,
  "concurrency": 6,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `events` (type: `string`):

The dataset of Eventbrite events (one item per event).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "location": "Austin, TX",
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/eventbrite-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "location": "Austin, TX",
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/eventbrite-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "location": "Austin, TX",
  "maxResults": 20
}' |
apify call scrapersdelight/eventbrite-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/eventbrite-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/8TYwpRRVe82tqW2sF/builds/AJfnpLthyj6qJxmg3/openapi.json
