# 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
- **Stats:** 1 total users, 1 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# 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.** Both surfaces this Actor reads are JSON that Eventbrite
embeds in its own server-rendered 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` and `organizer_url` are filled on 92.5% and 95.0% of
rows, and `price_min`/`price_max` on 95.0% — measured on 120 contiguous Austin events on 2026-08-12.**
Neither the organizer's name nor the ticket price exists on an Eventbrite listing page; both come from
a second request this Actor makes for you.

### 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-08-12 it returned **20 rows in 20 seconds** (1 listing fetch + 20 detail fetches).

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 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 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 is roughly
   20× fewer requests, 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 straight from the detail page — usually `2026-08-23T16:00:00-05:00`, but Eventbrite publishes a bare `YYYY-MM-DD` for some events; 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 passed through **exactly as Eventbrite's JSON-LD publishes them**:
normally full ISO-8601 with the event's offset, but on a minority of events Eventbrite emits a bare
`YYYY-MM-DD` with no time — measured 2026-08-13, 9 of 39 non-null `end_datetime` values across five
runs, versus 1 of 39 for `start_datetime`. 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-08-12

Austin, TX · all categories · pages 1–6 walked contiguously · `fetchDetails: true` · concurrency 6 ·
120 rows in 81 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_postal_code`, `venue_country` | **100.0%** |
| `venue_latitude`, `venue_longitude` | **100.0%** |
| `organizer_id`, `image`, `tickets_url` | 100.0% |
| `summary` | 99.2% |
| **`categories`**, `formats` | 96.7% |
| **`organizer_url`**, **`price_min`**, **`price_max`**, **`price_currency`**, **`is_free`**, `ticket_availability`, `start_datetime`, `end_datetime` | **95.0%** |
| **`organizer_name`** | **92.5%** |
| `subcategories` | 91.7% |
| `keywords` (organizer's own tags) | 85.8% |

**The number that could mislead you: `organizer_name` is 92.5%, not 100%.** 6 of the 120 detail
requests failed after three attempts (that is the 95.0% ceiling every detail-derived field shares), and
on 3 further rows Eventbrite itself published `"name": null` in its JSON-LD while still giving the
organizer URL. So: 5% is transport, 2.5% is Eventbrite. `organizer_id` is 100% either way, and
`organizer_url` at 95.0% is the handle to enrich from.

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 }
```

\~20× fewer requests. 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 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 the detail page.

### 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` | Second request per event for organizer + price. Off = ~20× fewer requests, same price per row. |
| `concurrency` | integer | `6` | Parallel detail fetches. 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 a detail
fetch 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.
- **5.0% of detail requests fail and stay failed.** 6 of 120, after three attempts each, on residential
  proxies. Those rows still ship, with the listing fields at 100% and the detail fields `null`.
- **No email addresses, and none can be produced.** Eventbrite publishes no organizer email anywhere on
  these surfaces. `organizer_url` (95.0%) 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` (85.8%) 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

Two surfaces, both plain JSON that Eventbrite server-renders into its own pages — no browser, no
private API, 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. **Detail page** `/e/<slug>-tickets-<id>` carries a schema.org `application/ld+json` Event block with
   the organizer name/URL, the offer price range and ISO datetimes.

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.

### 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 detail fetch does not fail the run** — the row ships with the listing fields and `null`
  organizer/price, and the run's log prints the parse rate so you can see it.

### 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. Both surfaces are public server-rendered pages with the data embedded as JSON. 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` (95.0% fill) 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?**
95.0% is the detail-fetch ceiling (6 of 120 requests failed after three attempts), and on ~2.5% of rows
Eventbrite itself 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 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): one extra request per event 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. OFF: roughly 20x fewer requests and a much faster run, and you keep every other field (name, schedule, full venue address, lat/lon, categories, image). The price per event is IDENTICAL either way — this only trades time for the organizer and price columns.

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

How many event detail pages to fetch 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/YZMStcF3reqGfVUWF/openapi.json
