Using the API

Everything you can see and do on Arcane City is also available as JSON, so you can build scripts, importers, bots, and apps on top of it.
This page is the plain-language introduction. The full endpoint reference lives at /api/docs.

Last updated: September 2026 · Back to Help

What the API is

A JSON REST API over the same data the site shows: events, entities (venues, artists, promoters, DJs…), series, tags, photos, links, contacts, forum threads, posts, blogs, and more.

  • Base URL: https://api.arcane.city/api
  • Format: requests and responses are JSON. Send Accept: application/json on every request so errors come back as JSON too.
  • Reference: every endpoint, parameter, and response shape is listed at /api/docs.

Your account is your API account

There is no separate developer signup. If you have a site account, you already have API access: register if you don't.

API calls run as your user, with exactly the same permissions and visibility rules as the website:

  • You see public content, plus any private content you'd be able to see when logged in.
  • You can edit or delete only what you own, unless your account has admin rights.
  • Anything you create is attributed to your account.

In short: if you can't do it in the browser, you can't do it through the API either.

Authentication

Most endpoints require you to identify yourself. There are two ways to do it.

Option 1: HTTP Basic auth

Send your site email and password on each request (Authorization: Basic base64(email:password)). It's the simplest option for quick scripts. Most tools handle the encoding for you:

curl -u 'you@example.com:your-password' \
  -H 'Accept: application/json' \
  https://api.arcane.city/api/events

Option 2: Bearer token

If you'd rather not store your password in a script, exchange it once for a token and send that instead.

  1. Create a token with basic auth and a token_name of your choice:
    curl -u 'you@example.com:your-password' \
      -H 'Accept: application/json' \
      -d 'token_name=my-importer' \
      https://api.arcane.city/api/tokens/create
    
    # => {"token": "12|AbCdEf..."}
    The token is shown only once, so save it somewhere safe.
  2. Send it as a bearer token:
    curl -H 'Authorization: Bearer 12|AbCdEf...' \
      -H 'Accept: application/json' \
      https://api.arcane.city/api/auth/me
    GET /api/auth/me returns the user the token belongs to, which is a handy way to check it works.
  3. To revoke tokens, call GET /api/tokens/invalidate with a token. It deletes all of your tokens, not just that one.

A token grants the same access as your account. Scoped or read-only tokens aren't available, so treat a token like your password.

Reading data

Each kind of record has a list endpoint and a single-record endpoint, for example GET /api/events, GET /api/events/{id}, GET /api/entities, GET /api/series, and GET /api/tags.

Paging

  • page: which page to return, starting at 1.
  • limit: results per page, up to 1000.
  • List responses include data plus current_page, last_page, total, and next_page_url. Keep following next_page_url until it's null.

Sorting

sort takes a column, for example events.start_at or entities.name. direction is asc or desc.

Filtering

Filters use filters[field]=value. Some useful ones:

  • filters[name]=…: name contains the text.
  • filters[tag]=techno,house: has any of these tags.
  • filters[tag_all]=techno,house: has all of these tags.
  • filters[venue]=…: events at a venue, matched by its slug (events only).
  • filters[start_at][start]=2026-10-01&filters[start_at][end]=2026-10-31: events starting in a date range.
  • filters[entity_type]=venue: entities of one type, matched by the type's slug (entities only).

See /api/docs for the filters each endpoint supports.

# Techno events in October 2026, soonest first, 50 per page
curl -G -u 'you@example.com:your-password' \
  -H 'Accept: application/json' \
  --data-urlencode 'filters[tag]=techno' \
  --data-urlencode 'filters[start_at][start]=2026-10-01' \
  --data-urlencode 'filters[start_at][end]=2026-10-31' \
  -d 'sort=events.start_at' -d 'direction=asc' -d 'limit=50' \
  https://api.arcane.city/api/events

Writing data

Create records with POST to the list endpoint (for example POST /api/events). Replace a record with PUT /api/events/{id}, or change only some fields with PATCH /api/events/{id}. The same rules apply as on the site's forms: required fields, validation messages, and ownership checks. Validation errors come back as 422 with a message for each field.

curl -u 'you@example.com:your-password' \
  -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -d '{
        "name": "Basement Session",
        "slug": "basement-session",
        "start_at": "2026-10-17 21:00:00",
        "event_type_id": 1,
        "visibility_id": 3,
        "tag_list": ["techno", 42]
      }' \
  https://api.arcane.city/api/events

Look up valid ids with GET /api/event-types, GET /api/visibilities, GET /api/entity-types, and so on.

Tags

  • tag_list accepts existing tag ids, tag names, or a mix of both.
  • Names match existing tags regardless of capitalization, so "diy" and "DIY" are the same tag. A new tag is created only when nothing matches.
  • On PUT, leaving out tag_list removes all tags. On PATCH, tags change only when you send tag_list.

Photos

  • Upload: POST /api/events/{id}/photos with a multipart file field. This also works for entities and series.
  • From a URL: POST /api/events/{id}/photos/from-url with {"url": "https://…"}. The server downloads the image for you. The URL must be public https, and the image must be a jpg, png, gif, or webp of 5 MB or less.
  • You can add photos only to records you own (or any record, as an admin). The first photo becomes the primary image.

Rate limits & etiquette

  • 240 requests per minute per logged-in user (basic auth or token).
  • 120 requests per minute per IP address for requests that aren't logged in.
  • Adding photos by URL has its own limit of 20 requests per minute per user.
  • After 20 failed logins in a minute from one IP address, basic auth from that address is refused until the minute is up, even with the right password. If your script keeps failing to log in, check your credentials before retrying.
  • Every response includes X-RateLimit-Remaining. Over a limit you'll get 429 Too Many Requests with a Retry-After header saying how many seconds to wait.
  • This is a community-run site on modest hardware. Page through results instead of requesting huge lists, cache what you can, and space out bulk imports.
  • Check for existing events, entities, and series before creating new ones so you don't make duplicates.
  • Everything you create through the API is covered by the Terms of Service, just like content added through the site.

Where to go next