# Eventbrite Scraper - Event Data: City, Date, Venue, Organizer (`eiv/eventbrite-scraper`) Actor

Eventbrite scraper for market research, event data. Search events by city, date, category, format, online or in-person. No login, no API key, no ticket prices - the feed has none. True UTC starts from local time + IANA timezone, venue coordinates, organizer IDs, publish date, coverage row per city.

- **URL**: https://apify.com/eiv/eventbrite-scraper.md
- **Developed by:** [Eimantas V](https://apify.com/eiv) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.90 / 1,000 event scrapes

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 Scraper — events by city and date

Eventbrite's own API shows you the events you own. This reads the ones you
don't. Give it `country--city` slugs, a search term and a date window; the
public discovery feed comes back as flat rows. No browser, no login, no API
key.

### What you get per event

| Field | Notes |
|---|---|
| `startsAtUtc` | The real instant, composed from the event's local time **and** its IANA zone. Not the local clock string. |
| `startDate` / `startTime` / `endDate` / `endTime` / `timezone` | The local values exactly as Eventbrite reports them |
| `venue` | `name`, `city`, `region`, `country`, `postalCode`, `address`, and `latitude` / `longitude` as **numbers** |
| `category`, `subcategory`, `format` | Eventbrite's own taxonomy — `Music` / `Hip Hop / Rap` / `Party or Social Gathering`. Measured over 200 events: category and format 98%, subcategory 82.5% |
| `organizerTags` | Free-text tags the organizer chose. Present on 90.5% |
| `organizerId` | Stable key for grouping an organizer's whole programme |
| `publishedAt` | When the listing went up, not when the event happens |
| `eventId`, `name`, `url`, `summary`, `ticketsUrl`, `imageUrl`, `isOnline`, `language` | `summary` and `publishedAt` are 100% |

Plus one **coverage summary row per location**: events returned and examined,
pages read against pages available, duplicates and filtered rows dropped, and
whether the corpus ran out or the page budget stopped it.

### Why `startsAtUtc` matters

Eventbrite gives you `"20:00"` and `"America/New_York"` separately. Treating
that time as UTC puts the event four or five hours out depending on the season,
and every window filter, sort and join downstream inherits the error. This
composes the instant properly, DST included:

```
2026-09-04 20:00 America/New_York  ->  2026-09-05T00:00:00Z   (EDT, UTC-4)
2026-01-15 20:00 America/New_York  ->  2026-01-16T01:00:00Z   (EST, UTC-5)
2026-09-04 20:00 Asia/Kolkata      ->  2026-09-04T14:30:00Z   (UTC+5:30)
```

If the date, the time or the zone is missing, the field is `null` rather than a
guessed instant.

### What this does not do

**There is no price data.** The discovery feed carries no ticket availability
at all — absent on all 200 events sampled — so ticket price, currency and
sold-out status are simply not knowable from here. This Actor does not emit
those fields as permanent nulls to look more complete than it is.

**No organizer name, and no contact details.** The row carries `organizerId`
and `organizerTags`, nothing else about the organizer. `organizerId` tells you
these eleven events are one operator, not who that operator is — group a city
by it and a programme falls out: cadence, categories, venues. One event is
noise; a twelve-event programme is an account. But there is no email, no phone
and no name here.

**No `seriesId` or `isCancelled` either.** Eventbrite defines both, and reports
both as `null` on all 200 events sampled, so they are not emitted. A recurring
series or a cancellation only shows on the event's own page, which this Actor
deliberately does not fetch — one request per twenty events is what makes it
cheap.

**Online events have no venue.** `venue` is `null`, not an object full of blank
strings. Measured over 200: absent on all 60 online events, present on all 140
in-person ones, and every one of those 140 carries real coordinates.

**A location it does not recognise is reported as such.** Eventbrite answers an
unrecognised discovery path with HTTP 200 and a perfectly normal-looking page
holding zero events, which is indistinguishable from a real place with nothing
scheduled. The summary row says which, in `zeroEventsReason`:
`location-not-recognised` versus `none-scheduled`. That is the difference
between a typo and an answer.

Everywhere else, `null` means Eventbrite did not report it — never zero, never
empty. A venue with a missing coordinate gets `null`, not latitude `0`, which
is a real place in the Gulf of Guinea that a bounding-box filter would match.

### Input

```json
{
  "locations": ["united-states--new-york", "united-kingdom--london", "online"],
  "query": "tech conference",
  "maxEventsPerLocation": 200,
  "startsAfter": "2026-09-01",
  "eventType": "any"
}
```

`locations` takes `country--city` slugs, the word `online`, or full
`eventbrite.com/d/...` URLs. A bare city name is **rejected rather than guessed
at**, for the reason above.

`maxEventsPerLocation` bounds the **output**; `maxPagesPerLocation` (default 25)
bounds the **work**. A narrow date filter would otherwise page to the end of a
49-page result set. Pages fetched but filtered away are never charged.

### Rate limiting

Measured against the live site from one IP:

```
40 requests/minute        12/12 clean (also clean at 20 and 30)
~60 requests in a burst   429
429 lockout               cleared between t+210s and t+240s
```

`requestDelayMs` defaults to 1500 — the measured-safe rate, not an optimistic
one. On a 429 the Actor waits the measured backoff once and retries; retrying
immediately only extends the lockout. Throughput scales with IPs, so use the
proxy input to run several locations quickly. Apify bills datacenter proxy per
IP per month, not per gigabyte, so it costs nothing per row.

### Pricing

From **$1.90 per 1,000 events** — $0.0040 each on the Free plan, falling to
$0.0019 on Business. Locations Eventbrite does not recognise are never charged
— nor are duplicates, or rows your filters removed. A city that resolved and
simply has nothing on **is** charged for its coverage row, because that is a
real answer that cost real requests. Full workings in
[docs/PRICING.md](docs/PRICING.md).

### Notes on the implementation

The events live in a `window.__SERVER_DATA__` island of roughly 177 KB, and it
contains `};` inside string values — so the obvious `/=\s*(\{.*?\});/` match
truncates into invalid JSON. Extraction is depth-counted with string and escape
awareness, which is the only thing that terminates in the right place. There is
a test for it.

Coordinates arrive as strings (`"40.7018895"`). A string where the dataset
schema declares a number is rejected at push time, after the fetch has already
been paid for, so they are coerced on the way in.

# Actor input Schema

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

`country--city` slugs like `united-states--new-york`, the word `online`, or full `eventbrite.com/d/...` URLs. A bare city name is rejected rather than guessed at: Eventbrite answers an unrecognised path with HTTP 200 and a normal-looking page holding zero events, so a wrong guess would silently return nothing.

## `query` (type: `string`):

Free text, applied to every location. Leave empty for all events.

## `maxEventsPerLocation` (type: `integer`):

Counts events **kept** after filtering. Eventbrite serves 20 per page.

## `maxPagesPerLocation` (type: `integer`):

Bounds the **work**, where the cap above bounds the **output**. A narrow date filter would otherwise page to the end of a 49-page result set. Pages fetched but filtered away are never charged.

## `startsAfter` (type: `string`):

`YYYY-MM-DD`. Compared against the event's true UTC start, composed from its local time and IANA zone — not against the local clock time.

## `startsBefore` (type: `string`):

`YYYY-MM-DD`. Same UTC basis as above.

## `eventType` (type: `string`):

Online events carry no venue at all — that is reported as `venue: null`, not as a venue with blank fields.

## `requestDelayMs` (type: `integer`):

Measured against the live site: 40 requests/minute ran clean, and a burst of about sixty tripped a 429 that then held for four minutes. The default is the measured-safe rate. Lower it only behind a rotating proxy.

## `proxyConfig` (type: `object`):

Optional. Eventbrite rate-limits per IP, so a datacenter proxy group is what lets several locations run quickly. Apify bills datacenter proxy per IP per month, not per gigabyte, so this costs nothing per row.

## Actor input object example

```json
{
  "locations": [
    "united-states--new-york",
    "united-kingdom--london",
    "online"
  ],
  "query": "tech conference",
  "maxEventsPerLocation": 200,
  "maxPagesPerLocation": 25,
  "startsAfter": "2026-09-01",
  "startsBefore": "2026-12-31",
  "eventType": "any",
  "requestDelayMs": 1500,
  "proxyConfig": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per event, plus one coverage summary row per location saying how many pages were read and whether the corpus ran out or the budget stopped it.

# 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": [
        "united-states--new-york",
        "united-kingdom--london",
        "online"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("eiv/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 = { "locations": [
        "united-states--new-york",
        "united-kingdom--london",
        "online",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("eiv/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 '{
  "locations": [
    "united-states--new-york",
    "united-kingdom--london",
    "online"
  ]
}' |
apify call eiv/eventbrite-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eiv/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/2hne8jJicCRRHmGIo/builds/SN9baygLhYtPzZrRU/openapi.json
