Google Play Music's Undocumented API

I was looking into a way of controlling Google Play Music (running in a tab in my browser) from another process and stumbled across an undocumented API which allowed me to do pretty much exactly that - all you need to use it is the ability to fire events on the #player-api element on the page.

The API works by listening for playerApi events on #player-api, calling JSON.parse on the detail field of the event, then performing an action depending on the data provided. The format of the object passed via the detail field is an array of at least one element. The function d.g6 in the code below is responsible for handling events sent to the API.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
d.g6 = function(a) {
a = JSON.parse(a.Rc.detail);
a instanceof Array && (a = new $C(a),
this.Yxa(a))
}
;
d.Yxa = function(a) {
var b = a.getCommand()
, c = a.TC();
this.Xa.kc("Player API", b.toString(), c ? c.toString() : void 0);
switch (b) {
case 10:
this.ta.NKa();
break;
case 1:
this.uf.Ol();
break;
case 2:
this.Ea.Zv(c);
break;
case 3:
this.Ea.P$();
break;
case 6:
this.Ab.hga();
break;
...

The first element of the array is the command number. The other elements are optional and are used as arguments for certain commands. The uses of the indices are shown below:

1
2
3
4
5
Index 0: Command number
Index 1: Relative seek time in milliseconds (for command 8)
Index 2: Volume percentage (for command 16)
Index 3: Absolute time to seek to in milliseconds (for command 19)
Index 4: Unknown argument

The command number should be a number between 1 and 22 inclusive, corresponding to the following actions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Command 1:  Toggle play state
Command 2: Next song
Command 3: Previous song
Command 4: "Thumbs Up" current song
Command 5: "Thumbs Down" current song
Command 6: Toggle shuffle mode
Command 7: Toggle repeat mode
Command 8: Seek relative to current time (reads from index 1)
Command 9: Get current time and send a playerApiReturn event with the time to the #player-api element
Command 10: Unknown
Command 11: Start "I'm feeling lucky" radio
Command 12: Navigate to the currently playing artist's page
Command 13: Navigate to the currently playing album's page
Command 14: Increase volume by 10%
Command 15: Decrease volume by 10%
Command 16: Set the volume to the percentage given in index 2
Command 17: Pause
Command 18: Play
Command 19: Set time in current song to time given in index 3
Command 20: Seek backwards by 30 seconds
Command 21: Seek forwards by 30 seconds
Command 22: Seek backwards by 10 seconds

For example, to set the current volume to 50%, you could run the following code:

1
2
3
document.getElementById('player-api').dispatchEvent(new CustomEvent('playerApi', {
detail: '[16, 0, 50]'
}));

For command 9, which will return the current time, you first need to setup an event handler for playerApiReturn on the #player-api element, then issue the command. Such a handler might look something like this:

1
document.getElementById('player-api').addEventListener('playerApiReturn', e => console.log(e.detail.time))

You can see this process in action below, returning the current time of 0:39.6:

Since this API isn’t documented anywhere, it can and probably will change at some point.