Reference
Catalogue
The agenda: public data, the same for every token. Needs read.
GET/events
List events
Every approved event in the agenda for NL and BE, soonest first. Defaults to today onwards. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
city optional | query | string | - | Only events in this city. Matched case-insensitively against the venue's city. |
genre optional | query | string | - | Only events carrying this genre, exactly as /v1/genres spells it. |
from optional | query | string (date) | - | First date, yyyy-mm-dd. Defaults to today in Europe/Amsterdam. |
to optional | query | string (date) | - | Last date, yyyy-mm-dd, inclusive. |
venue_id optional | query | string | - | Only events in this venue. |
artist_id optional | query | string | - | Only events this artist is billed on. |
organisation_id optional | query | string | - | Only events from this organisation. |
q optional | query | string | - | Free text over the title and the venue name. |
has_tickets optional | query | boolean | - | True: only events you can buy a ticket for right now. False: only events you cannot. |
max_price optional | query | number>= 0 | - | Only events whose cheapest ticket is at most this, in euros. 0 means free entry only. |
min_price optional | query | number>= 0 | - | Only events whose cheapest ticket is at least this, in euros. Free entry and an unknown price never pass a floor above 0. |
lat optional | query | number-90..90 | - | Latitude of the centre for a radius search, with lng. Only venues with coordinates can match. |
lng optional | query | number-180..180 | - | Longitude of the centre for a radius search, with lat. |
radius_km optional | query | integer1..200 | - | How far from lat,lng a venue may be, in kilometres. Needs lat and lng. Defaults to 25. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/events?city=Amsterdam&limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/events?city=Amsterdam&limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/events?city=Amsterdam&limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "oost-sat",
"slug": "oost-hard-techno-night",
"title": "OOST: Hard Techno Night",
"date": "2026-10-03",
"end_date": null,
"start": "23:00",
"end": "06:00",
"timezone": "Europe/Amsterdam",
"status": "approved",
"cancelled": false,
"cancel_reason": null,
"postponed_to": null,
"genres": [
"Hard techno",
"Techno"
],
"price": 18,
"price_max": 25,
"currency": "EUR",
"age": 18,
"ticket_url": null,
"artwork_url": "https://partyppl.nl/media/imports/oost/oost-sat.jpg",
"venue": {
"id": "oost",
"name": "OOST",
"city": "Amsterdam",
"address": "Zeeburgerpad 1, 1018 AJ Amsterdam",
"lat": 52.3701,
"lng": 4.9412
},
"organisation": {
"id": "oost-collective",
"name": "OOST Collective"
},
"url": "https://partyppl.nl/e/oost-hard-techno-night",
"updated_at": 1758499200
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List events400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/events/{id}
Get one event
One event by its id or by its slug, with the bill, the set times when there are any and the festival maps. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
Example request
curl -s "https://api.partyppl.nl/v1/events/oost-sat" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/events/oost-sat", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/events/oost-sat",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": {
"id": "oost-sat",
"slug": "oost-hard-techno-night",
"title": "OOST: Hard Techno Night",
"date": "2026-10-03",
"end_date": null,
"start": "23:00",
"end": "06:00",
"timezone": "Europe/Amsterdam",
"status": "approved",
"cancelled": false,
"cancel_reason": null,
"postponed_to": null,
"genres": [
"Hard techno",
"Techno"
],
"price": 18,
"price_max": 25,
"currency": "EUR",
"age": 18,
"ticket_url": null,
"artwork_url": "https://partyppl.nl/media/imports/oost/oost-sat.jpg",
"venue": {
"id": "oost",
"name": "OOST",
"city": "Amsterdam",
"address": "Zeeburgerpad 1, 1018 AJ Amsterdam",
"lat": 52.3701,
"lng": 4.9412
},
"organisation": {
"id": "oost-collective",
"name": "OOST Collective"
},
"url": "https://partyppl.nl/e/oost-hard-techno-night",
"updated_at": 1758499200,
"description": "Twee vloeren, hard techno tot zes uur.",
"lineup": [
{
"id": "amelie-lens",
"name": "Amelie Lens",
"order": 0
},
{
"id": "vril",
"name": "Vril",
"order": 1
}
],
"timetable": {
"Main": [
[
"23:00",
"Vril"
],
[
"01:00",
"Amelie Lens"
]
]
},
"maps": [
{
"label": "Plattegrond",
"image_url": "https://partyppl.nl/media/imports/oost/plattegrond.png",
"source": null
}
],
"rules": [
"18+, legitimatie verplicht",
"Geen fotos op de dansvloer"
],
"dress": null,
"source": "partyflock",
"source_url": "https://partyflock.nl/party/498065"
},
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200Get one event400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/events/{id}/token-prices
What a token costs at this festival
Dutch and Belgian festivals sell drinks in munten, and what that costs is worked out here from what visitors reported: euros per token at the till and in advance, tokens per drink and therefore the price of a beer, whether unspent tokens are refunded, what the previous edition cost, and how it sits against the rest of the season. Nothing in the answer was checked by us. verified is always false and reports and last_report_at are always there, so anything you build with it can say who said so and when. 404 for an event that is not a festival. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
Example request
curl -s "https://api.partyppl.nl/v1/events/oost-sat/token-prices" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/events/oost-sat/token-prices", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/events/oost-sat/token-prices",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": {
"event_id": "awakenings-2027",
"festival_key": "awakenings",
"season": 2027,
"verified": false,
"reports": 11,
"last_report_at": 1782000000,
"confidence": "good",
"token_price": 3.6,
"token_price_channel": "onsite",
"presale": 3.4,
"onsite": 3.6,
"saving": 0.2,
"drink_tokens": 2,
"drink_price": 7.2,
"refund": "no",
"outliers": 1,
"example": {
"paid": 18,
"tokens": 5
},
"previous_edition": {
"event_id": "awakenings-2026",
"title": "Awakenings Festival 2026",
"date": "2026-07-04",
"season": 2026,
"token_price": 3.4,
"drink_price": 6.8,
"reports": 8
},
"season_compare": {
"season": 2027,
"median": 3.4,
"festivals": 14,
"diff": 0.2
},
"url": "https://partyppl.nl/e/awakenings-festival-2027#munten"
},
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200What a token costs at this festival400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/events/{id}/token-reports
The token reports behind that price
The individual reports the price above is built from, newest first, so the working can be checked. Anonymous by design: each report carries what was paid and how close the reporter stood to the night (tier, which is what their weight is built from), and never who they are. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/events/oost-sat/token-reports?limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/events/oost-sat/token-reports?limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/events/oost-sat/token-reports?limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "tp_8cQ2fR",
"paid": 18,
"tokens": 5,
"token_price": 3.6,
"drink_tokens": 2,
"channel": "onsite",
"refund": "no",
"tier": "ticket",
"weight": 5,
"created_at": 1782000000
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200The token reports behind that price400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/venues
List venues
Clubs, concert halls and festival sites, by name. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
city optional | query | string | - | Only venues in this city. |
q optional | query | string | - | Free text over the name. |
with_events optional | query | boolean | - | True: only venues with at least one event from today on. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/venues?city=Rotterdam&limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/venues?city=Rotterdam&limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/venues?city=Rotterdam&limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "oost",
"name": "OOST",
"city": "Amsterdam",
"area": "Oost",
"address": "Zeeburgerpad 1, 1018 AJ Amsterdam",
"lat": 52.3701,
"lng": 4.9412,
"website": "https://clubooost.nl",
"tags": [
"club",
"techno"
],
"rating": 4.4,
"reviews_count": 128,
"image_url": "https://partyppl.nl/media/venues/oost.jpg",
"url": "https://partyppl.nl/locatie/oost"
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List venues400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/venues/{id}
Get one venue
One venue with its practical profile: capacity, lockers, payment, smoking, accessibility and the rest of what a visitor asks before they go. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
Example request
curl -s "https://api.partyppl.nl/v1/venues/oost" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/venues/oost", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/venues/oost",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": {
"id": "oost",
"name": "OOST",
"city": "Amsterdam",
"area": "Oost",
"address": "Zeeburgerpad 1, 1018 AJ Amsterdam",
"lat": 52.3701,
"lng": 4.9412,
"website": "https://clubooost.nl",
"tags": [
"club",
"techno"
],
"rating": 4.4,
"reviews_count": 128,
"image_url": "https://partyppl.nl/media/venues/oost.jpg",
"url": "https://partyppl.nl/locatie/oost",
"info": "Ingang aan de waterkant, garderobe verplicht.",
"accessibility": "Rolstoeltoegankelijk, aangepast toilet aanwezig.",
"rules": [
"18+, legitimatie verplicht"
],
"rules_url": "https://clubooost.nl/huisregels",
"facts": {
"capacity": 1200,
"rooms": "2 zalen",
"lockers": "app",
"cashless": "pin",
"smoking": "buiten"
},
"upcoming_events": 7
},
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200Get one venue400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/artists
List artists
Performers, by name. Most of these profiles are built from the line-ups we import. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
q optional | query | string | - | Free text over the name. |
genre optional | query | string | - | Only artists whose primary genre is this. |
country optional | query | string | - | Only artists from this country, as the profile spells it. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/artists?q=lens&limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/artists?q=lens&limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/artists?q=lens&limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "amelie-lens",
"name": "Amelie Lens",
"genre": "Techno",
"country": "BE",
"city": "Antwerpen",
"verified": true,
"image_url": "https://partyppl.nl/media/artists/amelie-lens.jpg",
"links": {
"spotify": "https://open.spotify.com/artist/0",
"instagram": "https://instagram.com/amelielens"
},
"url": "https://partyppl.nl/artiest/amelie-lens"
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List artists400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/artists/{id}
Get one artist
One artist with their biography, their links, their discography and their next dates in NL and BE. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
Example request
curl -s "https://api.partyppl.nl/v1/artists/amelie-lens" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/artists/amelie-lens", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/artists/amelie-lens",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": {
"id": "amelie-lens",
"name": "Amelie Lens",
"genre": "Techno",
"country": "BE",
"city": "Antwerpen",
"verified": true,
"image_url": "https://partyppl.nl/media/artists/amelie-lens.jpg",
"links": {
"spotify": "https://open.spotify.com/artist/0",
"instagram": "https://instagram.com/amelielens"
},
"url": "https://partyppl.nl/artiest/amelie-lens",
"bio": "Belgische techno-dj en producer, oprichter van Lenske.",
"aliases": [],
"agency": null,
"releases": [
{
"title": "Hypnotized",
"year": 2020
}
],
"upcoming_events": [
{
"id": "oost-sat",
"slug": "oost-hard-techno-night",
"title": "OOST: Hard Techno Night",
"date": "2026-10-03",
"end_date": null,
"start": "23:00",
"end": "06:00",
"timezone": "Europe/Amsterdam",
"status": "approved",
"cancelled": false,
"cancel_reason": null,
"postponed_to": null,
"genres": [
"Hard techno",
"Techno"
],
"price": 18,
"price_max": 25,
"currency": "EUR",
"age": 18,
"ticket_url": null,
"artwork_url": "https://partyppl.nl/media/imports/oost/oost-sat.jpg",
"venue": {
"id": "oost",
"name": "OOST",
"city": "Amsterdam",
"address": "Zeeburgerpad 1, 1018 AJ Amsterdam",
"lat": 52.3701,
"lng": 4.9412
},
"organisation": {
"id": "oost-collective",
"name": "OOST Collective"
},
"url": "https://partyppl.nl/e/oost-hard-techno-night",
"updated_at": 1758499200
}
]
},
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200Get one artist400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/organisations
List organisations
Promoters and the programming teams of clubs, by name. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
q optional | query | string | - | Free text over the name. |
verified optional | query | boolean | - | True: only organisations we have checked. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/organisations?q=oost&limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/organisations?q=oost&limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/organisations?q=oost&limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "oost-collective",
"name": "OOST Collective",
"verified": true,
"since": 2014,
"bio": "Hard techno in Amsterdam Oost.",
"website": "https://clubooost.nl",
"events_count": 42,
"url": "https://partyppl.nl/organisatie/oost-collective"
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List organisations400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/organisations/{id}
Get one organisation
One organisation. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
Example request
curl -s "https://api.partyppl.nl/v1/organisations/oost-collective" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/organisations/oost-collective", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/organisations/oost-collective",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": {
"id": "oost-collective",
"name": "OOST Collective",
"verified": true,
"since": 2014,
"bio": "Hard techno in Amsterdam Oost.",
"website": "https://clubooost.nl",
"events_count": 42,
"url": "https://partyppl.nl/organisatie/oost-collective"
},
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200Get one organisation400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/genres
List genres
The fixed list of genres an event can carry, with the family each belongs to. These are the only values ?genre= accepts. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/genres?limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/genres?limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/genres?limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"name": "Hard techno",
"group": "Hard",
"slug": "hard-techno"
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List genres400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/radar
List new releases
Radar: new singles, EPs and albums from the artists in the agenda, newest first, from the last one to four weeks. Read only. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
genre optional | query | string | - | Only records by artists of this genre, one of /v1/genres. |
weeks optional | query | integer1..4 | 4 | How many weeks back: 1, 2 or 4. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/radar?genre=Techno&limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/radar?genre=Techno&limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/radar?genre=Techno&limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"title": "The Resistance EP",
"type": "ep",
"release_date": "2026-09-24",
"genre": "Techno",
"artists": [
{
"id": "charlotte-de-witte",
"name": "Charlotte de Witte",
"url": "https://partyppl.nl/artiest/charlotte-de-witte"
}
],
"spotify_url": null,
"deezer_url": "https://www.deezer.com/album/123456789",
"cover_url": "https://partyppl.nl/media/artists/charlotte-de-witte-rel-1a2b3c4d.jpg",
"next_event": {
"id": "ev_7k2m",
"title": "Awakenings Upclose",
"date": "2026-10-17",
"city": "Amsterdam",
"url": "https://partyppl.nl/e/awakenings-upclose-2026"
}
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List new releases400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/badges
The badge catalogue
Every badge somebody can earn on PartyPPL, with its family, its rung and what it takes, in Dutch and English. Every rule is checked against what a person has actually been to or contributed: nights that have happened, cities, rooms, genres attended, festivals, friends, corrections the agenda team applied, answers somebody else found useful. Nothing here is read from a setting somebody ticked, and nothing rewards volume of posting or money spent. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/badges?limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/badges?limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/badges?limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "nacht-25",
"family": "nights",
"tier": 25,
"mark": "25",
"name": {
"nl": "25 nachten",
"en": "25 nights"
},
"description": {
"nl": "Vijfentwintig nachten geweest",
"en": "Twenty-five nights out"
},
"counts": "nights",
"needs": 25
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200The badge catalogue400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/users/{id}/badges
The badges one person has
The badges of one public profile, addressed by handle or by id, with the date each one was earned and the number behind it at that moment. Badges are personal data and this follows the profile exactly: a private account, a suspended account and an account that does not exist all answer 404, because saying which of the three it is would itself be telling you something about somebody. Nothing in the answer says what they have not earned.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
id required | path | string | - | The id of the resource. Events also accept their slug. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/users/oost-sat/badges?limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/users/oost-sat/badges?limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/users/oost-sat/badges?limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"id": "nacht-25",
"family": "nights",
"tier": 25,
"name": {
"nl": "25 nachten",
"en": "25 nights"
},
"description": {
"nl": "Vijfentwintig nachten geweest",
"en": "Twenty-five nights out"
},
"value": 27,
"earned_at": 1789654255
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"user": {
"id": "u_noor",
"handle": "noor",
"name": "Noor",
"url": "https://partyppl.nl/u/noor"
},
"catalogue": 40
}
}Statuses
200The badges one person has400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.404No such resource.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.
GET/cities
List cities
Every city with at least one event coming up, busiest first, with the number of events and the middle of that city's venues. Needs a token with the read scope. A read token is free: create one in the organiser portal under API. Browsing the site itself needs nothing.
Parameters
| Name | In | Type | Default | Description |
|---|---|---|---|---|
country optional | query | NL | BE | - | Only cities in this country. |
limit optional | query | integer1..100 | 25 | Items per page, 1 to 100. |
cursor optional | query | string | - | Opaque cursor from meta.next_cursor of the previous page. Do not build one yourself. |
Example request
curl -s "https://api.partyppl.nl/v1/cities?country=NL&limit=25" \
-H "Authorization: Bearer $PPL_TOKEN"const res = await fetch("https://api.partyppl.nl/v1/cities?country=NL&limit=25", {
headers: {
Authorization: `Bearer ${process.env.PPL_TOKEN}`,
},
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data, meta } = await res.json();import os
import requests
res = requests.get(
"https://api.partyppl.nl/v1/cities?country=NL&limit=25",
headers={"Authorization": f"Bearer {os.environ['PPL_TOKEN']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()Show the example response
{
"data": [
{
"name": "Amsterdam",
"country": "NL",
"upcoming_events": 312,
"lat": 52.3702,
"lng": 4.8952,
"url": "https://partyppl.nl/stad/amsterdam"
}
],
"meta": {
"request_id": "9f1c2b4a-7e3d-4a10-9c2f-6b8d1e5a0c33",
"count": 1,
"limit": 25,
"next_cursor": null,
"attribution": {
"text": "Event data from PartyPPL. Link back to the event page on partyppl.nl.",
"url": "https://partyppl.nl",
"terms": "https://api.partyppl.nl/v1/terms"
}
}
}Statuses
200List cities400The request is wrong.error.details.paramsnames every bad parameter.401No token, or a token we do not know.403The token is valid but does not carry thereadscope, or the organisation is suspended.429Over the per-minute limit or the daily quota.Retry-Aftersays how long to wait.500Something went wrong on our side. Quoterequest_id.