Skip to main content

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

BuilderXRPC procedure
Rocksky.Scrobble.Builderapp.rocksky.scrobble.createScrobble
Rocksky.Song.Builderapp.rocksky.song.createSong
Rocksky.Mirror.Builderapp.rocksky.mirror.putMirrorSource
Rocksky.Apikey.Builderapp.rocksky.apikey.createApikey
Rocksky.Shout.ReplyBuilderapp.rocksky.shout.replyShout
Rocksky.Shout.ReportBuilderapp.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")
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

ModuleNSID prefix
Rocksky.Actorapp.rocksky.actor.*
Rocksky.Albumapp.rocksky.album.*
Rocksky.Apikeyapp.rocksky.apikey.*
Rocksky.Artistapp.rocksky.artist.*
Rocksky.Chartsapp.rocksky.charts.*
Rocksky.Dropboxapp.rocksky.dropbox.*
Rocksky.Feedapp.rocksky.feed.*
Rocksky.GoogleDriveapp.rocksky.googledrive.*
Rocksky.Graphapp.rocksky.graph.*
Rocksky.Likeapp.rocksky.like.*
Rocksky.Mirrorapp.rocksky.mirror.*
Rocksky.Playerapp.rocksky.player.*
Rocksky.Playlistapp.rocksky.playlist.*
Rocksky.Scrobbleapp.rocksky.scrobble.*
Rocksky.Shoutapp.rocksky.shout.*
Rocksky.Songapp.rocksky.song.*
Rocksky.Spotifyapp.rocksky.spotify.*
Rocksky.Statsapp.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.