waterruupto

Search

Search for albums and tracks

Developer Guide

Push your media data to the AT Protocol and waterruupto indexes it automatically.

How it works

  1. You create records on your PDS in waterruupto's lexicon format
  2. Jetstream broadcasts the event to the network
  3. Our indexer picks it up, resolves metadata, and indexes it
  4. Your data appears on the site — and stays on your PDS

Entity reference

Every rating and comment targets an entity via an entityRef:

{
  "entityType": "musicAlbum",  // or "musicTrack"
  "source": "spotify",
  "sourceId": "4aawyAB9vmqN0BTKmVLGCR"  // Spotify ID
}

Supported entity types

entityTypesourcesourceId
musicAlbumspotifySpotify album ID
musicTrackspotifySpotify track ID

More entity types and sources coming.

Rating

Collection: xyz.waterruupto.rating

{
  "entityRef": {
    "entityType": "musicAlbum",
    "source": "spotify",
    "sourceId": "4aawyAB9vmqN0BTKmVLGCR"
  },
  "value": 8,          // 1–10
  "createdAt": "2025-01-15T12:00:00Z"
}

Comment

Collection: xyz.waterruupto.comment

{
  "entityRef": {
    "entityType": "musicTrack",
    "source": "spotify",
    "sourceId": "3n3Ppam7vgaVa1iaRUc9Lp"
  },
  "text": "The bass line on this track is incredible.",
  "createdAt": "2025-01-15T12:00:00Z"
}

Comments also support an optional musicClip field with startSeconds and endSeconds for referencing a specific part of a track.

Example: create a rating

Using @atproto/api to write a rating record to your PDS:

import { AtpAgent } from "@atproto/api";

const agent = new AtpAgent({ service: "https://bsky.social" });
await agent.login({ identifier: "you.bsky.social", password: "..." });

await agent.com.atproto.repo.createRecord({
  repo: agent.session.did,
  collection: "xyz.waterruupto.rating",
  record: {
    entityRef: {
      entityType: "musicAlbum",
      source: "spotify",
      sourceId: "4aawyAB9vmqN0BTKmVLGCR",
    },
    value: 8,
    createdAt: new Date().toISOString(),
  },
});

Bulk migration

Have a RateYourMusic export or Letterboxd CSV? Write a script that maps each entry to the lexicon format above and calls createRecord for each one. The indexer picks up every record automatically — no registration or API keys needed.

Lexicon source

Full lexicon schemas are in the GitHub repository.