ClubDAM has a mobile app called デンモクmini ("Denmoku mini") which uses an undocumented API.
The main search endpoint is located at:
POST https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet
The POST body is JSON, but the specific fields depend on the content you want to retrieve.
The following fields are required for all requests, or it will return an error response.
appVer
: The version of the Denmoku Mini app (e.g., current version is "2.1.0")deviceId
: A hex string uniquely identifying your device (e.g., "abcdef123456789")deviceNm
: The name of your device (e.g., "Walf's Phone")osVer
: Your OS-specific version (e.g., "4.4.4" if you're on Android 4.4.4)categoryCd
: 6-digit ID depending on what you're searching for (artist, song, etc)
Besides categoryCd
, the values don't seem to matter. Presumably
they're used for internal reporting purposes.
A list of categoryCd
values as extracted from the app:
ID | Description
------ | -----------------------------------
010000 | `ARTIST_NAME`
020000 | `SONG_NAME`
030100 | `NEW_SONG_ALL_SONG`
030201 | `NEW_SONG_LIVE_KARAOKE`
030202 | `NEW_SONG_CAST_PICTURE`
030203 | `NEW_SONG_CLIP_JUST_NOW`
030301 | `NEW_SONG_ANIME_GAME`
030302 | `NEW_SONG_SPECIAL_EFFECTS`
030401 | `NEW_SONG_CM`
030402 | `NEW_SONG_DRAMA_MOVIE`
030403 | `NEW_SONG_VARIETY`
030404 | `NEW_SONG_MUSIC_PROGRAM`
030405 | `NEW_SONG_INFORMATION_PROGRAM`
030406 | `NEW_SONG_SPORTS`
030500 | `NEW_SONG_SOON_DELIVERY`
040000 | `CAST_PICTURE`
050100 | `ANIMATION_SPECIAL_EFFECTS_ANIME`
050200 | `ANIMATION_SPECIAL_EFFECTS_SPECIAL`
050300 | `ANIMATION_SPECIAL_EFFECTS_IMAGE`
060100 | `VOCALOID_HATSUNE_MIKU`
060200 | `VOCALOID_KAGAMINE_RIN_REN`
060300 | `VOCALOID_MEGURINE_RUKA`
060400 | `VOCALOID_KAITO_MEIKO`
060500 | `VOCALOID_GUMI`
060600 | `VOCALOID_KAMUI_GAKUPO`
060700 | `VOCALOID_LILY`
060800 | `VOCALOID_OTHER`
060900 | `VOCALOID_IMAGE`
070100 | `DAM_BEST_POPS`
070200 | `DAM_BEST_BALLAD`
070300 | `DAM_BEST_WESTERN_MUSIC`
070400 | `DAM_BEST_DUET`
070500 | `DAM_BEST_ANIME_SPECIAL`
071100 | `DAM_BEST_RECOMMENDED_1`
071200 | `DAM_BEST_RECOMMENDED_2`
071300 | `DAM_BEST_RECOMMENDED_3`
071400 | `DAM_BEST_RECOMMENDED_4`
071500 | `DAM_BEST_RECOMMENDED_5`
080100 | `CNTNTS_RANKING_BATTLE`
080200 | `CNTNTS_PRECISION_GRADING`
080300 | `CNTNTS_PRECISION_GRADING_II`
080400 | `CNTNTS_PRECISION_GRADING_DX`
080500 | `CNTNTS_FULL_CHORUS`
080600 | `CNTNTS_OTHER_HISTORY`
080700 | `CNTNTS_VOICE_TRAINING_LOG`
080800 | `CNTNTS_PRECISION_GRADING_DX_G`
080900 | `CNTNTS_PRECISION_GRADING_DX_DUET`
By default, when doing song searches, it will return songs for the highest tier machine (LiveDAM). If you're on a lower tier machine (such as Premier DAM), many of these songs aren't actually available.
The Denmoku app has a way to link the app to the actual ClubDAM machine,
by using a QR code. In the app, it will tell you to enter a specific
song ID into the karaoke machine, which will put a QR code up on the
screen. Scanning the QR code will tell the app which machine you're on,
and will add a serialNo
field to the request JSON.
Once you know the serialNo
, you can reuse it in future requests. For
example, I did this at a karaoke place last year, and "AB316238" is
known to be Premier DAM.
songMatchType
specifies how songName
should match, and can be either:
- "0" for "starts with"
- "1" for "contains"
Here, we search for any song beginning with "passion fl".
Note that we're specifying serialNo
to be a Premier DAM machine. If we
search for "wake up my music" instead (which is not available on Premier
DAM), nothing shows up, unless we remove the serialNo
field entirely.
Request:
curl -XPOST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"categoryCd": "020000",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"page": "1",
"songMatchType": "0",
"serialNo": "AB316238",
"songName": "passion fl"
}'
Response:
{
"searchResult": [
{
"artistId": "107891",
"artistName": "みほ・もな from AIKATSU☆STARS!",
"distEnd": "99999999",
"distStart": "20150228",
"firstBars": "胸がときめく リズム 魔法かけてく",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"indicationMonth": "",
"myKey": "0",
"orgKey": "0",
"programTitle": "",
"reqNo": "372915",
"songName": "Passion flower",
"titleFirstKana": ""
}
],
"totalCount": "1",
"totalPage": "1"
}
artistMatchType
specifies how artistName
should match, and can be either:
- "0" for "starts with"
- "1" for "contains"
Here, we search for any artist with "aikatsu" in the name. The result
will give us an artistId
that we can use to find songs by that artist.
Request:
curl -XPOST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"categoryCd": "010000",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"page": "1",
"artistMatchType": "1",
"serialNo": "AB316238",
"artistName": "aikatsu"
}'
Response:
{
"searchResult": [
{
"artistId": "112822",
"artistName": "AIKATSU☆STARS!",
"distEnd": "",
"distStart": "",
"firstBars": "",
"funcAnimePicture": "",
"funcPersonPicture": "",
"funcRecording": "",
"funcScore": "",
"indicationMonth": "",
"myKey": "",
"orgKey": "",
"programTitle": "",
"reqNo": "",
"songName": "",
"titleFirstKana": ""
},
{
"artistId": "110872",
"artistName": "かな・るか from AIKATSU☆STARS!",
"distEnd": "",
"distStart": "",
"firstBars": "",
"funcAnimePicture": "",
"funcPersonPicture": "",
"funcRecording": "",
"funcScore": "",
"indicationMonth": "",
"myKey": "",
"orgKey": "",
"programTitle": "",
"reqNo": "",
"songName": "",
"titleFirstKana": ""
},
// etc
],
"totalCount": "15",
"totalPage": "1"
}
Request:
curl -XPOST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"categoryCd": "010000",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"page": "1",
"serialNo": "AB316238",
"artistId": "112822"
}'
Response:
{
"searchResult": [
{
"artistId": "112822",
"artistName": "AIKATSU☆STARS!",
"distEnd": "99999999",
"distStart": "20151028",
"firstBars": "今日が生まれかわる",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"indicationMonth": "",
"myKey": "0",
"orgKey": "0",
"programTitle": "",
"reqNo": "374166",
"songName": "START DASH SENSATION",
"titleFirstKana": ""
},
{
"artistId": "112822",
"artistName": "AIKATSU☆STARS!",
"distEnd": "99999999",
"distStart": "20151028",
"firstBars": "きみと ハピハピ ピカピカ",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"indicationMonth": "",
"myKey": "0",
"orgKey": "0",
"programTitle": "",
"reqNo": "374167",
"songName": "lucky train!",
"titleFirstKana": ""
}
],
"totalCount": "2",
"totalPage": "1"
}
This uses categoryCd
"030301" which corresponds to NEW_SONG_ANIME_GAME
.
You can use the other NEW_SONG_
categories for other results.
The results are sorted by series title (programTitle
) instead of
release date (distStart
), so you will need to sort client-side.
Although the results might say there's more than 1 page, there's really just one page with all the songs.
Request:
curl -XPOST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"categoryCd": "030301",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"page": "1",
"songMatchType": "0",
"serialNo": "AB316238"
}'
Response:
{
"searchResult": [
{
"artistId": "121081",
"artistName": "るか・せな from AIKATSU☆STARS!",
"distEnd": "99999999",
"distStart": "20161102",
"firstBars": "ページをめくるたびに 新しいキミ",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"indicationMonth": "999999",
"myKey": "0",
"orgKey": "0",
"programTitle": "アイカツスターズ!",
"reqNo": "376977",
"songName": "So Beautiful Story",
"titleFirstKana": ""
},
// ... etc
],
"totalCount": "308",
"totalPage": "4"
}
This request is used by the app to list all anime series. There's no pagination, it just returns a huge list of all of them (currently 3.6k songs, with a 1.4MB response). It's very slow.
It actually returns one song for each series, but the app only uses
the programTitle
and titleFirstKana
(to group series together for
sorting/navigation purposes) from the request.
This uses categoryCd
of "050100" which corresponds to
ANIMATION_SPECIAL_EFFECTS_ANIME
. Other categories will yield different
results.
Request:
curl -XPOST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"categoryCd": "050100",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"page": "1",
"serialNo": "AB316238"
}'
Response:
{
"searchResult": [
{
"artistId": "39298",
"artistName": "UNCO☆STAR",
"distEnd": "99999999",
"distStart": "20070126",
"firstBars": "U.N.C.O. U.N.C.O. U.N.C.O.",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"indicationMonth": "",
"myKey": "0",
"orgKey": "0",
"programTitle": "アークエとガッチンポー てんこもり",
"reqNo": "643758",
"songName": "ウラ・ガッチンポーのテーマ",
"titleFirstKana": "ア"
},
// ... etc
],
"totalCount": "3618",
"totalPage": "37"
}
Again, the totalPage
count is a lie, there's only one page available,
with 3.6k items.
If you know the programTitle
(series name) of the series, you can
find songs for that series, assuming you use the same categoryCd
that the series belongs to.
Request:
curl -XPOST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamSearchServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"categoryCd": "050300",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"page": "1",
"programTitle": "アイカツ!",
"serialNo": "AB316238"
}'
Response:
{
"searchResult": [
{
"artistId": "91801",
"artistName": "わか、ふうり、すなお from STAR☆ANIS",
"distEnd": "99999999",
"distStart": "20130330",
"firstBars": "さぁ! 行こう 光る未来へ ホラ",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"indicationMonth": "",
"myKey": "0",
"orgKey": "0",
"programTitle": "アイカツ!",
"reqNo": "360715",
"songName": "アイドル活動!",
"titleFirstKana": "ア"
},
// ... etc
],
"totalCount": "45",
"totalPage": "1"
}
The Denmoku app also has a feature where you can select songs on your phone and check if they exist in the system. This will send the song name and artist name, and return any matches.
It uses a different endpoint from the search one:
POST https://denmoku.clubdam.com/dkdenmoku/DkDamIsExistServlet
It accepts the same required fields, and optional serialNo
.
If the song isn't present, it will return empty strings in the response.
Note that artistName
isn't required, it's only used for disambiguation.
You can leave artistName
as an empty string if you know the songName
is unique. If there are multiple matches for the song name, it will
pick one for you (which may not be the one you want).
Request:
curl --request POST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamIsExistServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"serialNo": "AB316238",
"isExist": [
{ "artistName": "SUPER☆GiRLS", "songName": "胸キュンLove Song" },
{ "artistName": "りさ、えいみ", "songName": "wake up my music" }
]
}'
Response:
{
"QRcode": "",
"appVer": "",
"cdmNo": "",
"deviceId": "",
"deviceNm": "",
"isExist": [
{
"artistId": "76729",
"artistName": "SUPER☆GiRLS",
"distEnd": "99999999",
"distStart": "20150815",
"firstBars": "キミがそっと打ち明けた",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"myKey": "0",
"orgKey": "0",
"reqNo": "489143",
"songName": "胸キュンLove Song"
},
{
"artistId": "",
"artistName": "りさ、えいみ",
"distEnd": "",
"distStart": "",
"firstBars": "",
"funcAnimePicture": "",
"funcPersonPicture": "",
"funcRecording": "",
"funcScore": "",
"myKey": "",
"orgKey": "",
"reqNo": "",
"songName": "wake up my music"
}
],
"osVer": "",
"reqNo": "",
"serialNo": ""
}
The app doesn't actually have this functionality, but I found it by messing around with the request params. If you know the song ID, you can find out which song matches it.
Request:
curl --request POST \
--url https://denmoku.clubdam.com/dkdenmoku/DkDamIsExistServlet \
--header 'content-type: application/json' \
--data '{
"appVer": "2.1.0",
"deviceId": "abcdef123456789",
"deviceNm": "cURL",
"osVer": "4.4.4",
"serialNo": "AB316238",
"isExist": [
{ "reqNo": "369073" }
]
}'
Response:
{
"QRcode": "",
"appVer": "",
"cdmNo": "",
"deviceId": "",
"deviceNm": "",
"isExist": [
{
"artistId": "19111",
"artistName": "すなお from STAR☆ANIS",
"distEnd": "99999999",
"distStart": "20140208",
"firstBars": "We wish you a merry Christmas,",
"funcAnimePicture": "0",
"funcPersonPicture": "0",
"funcRecording": "11",
"funcScore": "1",
"myKey": "0",
"orgKey": "0",
"reqNo": "369073",
"songName": "We wish you a merry Christmas AIKATSU! Ver."
}
],
"osVer": "",
"reqNo": "",
"serialNo": ""
}
In the app, when you go to a song page, it'll have a carousel of recommended (similar) songs. This uses a different endpoint:
POST https://csgw.clubdam.com/minsei/recommend/GetRecommendSongs.api
This one uses form params instead of JSON. I don't know what all of them mean, but the following are hardcoded into the app:
compId
:1
contractId
:1
compAuthKey
:2/Qb9R@8s*
format
:json
The following are specific to the request:
requestNoList
: Song ID (e.g., "3729-15")serial
: Same asserialNo
in the other endpoints (e.g., "AB316238")
Request:
curl --request POST \
--url https://csgw.clubdam.com/minsei/recommend/GetRecommendSongs.api \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'compId=1&contractId=1&compAuthKey=2%2FQb9R%408s*&format=json&requestNoList=3729-15&serial=AB316238'
Response:
{
"data": {
"recommendCount": "64",
"requestNo": [
"3729-15"
]
},
"list": [
{
"artist": "せな・りえ from AIKATSU☆STARS!",
"artistCode": "476404",
"contents": "スタートライン!",
"contentsId": "5616135",
"contentsYomi": "スタートライン",
"dArtistNameYomi": "セナリエフロムアイカツスターズ",
"dSongNameYomi": "スタートライン",
"damArtistCode": "117590",
"denmokuArtist": "せな・りえ from AIKATSU☆STARS!",
"denmokuContents": "スタートライン!",
"nameYomi": "セナリエフロムアイカツスターズ",
"requestNo": "3762-82"
},
{
"artist": "るか・せな from AIKATSU☆STARS!",
"artistCode": "477795",
"contents": "So Beautiful Story",
"contentsId": "5639199",
"contentsYomi": "ソービューティフルストーリー",
"dArtistNameYomi": "ルカセナフロムアイカツスターズ",
"dSongNameYomi": "ソービューティフルストーリー",
"damArtistCode": "121081",
"denmokuArtist": "るか・せな from AIKATSU☆STARS!",
"denmokuContents": "So Beautiful Story",
"nameYomi": "ルカセナフロムアイカツスターズ",
"requestNo": "3769-77"
},
// ... etc
],
"message": "",
"status": "OK",
"statusCode": "0000",
"type": "2.2"
}
The response includes the katakana pronunciation of the artist/title.