# Eventbrite Events Scraper (`scrapyx/eventbrite-events-scraper`) Actor

Scrapes events from Eventbrite by location, with optional keyword, date-range and language filters. Returns name, description, tags, venue with coordinates, times with timezone and ticket URL per event, plus an optional detail pass adding ticket prices, organizer and performers.

- **URL**: https://apify.com/scrapyx/eventbrite-events-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Travel
- **Stats:** 4 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.26 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Events Scraper

Scrapes events from [Eventbrite](https://www.eventbrite.com) by location, with
optional keyword, date-range and language filters. HTTP-only (no browser), and
every filter it offers was verified to actually work upstream.

### What you get

One row per event, plus a `SEARCH_SUMMARY` row per query and an `ERROR` row for
any query that fails — so **every input maps to at least one row**.

Each event row carries Eventbrite's own search object verbatim under `listing`
(36 fields), with the most-used ones lifted to the top level:

| Field | Example |
| --- | --- |
| `eventId` | `1984167730093` |
| `eventName` | `Black & Bronze: A Bronzeville Music Fest` |
| `eventUrl` | `https://www.eventbrite.com/e/...-tickets-1984167730093` |
| `startDate` / `startTime` | `2026-08-29` / `13:00` (local, with `listing.timezone`) |
| `isOnlineEvent` | `false` |

Inside `listing` you also get: `full_description`, `summary`, `tags` (including
Eventbrite's own category/subcategory labels such as `Music` / `R&B`),
`primary_venue` (name, full address, **latitude/longitude**), `end_date`,
`is_cancelled`, `series_id`, `tickets_url`, `language`, `image` (multiple sizes).

#### Optional detail pass

`includeEventDetails: true` fetches each event's page and attaches its JSON-LD
under `eventDetail`, adding the three things search rows genuinely lack:

- **`offers`** — ticket price, currency, availability
- **`organizer`** — organizer name and URL (search only gives a numeric id)
- **`performer`**

Costs one extra request per event, so it is off by default.

### Input

```json
{
  "locations": ["il--chicago", "united-kingdom--london"],
  "keywords": ["jazz"],
  "startDate": "2026-09-01",
  "endDate": "2026-09-30",
  "includeEventDetails": false,
  "maxItems": 100
}
```

`locations` takes Eventbrite location slugs — the part of a search URL after
`/d/`. Both grammars work (`united-states--new-york`, `il--chicago`), as does
`online` for online-only events. Non-US markets are fully supported (London,
Sydney, Toronto, Berlin, Jakarta all verified live).

The run is the **cross product** of `locations` × `keywords`, each pair getting
its own summary row. Leave `keywords` empty to fetch everything in a location.

### Known limits — read these before scaling a crawl

**~1,000 events per query, whatever the total says.** Eventbrite stops serving
rows after page 50 (20/page). This was bisected live: page 50 returned 19 rows,
page 51 returned zero, on a query whose own total claimed 5,524. When a query
stops for this reason the summary row sets `reachableCeilingHit: true`. To get
more, split the query — more locations, more keywords, or narrower date ranges.

**`totalResults` is an upper bound, not a row count.** It is capped at 10,000
for large markets, and when a keyword is used it reports a fixed pool size
rather than a match count (a real and a nonsense keyword both reported 6,930
while returning almost entirely different events).

**Keyword search is relevance-based, not exact-match.** A keyword with no strong
matches returns loosely-related events rather than nothing. That is Eventbrite's
own search behaviour, documented here rather than papered over.

**Location may resolve to a narrower place.** `indonesia--jakarta` is served as
`indonesia--jakarta-pusat` (one district). The summary row reports
`locationExactMatch: false` along with `locationResolvedSlug` and
`locationResolvedName` so the label always matches the data.

**Category filtering is not available.** Eventbrite's category browse pages
(`/b/{location}/{category}/`) serve only an 8-event teaser with no working
pagination, and the underlying category tag is inert on the deep-paginating
search surface (verified). Use `keywords` instead — and note every row already
carries its categories under `listing.tags` for client-side filtering.

**Filters deliberately not offered**, because each was measured to be silently
ignored (they return unfiltered results under a filtered label): `price`,
`cats`, `dates`, `online_events_only`, `page_size`, `tags`.

**Date range needs both bounds.** `startDate` without `endDate` is silently
ignored upstream, so the actor refuses that input instead of returning
unfiltered events.

### Anti-bot / transport

No WAF, no challenge page, no JS gate — 8 TLS profiles across both the search
and detail surfaces returned clean 200s, cold, without warmup.

What Eventbrite **does** enforce is a **per-IP request-velocity limit**:
sustained bursts earn a plain `HTTP 429` that persists for ~3–4 minutes and does
not clear on TLS-profile rotation. The actor treats 429 as retryable, backs off
progressively, and rebuilds its session (drawing a fresh proxy exit IP) on each
retry — which is what actually clears it.

Because of this, **`minRequestInterval` is the honest speed control here**, not
`maxConcurrency`: once the limit binds, extra parallelism buys nothing. Defaults
are deliberately gentle (interval 2s, concurrency 3). Residential proxy is the
default and is intentionally **not** country-pinned, since a rotating pool is
precisely the remedy for an IP-scoped limit.

### robots.txt

The `/d/` search surface and `/e/` event pages this actor uses are not
disallowed. Eventbrite's `robots.txt` disallows `/directory/`,
`/api/v3/destination/events/`, `/land-browse-bff` and a set of query-string
patterns (`*?i*`, `*&i*`, `*?x*`, `*&x*`, `*?calendar*`, `*&id*`, …); every
parameter this actor emits (`page`, `q`, `start_date`, `end_date`, `lang`) was
checked against that list. The internal `/api/v3/destination/search/` JSON
endpoint was deliberately **not** used — its sibling paths are explicitly
disallowed, and the SSR page carries the same payload anyway.

# Actor input Schema

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

One or more Eventbrite location slugs, exactly as they appear in an Eventbrite search URL after /d/ -- e.g. 'united-states--new-york', 'il--chicago', 'united-kingdom--london', 'australia--sydney', 'germany--berlin', or 'online' for online-only events. Both slug forms work (country--region and region--city). An unrecognised slug returns a clean 404 and is reported as an ERROR row, never silently widened to another place. Note Eventbrite sometimes resolves a slug to a narrower place than requested (e.g. 'indonesia--jakarta' becomes 'indonesia--jakarta-pusat'); when that happens the SEARCH\_SUMMARY row reports locationExactMatch=false with the resolved slug.

## `keywords` (type: `array`):

Optional free-text keyword search, e.g. 'jazz', 'startup', 'yoga'. The run is the cross product of locations x keywords, each pair producing its own SEARCH\_SUMMARY row. Leave empty to fetch all events for each location. Note: this is a relevance search, so a keyword with no strong matches still returns loosely-related events rather than nothing, and the reported total is a fixed pool size rather than a match count when a keyword is used.

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

Optional lower bound on event start date. MUST be set together with End date -- Eventbrite silently ignores one bound without the other (verified: the result count does not move), so this actor refuses the half-set case instead of returning unfiltered events under a date-filtered label.

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

Optional upper bound on event start date. Must be set together with Start date. Applied as a real filter (verified: a 3-day window narrowed a 5,524-event city to 919).

## `language` (type: `string`):

Optional ISO language code (e.g. 'en', 'es', 'de') restricting results to events published in that language. Verified to genuinely filter; the SEARCH\_SUMMARY row reports languageApplied from Eventbrite's own filter echo.

## `includeEventDetails` (type: `boolean`):

Also fetch each event's page for its JSON-LD, adding ticket offers (price, currency, availability), the named organizer and performers -- the three things genuinely absent from search rows. Search rows already carry name, full description, summary, tags, venue with coordinates, start/end times with timezone and the ticket URL. Off by default: one extra request per event.

## `maxItems` (type: `integer`):

Stop paginating a (location, keyword) query after this many events. Set to 0 for unlimited, which is still bounded by Eventbrite's own ceiling of roughly 1,000 events per query.

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

Hard cap on pagination depth (20 events/page). Eventbrite stops serving rows after page 50 regardless of how large the reported total is, so values above 50 are clamped to 50. When that ceiling is what stopped a query, the SEARCH\_SUMMARY row says so via reachableCeilingHit=true.

## `maxConcurrency` (type: `integer`):

Upper bound on requests in flight at once, across queries and detail fetches. Kept low by default: Eventbrite enforces a per-IP request-velocity limit (HTTP 429) that stays active for several minutes, so throughput here is governed by pacing rather than parallelism.

## `minRequestInterval` (type: `integer`):

Paces request starts without holding a concurrency slot. This is the honest speed control for this target: Eventbrite answers sustained bursts with HTTP 429 and a multi-minute cooldown, so lowering this buys nothing once the limit binds. Raise it if you see 429 warnings in the log.

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

No WAF or bot challenge was found for Eventbrite (8 TLS profiles across both surfaces, all clean). Residential proxy is still the default because this target rate-limits per IP: each retry draws a fresh exit IP, which is what actually clears a 429. Deliberately not pinned to a country, since the site is global and a pin would shrink the rotation pool.

## Actor input object example

```json
{
  "locations": [
    "il--chicago"
  ],
  "keywords": [],
  "startDate": "",
  "endDate": "",
  "language": "",
  "includeEventDetails": false,
  "maxItems": 100,
  "maxPages": 25,
  "maxConcurrency": 3,
  "minRequestInterval": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "locations": [
        "il--chicago"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/eventbrite-events-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 = { "locations": ["il--chicago"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/eventbrite-events-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 '{
  "locations": [
    "il--chicago"
  ]
}' |
apify call scrapyx/eventbrite-events-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/eventbrite-events-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/XIqWzB5jaHD1o1GHS/builds/a1CCgQQlJwkJ2t5Vo/openapi.json
