Documentation Index
Fetch the complete documentation index at: https://docs.rocksky.app/llms.txt
Use this file to discover all available pages before exploring further.
rocksky_ex is a pipe-friendly Elixir client built on Req.
Install
def deps do
[
{:rocksky_ex, "~> 0.2"}
]
end
Quick start
client = Rocksky.new(token: System.get_env("ROCKSKY_TOKEN"))
{:ok, profile} =
client
|> Rocksky.Actor.get_profile(did: "did:plc:7vdlgi2bflelz7mmuxoqjfcr")
{:ok, %{"scrobbles" => scrobbles}} =
client
|> Rocksky.Actor.get_actor_scrobbles(did: "did:plc:7vdlgi2bflelz7mmuxoqjfcr", limit: 25)
The client is the first argument so calls compose naturally with |>. Each
namespace module mirrors a lexicon — app.rocksky.actor.getProfile becomes
Rocksky.Actor.get_profile/2.
Configuration
Rocksky.new(
base_url: "https://api.rocksky.app",
token: "your-bearer-token",
headers: [{"x-app", "my-app"}],
req_options: [retry: false]
)
Or globally:
# config/config.exs
config :rocksky_ex, base_url: "https://api.rocksky.app"
Derive an authenticated client from a shared base:
base = Rocksky.new()
authed = Rocksky.Client.with_token(base, "tok")
Create a scrobble
client
|> Rocksky.Scrobble.create_scrobble(
title: "In Bloom",
artist: "Nirvana",
album: "Nevermind",
timestamp: System.system_time(:second)
)
Builder style
| Builder | XRPC procedure |
|---|
Rocksky.Scrobble.Builder | app.rocksky.scrobble.createScrobble |
Rocksky.Song.Builder | app.rocksky.song.createSong |
Rocksky.Mirror.Builder | app.rocksky.mirror.putMirrorSource |
Rocksky.Apikey.Builder | app.rocksky.apikey.createApikey |
Rocksky.Shout.ReplyBuilder | app.rocksky.shout.replyShout |
Rocksky.Shout.ReportBuilder | app.rocksky.shout.reportShout |
alias Rocksky.Scrobble.Builder, as: Scrobble
Scrobble.new(title: "In Bloom", artist: "Nirvana")
|> Scrobble.album("Nevermind")
|> Scrobble.album_art("https://...")
|> Scrobble.spotify_link("https://open.spotify.com/track/...")
|> Scrobble.timestamp(System.system_time(:second))
|> Scrobble.submit(client)
Camel-cased lexicon keys become snake-cased setters (album_art/2,
mb_id/2, spotify_link/2). new/1 and put/2 accept either form. Use
put/2 to set several at once and to_body/1 to inspect the JSON body
without submitting.
Missing required fields are caught locally:
Scrobble.new(title: "Only title") |> Scrobble.submit(client)
# => {:error, %Rocksky.Error{reason: :missing_fields, body: %{missing: [:artist]}}}
Find a song
{:ok, song} = Rocksky.Song.get_song(client, isrc: "USDW19811234")
{:ok, song} = Rocksky.Song.get_song(client, mbid: "f1234567-...")
{:ok, song} = Rocksky.Song.get_song(client, uri: "at://did:plc:abc/app.rocksky.song/123")
Charts and search
client |> Rocksky.Charts.get_top_tracks(limit: 10, startDate: "2026-01-01")
{:ok, results} = Rocksky.Feed.search(client, query: "nevermind")
Player remote-control
client |> Rocksky.Player.play(playerId: id)
client |> Rocksky.Player.pause(playerId: id)
client |> Rocksky.Player.next(playerId: id)
client |> Rocksky.Player.seek(playerId: id, position: 60_000)
Paginate with Stream
Stream.unfold(0, fn offset ->
case Rocksky.Actor.get_actor_scrobbles(client,
did: "alice.bsky.social",
limit: 50,
offset: offset
) do
{:ok, %{"scrobbles" => []}} -> nil
{:ok, %{"scrobbles" => batch}} -> {batch, offset + length(batch)}
{:error, _} -> nil
end
end)
|> Stream.flat_map(& &1)
|> Enum.take(500)
Errors
case Rocksky.Song.get_song(client, uri: "at://missing") do
{:ok, song} -> song
{:error, %Rocksky.Error{status: 404}} -> :not_found
{:error, %Rocksky.Error{reason: :unauthorized}} -> :reauth
{:error, err} -> Logger.error("rocksky: #{Exception.message(err)}")
end
Modules
| Module | NSID prefix |
|---|
Rocksky.Actor | app.rocksky.actor.* |
Rocksky.Album | app.rocksky.album.* |
Rocksky.Apikey | app.rocksky.apikey.* |
Rocksky.Artist | app.rocksky.artist.* |
Rocksky.Charts | app.rocksky.charts.* |
Rocksky.Dropbox | app.rocksky.dropbox.* |
Rocksky.Feed | app.rocksky.feed.* |
Rocksky.GoogleDrive | app.rocksky.googledrive.* |
Rocksky.Graph | app.rocksky.graph.* |
Rocksky.Like | app.rocksky.like.* |
Rocksky.Mirror | app.rocksky.mirror.* |
Rocksky.Player | app.rocksky.player.* |
Rocksky.Playlist | app.rocksky.playlist.* |
Rocksky.Scrobble | app.rocksky.scrobble.* |
Rocksky.Shout | app.rocksky.shout.* |
Rocksky.Song | app.rocksky.song.* |
Rocksky.Spotify | app.rocksky.spotify.* |
Rocksky.Stats | app.rocksky.stats.* |
Generic escape hatch:
Rocksky.HTTP.query(client, "app.rocksky.actor.getProfile", did: "alice")
Types
Lexicon-derived structs are available under Rocksky.Generated.*, mirroring every lex *View* / *Record / *Input / *Output / *Params shape from the Rocksky lexicons. Regenerate with bun run lexgen:types at the repo root.
License
MIT © Tsiry Sandratraina. Source:
sdk/elixir.