Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hitenpatel/songprint/llms.txt

Use this file to discover all available pages before exploring further.

RecognitionClient is a thin, dependency-free HTTP client that POSTs a binary signature to the public Shazam discovery endpoint and returns the raw JSON response. It deliberately carries no JSON parsing dependency — use whatever JSON library your project already uses to decode the response. The entire implementation relies only on java.net.HttpURLConnection, making it compatible with any JVM or Android project without additional transitive dependencies.

Class declaration

class RecognitionClient(
    private val endpoint: String = DEFAULT_ENDPOINT,
    private val connectTimeoutMs: Int = 10_000,
    private val readTimeoutMs: Int = 10_000,
)

Constructor parameters

endpoint
String
default:"DEFAULT_ENDPOINT"
The discovery endpoint URL. Defaults to DEFAULT_ENDPOINT. Override this when writing tests against a local stub server, or if the default URL changes.
connectTimeoutMs
Int
default:"10000"
Connection timeout in milliseconds passed to HttpURLConnection. Default: 10_000 (10 seconds). Increase on flaky or high-latency networks.
readTimeoutMs
Int
default:"10000"
Read timeout in milliseconds passed to HttpURLConnection. Default: 10_000 (10 seconds). The Shazam endpoint typically responds in under two seconds on a good connection.

Methods

recognise

fun recognise(signature: ByteArray, numSamples: Int): String?
POSTs the supplied binary signature to the discovery endpoint and returns the raw JSON response body.
signature
ByteArray
required
The binary fingerprint produced by SignatureGenerator.generateSignature. It is base64-encoded and embedded in the JSON request body as a data URI.
numSamples
Int
required
The number of 16kHz mono samples the signature was generated from — i.e. mono16k.size. Used to compute the samplems field in the request body, which tells the endpoint how much audio was analysed.
Returns String? — the raw JSON response body when the server responds with HTTP 200. Returns null on any non-200 status code. Network errors (connection timeout, read timeout, DNS failure, etc.) propagate as thrown IOException exceptions — they are not silently converted to null.

Request body structure

The client constructs the following JSON payload. The signature bytes are base64-encoded and embedded as a data: URI; samplems is derived from numSamples / SAMPLE_RATE * 1000.
{
  "signature": {
    "uri": "data:audio/vnd.shazam.sig;base64,<base64-encoded signature bytes>",
    "samplems": 12000
  },
  "timestamp": 1718000000000,
  "context": {},
  "geolocation": {}
}

Response structure

The endpoint returns a JSON object. The key fields you will typically read are:
FieldTypeDescription
matchesarrayList of match objects. An empty array means no song was identified.
track.titlestringSong title.
track.subtitlestringArtist name.
track.images.coverartstringURL of the album cover art image.
track.share.hrefstringLink to the track on Shazam / Apple Music.
A minimal response for a successful identification looks like:
{
  "matches": [{ "id": "...", "offset": 42.3, "timeskew": 0.0, "frequencyskew": 0.0 }],
  "location": { "accuracy": 0.01 },
  "timestamp": 1718000000000,
  "timezone": "Europe/London",
  "track": {
    "layout": "5",
    "type": "MUSIC",
    "key": "12345678",
    "title": "Bohemian Rhapsody",
    "subtitle": "Queen",
    "images": {
      "coverart": "https://is1-ssl.mzstatic.com/image/thumb/.../300x300bb.jpg"
    },
    "share": {
      "href": "https://www.shazam.com/track/12345678/bohemian-rhapsody"
    }
  }
}
When no song is identified, matches is empty and the track key is absent.

Usage example

import io.github.hitenpatel.songprint.Pcm
import io.github.hitenpatel.songprint.RecognitionClient
import io.github.hitenpatel.songprint.SignatureGenerator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

// Build the signature from recorded audio
val samples = Pcm.bytesToShorts(pcmBytes)
val mono16k = Pcm.resampleToMono16k(samples, sampleRate = 44100, channels = 2)
val signature = SignatureGenerator().generateSignature(mono16k)

// Send to the discovery endpoint — must be off the main thread
val json: String? = withContext(Dispatchers.IO) {
    RecognitionClient().recognise(signature, numSamples = mono16k.size)
}

if (json != null) {
    // Parse with your preferred JSON library, e.g. kotlinx.serialization or Gson
    println(json)
} else {
    // null means a non-200 HTTP response; a no-match is a 200 with an empty `matches` array
    println("HTTP error — non-200 response")
}
To customise timeouts:
val client = RecognitionClient(
    connectTimeoutMs = 5_000,
    readTimeoutMs = 15_000,
)
val json = client.recognise(signature, mono16k.size)

Companion object

ConstantValue
DEFAULT_ENDPOINT"https://amp.shazam.com/discovery/v5/en/GB/android/-/tag"
Two random UUIDs are appended as path segments on each request (matching the pattern used by the official Shazam Android client): $DEFAULT_ENDPOINT/{uuid1}/{uuid2}?sync=true&webv3=true&...
This endpoint is not an official Shazam API. It is undocumented and reverse-engineered from client traffic. It has been stable for several years and is used by multiple open-source projects, but it can change or disappear without notice. Do not use it for high-volume or commercial applications, and be considerate with request rates.
recognise makes a blocking network call using java.net.HttpURLConnection. On Android, always call it from a coroutine with Dispatchers.IO or from a background thread — never from the main thread.

Build docs developers (and LLMs) love