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.
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.
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 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
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
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)
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)
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)
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)
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
$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"] }
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.
- Python SDK —
pip install tunelab— Q3 2026 - JavaScript/TypeScript SDK —
npm install tunelab— Q3 2026
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
- Dashboard — platform.tunelab.dev — manage API keys, billing, webhooks, and usage analytics.
-
Status page —
developers.tunelab.dev/status— real-time uptime for the API, resolution cascade, and GPU queue (coming soon). - Changelog — developers.tunelab.dev/changelog — every new endpoint, field, and breaking change, oldest-first.
Testing tips
Use tl_test_* keys in CI and automated tests. They:
- Return deterministic mock responses — same input, same output, every time.
- Don’t charge credits, so you can hit them from every PR without burning your balance.
- Are capped at 10 req/min, which is plenty for even large test suites.
- Still return the full
_metaenvelope, so you can assert ontrace_id,latency_ms, and cache behaviour.
See Authentication for the full breakdown of test vs live keys, key rotation, and scoping.