Logo

API

Mit der API können deine eigenen Anwendungen – z. B. deine Website oder dein Ticketsystem – die Events eines Profils lesen, erstellen und bearbeiten. Die API gilt immer für genau ein Profil und nur für Events, deren Eigentümer dieses Profil ist.

Erste Schritte

  1. Wechsle zu dem Profil, für das du die API nutzen willst.
  2. Öffne Einstellungen → API und erstelle ein Token. Nur der Eigentümer eines Profils kann Tokens erstellen.
  3. Kopiere das Token sofort – es wird nur einmal angezeigt. Behandle es vertraulich wie ein Passwort.

Schicke das Token bei jeder Anfrage im Header Authorization mit. Alle Adressen beginnen mit /api/v1 unter https://upcoming.at:

curl https://upcoming.at/api/v1/events \
  -H "Authorization: Bearer 1|AbCdEf…"

In den Beispielen steht $TOKEN für dein Token.

Berechtigungen und Grenzen

  • Nur lesen: Events, Orte und Tags abrufen.
  • Lesen und schreiben: zusätzlich Events erstellen und bearbeiten sowie Orte speichern. Dafür muss das Profil freigeschaltet sein: Beantrage den Schreibzugriff unter Einstellungen → API und beschreibe kurz, wofür du die API nutzt. Nach der Prüfung durch das Upcoming-Team bekommst du eine Benachrichtigung. Lesen geht sofort.
  • Events anderer Profile – auch solche, bei denen dein Profil nur Organisator ist – gibt es für die API nicht (Antwort 404).
  • Höchstens 60 Anfragen pro Minute und Profil.
  • Gesperrte Profile können die API nicht nutzen. Wird ein Profil übergeben, verlieren alle seine Tokens die Gültigkeit.
  • Löschen von Events, Sub-Events und weitere Organisatoren sind (noch) nicht Teil der API – das geht weiterhin auf der Website.

Die Sprache von Fehlermeldungen und Namen richtet sich nach dem Header Accept-Language (de-DE oder en-US), ohne ihn nach der Sprache des Eigentümer-Kontos.

Events auflisten

GET /api/v1/events – die Events des Profils, nach Beginn sortiert, 25 pro Seite.

Parameter Bedeutung
from, to Nur Events, die in diesem Zeitraum beginnen (ISO 8601)
updated_since Nur Events, die seitdem geändert wurden – praktisch zum Abgleichen
per_page 1–100 Events pro Seite
page Seite
curl "https://upcoming.at/api/v1/events?from=2026-12-01T00:00:00%2B01:00" \
  -H "Authorization: Bearer $TOKEN"

Die Antwort enthält die Events in data, dazu links und meta (u. a. total, last_page) zum Blättern.

Ein Event abrufen

GET /api/v1/events/{uuid}

{
  "data": {
    "uuid": "9d4c…",
    "url": "https://upcoming.at/events/9d4c…",
    "title": "Sommerkonzert",
    "description": "Open Air im Park",
    "event_type": "in_person",
    "visibility": "public",
    "startdatetime": "2026-07-04T19:00:00+02:00",
    "enddatetime": "2026-07-04T22:00:00+02:00",
    "startdatetime_utc": "2026-07-04T17:00:00+00:00",
    "enddatetime_utc": "2026-07-04T20:00:00+00:00",
    "timezone": "Europe/Berlin",
    "is_all_day": false,
    "place_id": 42,
    "place": { "id": 42, "type": "venue", "name": "Stadtpark", "address": "Parkweg 1", "postal_code": "90402", "city": "Nürnberg", "country": "Deutschland", "latitude": 49.45, "longitude": 11.08, "url": "…" },
    "online_url": null,
    "online_instructions": null,
    "photo_urls": null,
    "options": { "links": [], "allow_comments": true, "allow_invitations": true, "organizers_can_duplicate": false, "allow_sub_events": false, "max_attendees": null, "ticket_price": "15 €" },
    "tags": [{ "id": 3, "slug": "konzert", "name": "Konzert", "names": { "de-DE": "Konzert", "en-US": "Concert" } }],
    "labels": [{ "id": 7, "name": "Sommer" }],
    "parent_uuid": null,
    "attendance_counts": { "going": 12, "interested": 30, "invited": 4 },
    "created_at": "2026-05-01T10:00:00+00:00",
    "updated_at": "2026-05-02T08:30:00+00:00"
  }
}

Event erstellen

POST /api/v1/events – Eigentümer ist immer das Profil des Tokens. Antwort: 201 mit dem Event.

curl -X POST "https://upcoming.at/api/v1/events" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sommerkonzert",
    "startdatetime": "2026-07-04T19:00:00",
    "enddatetime": "2026-07-04T22:00:00",
    "timezone": "Europe/Berlin",
    "place_id": 42,
    "tag_ids": [3],
    "new_labels": ["Sommer"]
  }'
Feld Bedeutung
title Pflicht, max. 255 Zeichen
description Text, max. 10.000 Zeichen
event_type in_person (Standard), online oder hybrid
startdatetime Pflicht. Beginn, siehe „Datum und Zeitzone“
enddatetime Ende, nach dem Beginn
timezone Pflicht, z. B. Europe/Berlin
is_all_day Ganztägig (true/false), siehe „Datum und Zeitzone“
visibility public (Standard), followers, unlisted oder private
place_id Ort, siehe „Orte“
online_url Pflicht bei online und hybrid
online_instructions Hinweise zur Teilnahme online
is_organizer Profil als Organisator anzeigen (Standard true)
tag_ids IDs aus „Tags“, höchstens so viele wie auf der Website
label_ids, new_labels Eigene Labels des Profils: vorhandene IDs und neue Namen
photo Bild (JPG, PNG, WebP, GIF, HEIC), nur per multipart/form-data
options.links Bis zu 5 Links: { "url": "…", "title": "…", "type": "ticket" }
options.ticket_price Preis als Text, z. B. "15 €"
options.max_attendees Höchstzahl Teilnehmender
options.allow_comments, options.allow_invitations Kommentare bzw. Einladungen erlauben (Standard true)

Mit Foto sendest du die Felder als Formular:

curl -X POST "https://upcoming.at/api/v1/events" \
  -H "Authorization: Bearer $TOKEN" \
  -F title="Sommerkonzert" \
  -F startdatetime="2026-07-04T19:00:00" \
  -F timezone="Europe/Berlin" \
  -F photo=@plakat.jpg

Event bearbeiten

PATCH /api/v1/events/{uuid} – nur die gesendeten Felder ändern sich, auch bei options. Tags und Labels bleiben, wenn du tag_ids bzw. label_ids/new_labels nicht sendest; gesendet ersetzen sie die bisherigen ("tag_ids": [] entfernt alle Tags). "remove_photo": true entfernt das Foto.

curl -X PATCH "https://upcoming.at/api/v1/events/9d4c…" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "startdatetime": "2026-07-05T19:00:00" }'

Ändern sich Datum, Uhrzeit oder Ort, werden Teilnehmende und Eingeladene benachrichtigt – wie auf der Website.

Ein neues Foto lädst du per POST mit _method=PATCH hoch (PHP nimmt Dateien nur bei POST an):

curl -X POST "https://upcoming.at/api/v1/events/9d4c…" \
  -H "Authorization: Bearer $TOKEN" \
  -F _method=PATCH \
  -F photo=@plakat.jpg

Datum und Zeitzone

Zeiten gibst du im ISO-8601-Format an:

  • ohne Offset (2026-07-04T19:00:00): gilt als Ortszeit in timezone des Events,
  • mit Offset (2026-07-04T19:00:00+02:00): der Zeitpunkt zählt, egal welche Zeitzone das Event hat.

Antworten enthalten beides: die Ortszeit mit Offset (startdatetime) und UTC (startdatetime_utc).

Ganztägige Events: Mit "is_all_day": true reicht das Datum (2026-07-04). Mitgesendete Uhrzeiten werden ignoriert: Beginn ist 00:00, Ende 23:59 Ortszeit. Für ein eintägiges Event lässt du enddatetime weg, für mehrere Tage gibst du den letzten Tag an:

{ "is_all_day": true, "startdatetime": "2026-07-04", "enddatetime": "2026-07-06", "timezone": "Europe/Berlin" }

Orte

Events verweisen per place_id auf einen Ort. Suche ihn zuerst:

GET /api/v1/places?query=Stadtpark (optional limit, 1–20)

Die Ergebnisse enthalten zuerst Orte, die es auf Upcoming schon gibt ("source": "local", mit id), danach weitere Treffer der Ortssuche ("source": "geoapify", id ist null). Einen solchen Treffer speicherst du, indem du ihn unverändert sendest:

curl -X POST "https://upcoming.at/api/v1/places" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "geoapify_place_id": "51a3…", "name": "Stadtpark", "address": "Parkweg 1", "city": "Nürnberg", "country": "Deutschland", "postal_code": "90402", "latitude": 49.45, "longitude": 11.08, "type": "venue" }'

Die Antwort enthält den Ort mit seiner id – 201, wenn er neu ist, 200, wenn es ihn schon gab.

Tags

GET /api/v1/tags – alle Tags, die du Events geben kannst, mit id, slug, name und den Namen je Sprache (names).

Fehler

Status Bedeutung
401 Kein oder ungültiges Token
403 Token darf das nicht (z. B. nur lesen), Schreibzugriff ist nicht freigeschaltet, oder das Profil ist gesperrt
404 Event nicht vorhanden oder gehört nicht zu deinem Profil
422 Ungültige Daten, Details in errors
429 Zu viele Anfragen, kurz warten
{
  "message": "Title ist erforderlich.",
  "errors": { "title": ["Title ist erforderlich."] }
}

Versionen

Innerhalb von v1 kommen höchstens neue Felder und Adressen hinzu. Felder werden nicht umbenannt oder entfernt, und ihre Bedeutung ändert sich nicht. Größere Änderungen erscheinen als v2, v1 bleibt dann eine Zeit lang erhalten.