Luma Events API — Events by City, Category & Calendar avatar

Luma Events API — Events by City, Category & Calendar

Pricing

from $0.60 / 1,000 events

Go to Apify Store
Luma Events API — Events by City, Category & Calendar

Luma Events API — Events by City, Category & Calendar

Upcoming Luma (lu.ma) events as clean rows: every AI, tech or startup meetup in a city, a category or a calendar, with start time, venue, price, capacity, guest count and organiser calendar. Descriptions and ticket types on request. Host names are never included. No login, no browser.

Pricing

from $0.60 / 1,000 events

Rating

0.0

(0)

Developer

Insight Solutions

Insight Solutions

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Every upcoming Luma (lu.ma) event in a city, a category or a calendar, as clean rows: name, start and end time, timezone, venue and address, city and coordinates, price and currency, sold-out and waitlist state, spots left, guest count, and the organising calendar. Turn on details for each event's description (plain text or Markdown), ticket types and categories. Filter by keyword, date window, in person or online, and free only. No login, no browser, no key — and no host's name, handle or photo, ever.

Luma is where a large share of tech, AI and startup meetups are listed now, and it serves its event data as JSON to every visitor. This Actor reads that JSON — the same data Luma's own pages are built from — rather than the HTML around it.

At a glance

Input — this is the Store prefill; paste it and run:

{ "mode": "place", "places": ["sf"], "maxEventsPerSource": 25 }

The next 25 events in San Francisco, soonest first, plus one free row describing what was read. It is one 145 KB request.

Output — one row per event; the fields you will use most are name, startAt, venueName, address, city, isFree, price, registrationStatus, guestCount, calendarName and url (full list under Output reference). Anything that could not be read — an unknown city, a private event, a blocked request — comes back as a free diagnostic row (rowType: "diagnostic", errorType, error) instead of a charge.

Price — $1.00 per 1,000 events on the FREE tier (+ $0.001 per run, charged only once a run has returned an event). City, category, source and diagnostic rows are free, so are events your filters exclude, and a run that returns nothing bills nothing at all. Pay per event, limited permissions — works over the Apify MCP server (mcp.apify.com) and with agentic (x402) payments.

From code — client.actor("insight.solutions/luma-events-api").call(run_input={…}) with apify-client, or

POST https://api.apify.com/v2/acts/insight.solutions~luma-events-api/run-sync-get-dataset-items
.


Who it is for

  • Event marketers and community teams — every AI, crypto or climate event in the cities you cover, with dates, venues, capacity and the communities running them, refreshed on a schedule.
  • Sponsorship and partnerships — which organisations host the most events, how full they get (guestCount, isSoldOut, waitlist), and where.
  • Researchers and newsletters — a week's or a month's events in a city as one table (startAfter / startBefore), with Markdown descriptions ready to summarise.
  • Founders and job seekers — the meetups worth going to, filtered to free and in person.

What you get

An event (from the San Francisco prefill; null columns cut):

{
"ok": true,
"rowType": "event",
"input": "sf",
"source": "place:sf",
"sourceUrl": "https://api.lu.ma/event/get?event_api_id=evt-D083fftWbb5Dpqv",
"eventId": "evt-D083fftWbb5Dpqv",
"slug": "init-conf",
"url": "https://luma.com/init-conf",
"name": "init() by WorkOS",
"startAt": "2026-10-07T15:00:00Z",
"endAt": "2026-10-08T00:00:00Z",
"timezone": "America/Los_Angeles",
"locationType": "offline",
"venueName": "SFJAZZ",
"address": "SFJAZZ, 201 Franklin St, San Francisco, CA 94102, USA",
"city": "San Francisco",
"region": "California",
"country": "United States",
"countryCode": "US",
"latitude": 37.7764351,
"longitude": -122.421631,
"addressVisibility": "public",
"coverUrl": "https://images.lumacdn.com/uploads/bt/3bcd166d-cf3c-4d92-af57-2686f9de44da.gif",
"isFree": true,
"isSoldOut": false,
"isNearCapacity": false,
"requireApproval": true,
"registrationStatus": "open",
"waitlist": false,
"guestCount": 0,
"ticketCount": 0,
"hostCount": 1,
"verifiedHostCount": 0,
"calendarKind": "organization",
"calendarId": "cal-Z1tslEBMjjCh4fd",
"calendarName": "WorkOS Events",
"calendarWebsite": "https://workos.com",
"calendarAvatarUrl": "https://images.lumacdn.com/calendars/xz/a7cb4c75-9cbd-4853-8012-496c5ef3eb77.png",
"rank": 1
}

A paid, sold-out one:

{
"name": "NeoCloud Summit 2026 — Where the Next Cloud Gets Built",
"isFree": false,
"price": 995,
"currency": "USD",
"isSoldOut": true,
"spotsRemaining": 0,
"registrationStatus": "sold-out",
"calendarName": "Ignite Community"
}

An event whose organiser shows the address to registered guests only — the city is given, the street is not, and Luma's approximate point is rounded:

{
"name": "Hardware hackathon: turn a vintage toy into a voice agent",
"venueName": null,
"address": null,
"city": "San Francisco",
"latitude": 37.8,
"longitude": -122.4,
"addressVisibility": "guests-only"
}

With includeDetails (and always in events mode) the row also carries the description, ticket types and the rest:

{
"description": "WorkOS presents [**init(): the conference for builders**](https://workos.com/init). \n\nJoin the founders, engineers, and technical leaders shaping what’s next. …",
"descriptionFormat": "markdown",
"categories": ["AI"],
"ticketTypes": [{ "name": "Standard", "type": "free", "price": null, "registered": 1060, "requireApproval": true, "isHidden": false }],
"sessionCount": 0,
"faqCount": 0,
"registrationQuestionCount": 7,
"locale": "en"
}

And one free source row per city, category or calendar, saying what was read: eventsFound, eventsReturned, eventsFiltered, duplicatesSkipped, pages, capReached and hasMore.


Quick start

  1. Pick What to read: cities, categories, calendars, or single events.
  2. Fill that list — sf, https://lu.ma/ai, a calendar's link, event links. Don't know the slug? Run mode discover once: it lists every city and category Luma has, with its id and slug, free.
  3. Optionally narrow it: Keyword, Starting on or after / before, In person or online, Free events only.
  4. Turn on Include details for descriptions and ticket types, set Max events per source, and run. Rows arrive soonest first.

Input

FieldTypeDefaultWhat it does
modeselectplaceplace, category, calendar, events or discover. Each mode reads only its own list.
placesstring[]prefill ["sf"]City slugs (sf, nyc, london), links (https://lu.ma/sf), ids (discplace-…) or names as Luma shows them (San Francisco).
categoriesstring[][]Category slugs (ai, tech, climate), links, ids (cat-ai) or names (Food & Drink).
calendarsstring[][]Calendar slugs (genai-sf), links or ids (cal-…).
eventsstring[][]Event links (https://lu.ma/init-conf), slugs or ids (evt-…).
keywordstring""Whole words, any case, comma-separated alternatives (AI, LLM, agents). Matches the event name; with details on, the description too.
startAfter / startBeforestring—2026-11-01 is a calendar day in each event's own timezone; 2026-11-01T18:00:00Z is an exact instant (UTC when no zone is given). startBefore excludes the day itself, so 2026-11-01 … 2026-12-01 is November.
locationTypeselectanyoffline keeps in-person events, online the rest.
freeOnlybooleanfalseOnly events whose registration is free.
includeDetailsbooleanfalseDescription, ticket types, categories, session/FAQ/registration-question counts, refund policy: one extra request per event, same price.
descriptionFormatselecttexttext or markdown.
maxEventsPerSourceinteger100 (prefill 25)1–10,000 paid events from each city, category or calendar.
maxEventsTotalinteger10001–100,000 paid events in the whole run.
maxConcurrencyinteger3Sources read at once (1–10).
maxRunSecsinteger24030–3,600. Rows already returned are kept when it runs out.
proxyConfigurationobject{ "useApifyProxy": true }Apify datacenter proxy by default; switch to residential if you see blocked rows.

Links on lu.ma and luma.com are both accepted (lu.ma now redirects to luma.com, so every url in the output is a luma.com link). Duplicates are dropped on what they resolve to, so sf, https://lu.ma/sf and its discplace- id are one city.

A run with nothing usable to read — an empty list for the chosen mode, entries that are not Luma's, a date that is not a date, startAfter not before startBefore — finishes FAILED before a single request, with a free row saying which entry was the problem. A date typo is refused rather than ignored, so it can never turn a filtered run into an unfiltered (and billed) one.


Output reference

Every row has the same columns, null where a column does not apply, so the dataset exports as one table.

On every row: ok, rowType (event · place · category · source · diagnostic), input (your entry, as typed), error, errorType, scrapedAt, source (place:sf, category:ai, calendar:cal-…, event, discover), sourceUrl.

Events:

ColumnWhat it is
eventId, slug, urlLuma's id (evt-…), the event's path, and its page on luma.com.
nameThe name as the organiser published it.
startAt, endAt, timezoneUTC with a Z, and the event's own IANA timezone.
locationTypeAs Luma sends it: offline is in person.
venueName, addressThe venue's name (when Luma has a named place) and the full street address. Both null when the organiser shows the address to guests only.
city, region, country, countryCodeGiven even when the street address is hidden.
latitude, longitude, addressVisibilityThe point Luma shows. For a guests-only address Luma's point is approximate, so it is rounded to two decimals (about 1 km) and addressVisibility is guests-only.
coverUrlThe cover image.
isFree, price, maxPrice, currencyPrice in currency units (Luma sends cents: 99500 is 995), ISO currency code.
isSoldOut, spotsRemaining, isNearCapacity, requireApprovalCapacity and approval, as Luma shows them.
registrationStatus, waitlistopen, waitlist or sold-out; whether new registrations go to the waitlist.
guestCount, ticketCountNumbers only — no guest is ever named.
hostCount, verifiedHostCountHow many hosts, and how many Luma has verified. Never who.
calendarKindorganization or personal.
calendarId, calendarName, calendarSlug, calendarUrl, calendarWebsite, calendarAvatarUrlThe organisation calendar running the event. All null for a personal calendar — see below.
rank1 for the first event a source returned, then 2, 3… in Luma's order (start time). In events mode, the link's position in your list.
description, descriptionFormatWith details: the description as text or Markdown (headings, bold, italic, links, lists, images).
ticketTypesWith details: name, type, price, min price, currency, flexible, capacity, spots left, how many registered, approval, hidden and disabled flags, sale window, description.
categoriesWith details: the Luma categories the event is in, by name.
sessionCount, faqCount, registrationQuestionCountWith details: counts only. Registration questions and answers are never included.
refundPolicy, localeWith details.

Place and category rows (free, discover mode): placeId or categoryId, slug, name, url, eventCount, timezone, latitude, longitude and description for cities; upcomingEventCount for categories.

Source rows (free, one per source): eventsFound, eventsReturned, eventsFiltered, duplicatesSkipped, pages, capReached, hasMore, plus the city's, category's or calendar's id and name.

Diagnostic rows (free):

errorTypeMeaning
invalid-inputAn entry that is not a Luma slug, link or id, an id in the wrong list, a page of the wrong kind (a category in places), or a date filter that is not a date.
not-foundLuma has no such city, calendar or event, or refuses it (private, removed).
blockedA 403, 429 or bot-protection page, twice, from two exit IPs.
timeoutA request that did not answer in time, or the run's time budget running out. Rows already returned are kept.
parse-failedA page or answer without the data this Actor reads — Luma may have changed its format.
robots-disallowedA luma.com path that luma.com/robots.txt disallows. Not requested.
httpAny other upstream error, such as Luma refusing a continuation cursor.
internalThis Actor's own error. The run is then FAILED.

How Luma is read

WhatRequestNotes
A cityapi.lu.ma/discover/get-paginated-events?discover_place_api_id=…, 25 per pagePaged with Luma's own opaque cursor, passed back exactly as sent. The id of the 87 cities Luma lists is built in, so a city slug costs no extra request.
A calendarapi.lu.ma/calendar/get-items?calendar_api_id=…&period=future, 20 per pageA slug is first resolved from the calendar's page.
A categoryluma.com/<category>A category page lists calendars, not events. The Actor reads the upcoming events of the calendars the page features — page 1 of each, merged by start time, then a further page of one calendar only when its events are next.
One eventapi.lu.ma/event/get?event_api_id=… or api.lu.ma/url?url=<slug>For a link or slug, the event's page is the fallback when the API cannot be read.
Detailsapi.lu.ma/event/get per eventOnly with includeDetails.
Discoverluma.com/discoverThe list of cities and categories.

Each source gets one request in flight, 250–600 ms apart, through its own proxy session, however many sources run at once (in events mode, the links one worker reads share its session and its pacing). A city or calendar walk stops as soon as Luma's events pass your startBefore, since Luma lists them by start time. luma.com/robots.txt is read once per run, before the first page, and every rule in it is honoured for this Actor too — including the ones it addresses only to one search engine's crawler (/in/, /company/, /session-*, /social-share). A run that reads no page (a city in the catalog, a calendar or event by id) never fetches it.

Host names are never included. Luma's event data lists the people hosting each event — names, usernames, photos, bios, social handles — and the guests it features. This Actor never copies any of them: you get hostCount and verifiedHostCount, and nothing else about a person. A calendar is identified only when Luma marks it as an organisation's; a person's own calendar comes back as calendarKind: "personal" with its name, link and picture left out, and a calendar source of that kind is labelled calendar:personal. Payment accounts, coupon codes, tracking pixels and crypto-wallet requirements are never read into a row, and registration questions are only counted. Event names, descriptions and venue addresses are the organiser's published copy and are returned as Luma serves them.


What you are never charged for

  • Every place, category and source row.
  • Every diagnostic row: unknown cities, private or removed events, blocked requests, timeouts, pages Luma changed, paths robots.txt disallows.
  • Events your filters exclude — keyword, dates, in person or online, free only.
  • An event reached a second time in a run, from another source or another page (it is a duplicate, counted on the source row).
  • The detail request behind includeDetails: same price per event.
  • A run that returns nothing at all: it finishes SUCCEEDED with zero results and a "0 results … Nothing was charged." status message naming the diagnostic rows by type, and bills nothing, start fee included.

Pricing

EventWhat it isFREEBRONZESILVERGOLD
actor-startCharged once per run, only after the run has returned an event$0.001$0.001$0.001$0.001
eventOne event row, with or without details$0.001$0.001$0.0008$0.0006

$1.00 per 1,000 events.

RunCost
The default input — 25 events in San Francisco$0.026
Every upcoming event in San Francisco (74 on 2026-10-07)$0.075
1,000 events across ten cities$1.001
The same 1,000 at GOLD$0.601
Discover mode — every city and category with its id$0.00

Charging is charge-after-push: rows are in your dataset before the event is recorded. ACTOR_MAX_TOTAL_CHARGE_USD is respected — the run stops fetching when it is reached and finishes SUCCEEDED saying so.


Proxy

The default is { "useApifyProxy": true } — Apify's datacenter pool. The capture probe read every endpoint above through it. A 403 or 429 is treated as a block: that source's proxy session is rotated once, and a second refusal becomes a free blocked row rather than a retry loop. A JSON 403 from Luma itself (a private event) is reported as not-found straight away — asking again from another address would not change the answer. If blocked rows become common, switch proxyConfiguration to residential.


Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with limited permissions, uses pay-per-event pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The Integrations tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

curl -X POST "https://api.apify.com/v2/acts/insight.solutions~luma-events-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode":"place","places":["sf","nyc"],"keyword":"AI","startAfter":"2026-11-01","startBefore":"2026-12-01","maxEventsPerSource":100}'
# pip install apify-client
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/luma-events-api").call(run_input={
"mode": "category",
"categories": ["ai", "climate"],
"freeOnly": True,
"includeDetails": True,
"descriptionFormat": "markdown",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["rowType"] == "event":
print(row["startAt"], row["city"], row["name"], row["calendarName"], row["url"], sep=" | ")
elif row["rowType"] == "diagnostic":
print("could not read", row["input"], row["errorType"], row["error"])

Building an app with Lovable? Apify's Lovable connector runs any Actor from inside a Lovable project with your Apify token. Connect Apify in Lovable once, then ask it: "Use Apify to run insight.solutions/luma-events-api for my city and build an events calendar around the results." At the default settings a run finishes well inside Lovable's 300-second synchronous limit, and every result is one JSON object per row, so the generated app needs no extra parsing. Usage bills to the Apify account whose token is connected.

FAQ

Do I need a Luma account or an API key? No. Only what Luma shows any visitor is read.

How do I find a city's or category's slug? Run mode: "discover" once. It returns every city (87 when this was written) and category (12) with its id, slug and event count, as free rows.

Is a category every event Luma tags with it? No — and this is worth knowing. A Luma category page lists calendars (communities and conference calendars), not events, so category mode returns the upcoming events of the calendars the category page features, merged by start time. For "every AI event in a city", use place mode with keyword: "AI", and turn on includeDetails to search descriptions too.

Why is address empty for some events? The organiser chose to show it to registered guests only. The city, region and country are still given, and an approximate point rounded to about a kilometre.

Why are host names missing? By design: hosts are people, and only their count is returned. Organisation calendars are identified by name; a person's own calendar is not.

Does startAfter use UTC? A bare date is a day in each event's own timezone, so 2026-11-01 keeps a 7 pm talk in San Francisco on 1 November even though it is 2 November in UTC. A full timestamp is exact.

How many events can I get? Up to 10,000 per source and 100,000 per run. A source stops after reading 5,000 events, so a narrow filter on a very large calendar may not reach its end; its source row then says Luma had more.


Limitations

  • Category mode reads the calendars a category page features, not a full category index (see the FAQ). Luma offers no category event list to visitors; the endpoint that looks like one answers 404.
  • Upcoming events only in place, category and calendar modes: the lists Luma serves start from now.
  • includeDetails costs one request per event, paced at 250–600 ms within a source: one to two events a second per source. Several sources run at once (maxConcurrency); raise maxRunSecs for large detailed runs — the default 240 seconds covers a few hundred detailed events per source.
  • Host names are never included, by design (see How Luma is read).
  • Prices are read as Luma's cents divided by 100. Currencies without minor units have not been seen in a capture yet.
  • The upstream format may change: this Actor reads Luma's JSON as it is served today, leaves a column null when an event does not carry it, and returns a free parse-failed row rather than a wrong one when a page no longer has the data it expects.
  • Not affiliated with Luma. "Luma" is used only to say whose public event pages are read.

Related: to keep only the new events of a calendar between runs, put this Actor on a schedule and compare eventIds; to read an event's own website, use Website to Markdown — Content Extractor for LLMs & RAG.

Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

Video, audio & social

News, documents & the web

Business, finance & jobs

Apps & games