Libraries & Tools

TuneLab is a plain REST API, so it works with any HTTP client in any language. Official SDKs are coming Q3 2026. For now, we provide the OpenAPI spec, a Postman collection, and idiomatic code examples in every language you’re likely to use.

OpenAPI spec

The full API surface is published as an OpenAPI 3.1 document at /api/openapi.json. Every endpoint, parameter, request body, and response schema is described there — it’s the single source of truth for the API reference pages on this site.

download the spec
curl https://developers.tunelab.dev/api/openapi.json -o tunelab-api.json

Because it’s standard OpenAPI 3.1, you can feed it straight into any code generator — openapi-generator, Speakeasy, or Stainless will produce a typed client library in the language of your choice. You can also import the spec into Postman, Insomnia, Bruno, Hoppscotch, or any other API client that speaks OpenAPI.

Postman collection

The easiest way to explore the API interactively is to import the OpenAPI spec into Postman. In Postman, choose File → Import and paste the URL https://developers.tunelab.dev/api/openapi.json. Postman auto-generates example requests for every endpoint, complete with parameter descriptions, example values, and response schemas.

Use a Postman environment variable for your key.
Create a Postman Environment with a variable named api_key set to your tl_live_ or tl_test_ key, then reference it in the Authorization header as Bearer {{api_key}}. You'll only enter the key once and every request in the collection will use it.

The same approach works for Insomnia, Bruno, and Hoppscotch — all three support OpenAPI 3.1 import and environment variables.

HTTP client examples

Below is the same request — GET /v1/resolve to look up a track by song and artist — in every language we get asked about. All examples use the test key prefix tl_test_; swap it for your tl_live_ key when you’re ready to hit real compute.

cURL

cURL
curl https://api.tunelab.dev/v1/resolve \
  -G --data-urlencode "song=Get Lucky" \
      --data-urlencode "artist=Daft Punk" \
  -H "Authorization: Bearer tl_test_YOUR_KEY"

Python (requests)

Install: pip install requests

python
import requests

response = requests.get(
    "https://api.tunelab.dev/v1/resolve",
    params={"song": "Get Lucky", "artist": "Daft Punk"},
    headers={"Authorization": "Bearer tl_test_YOUR_KEY"},
    timeout=10,
)
response.raise_for_status()
track = response.json()
print(f"{track['artist']} — {track['title']}: {track['tempo']} BPM")

Python async (httpx)

Install: pip install httpx

python async
import asyncio
import httpx

async def resolve_track(song: str, artist: str) -> dict:
    async with httpx.AsyncClient(timeout=10.0) as client:
        response = await client.get(
            "https://api.tunelab.dev/v1/resolve",
            params={"song": song, "artist": artist},
            headers={"Authorization": "Bearer tl_test_YOUR_KEY"},
        )
        response.raise_for_status()
        return response.json()

track = asyncio.run(resolve_track("Get Lucky", "Daft Punk"))
print(f"{track['artist']} — {track['title']}: {track['tempo']} BPM")

JavaScript (Node.js 18+ / browser fetch)

javascript
const params = new URLSearchParams({
  song: "Get Lucky",
  artist: "Daft Punk",
});

const response = await fetch(
  `https://api.tunelab.dev/v1/resolve?${params}`,
  { headers: { Authorization: "Bearer tl_test_YOUR_KEY" } }
);

if (!response.ok) {
  throw new Error(`TuneLab API error: ${response.status}`);
}

const track = await response.json();
console.log(`${track.artist} — ${track.title}: ${track.tempo} BPM`);

TypeScript (typed)

typescript
interface ResolvedTrack {
  artist: string;
  title: string;
  tempo: number;
  key: string;
  mode: "major" | "minor";
  camelot: string;
  energy: number;
  danceability: number;
  _meta: {
    cache: "hit" | "miss";
    latency_ms: number;
    credits_used: number;
    credits_remaining: number;
    trace_id: string;
  };
}

async function resolveTrack(song: string, artist: string): Promise<ResolvedTrack> {
  const params = new URLSearchParams({ song, artist });
  const response = await fetch(
    `https://api.tunelab.dev/v1/resolve?${params}`,
    { headers: { Authorization: "Bearer tl_test_YOUR_KEY" } }
  );
  if (!response.ok) {
    throw new Error(`TuneLab API error: ${response.status}`);
  }
  return response.json() as Promise<ResolvedTrack>;
}

const track = await resolveTrack("Get Lucky", "Daft Punk");
console.log(`${track.artist} — ${track.title}: ${track.tempo} BPM`);

Go (net/http)

go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
)

type Track struct {
    Artist  string  `json:"artist"`
    Title   string  `json:"title"`
    Tempo   float64 `json:"tempo"`
    Key     string  `json:"key"`
    Mode    string  `json:"mode"`
    Camelot string  `json:"camelot"`
}

func main() {
    params := url.Values{}
    params.Set("song", "Get Lucky")
    params.Set("artist", "Daft Punk")

    req, _ := http.NewRequest("GET",
        "https://api.tunelab.dev/v1/resolve?"+params.Encode(), nil)
    req.Header.Set("Authorization", "Bearer tl_test_YOUR_KEY")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    var track Track
    if err := json.NewDecoder(resp.Body).Decode(&track); err != nil {
        panic(err)
    }
    fmt.Printf("%s — %s: %.2f BPM\n", track.Artist, track.Title, track.Tempo)
}

Ruby (Net::HTTP)

ruby
require "net/http"
require "json"
require "uri"

uri = URI("https://api.tunelab.dev/v1/resolve")
uri.query = URI.encode_www_form(song: "Get Lucky", artist: "Daft Punk")

request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer tl_test_YOUR_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

track = JSON.parse(response.body)
puts "#{track['artist']} — #{track['title']}: #{track['tempo']} BPM"

PHP (curl)

php
<?php
$params = http_build_query([
    "song"   => "Get Lucky",
    "artist" => "Daft Punk",
]);

$handle = curl_init("https://api.tunelab.dev/v1/resolve?" . $params);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer tl_test_YOUR_KEY",
]);

$response = curl_exec ($handle);
curl_close($handle);

$track = json_decode($response, true);
printf("%s — %s: %.2f BPM\n", $track["artist"], $track["title"], $track["tempo"]);

Rust (reqwest)

Add to Cargo.toml: reqwest = { version = "0.12", features = ["json"] } and tokio = { version = "1", features = ["full"] }

rust
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Track {
    artist: String,
    title: String,
    tempo: f64,
    key: String,
    mode: String,
    camelot: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let track: Track = client
        .get("https://api.tunelab.dev/v1/resolve")
        .query(&[("song", "Get Lucky"), ("artist", "Daft Punk")])
        .bearer_auth("tl_test_YOUR_KEY")
        .send()
        .await?
        .json()
        .await?;

    println!("{} — {}: {:.2} BPM", track.artist, track.title, track.tempo);
    Ok(())
}

Official SDKs (coming soon)

We’re building first-party SDKs that wrap authentication, retries, pagination, and async job polling so you don’t have to. They’ll be generated from the same OpenAPI spec linked above, with full type safety and IDE autocomplete.

Want early access?
Drop us a line at hello@tunelab.dev and tell us which language you need first. Early-access users help shape the SDK surface before we tag v1.0.

Community libraries

No community libraries yet. If you build one — a client library, a CLI, a plugin for your favourite DAW or DJ software, a Zapier integration — email hello@tunelab.dev and we’ll feature it here with a link back to your repo.

Developer tools

Testing tips

Use tl_test_* keys in CI and automated tests. They:

See Authentication for the full breakdown of test vs live keys, key rotation, and scoping.