Quick Start
Buy BTC through the True Markets Retail API.
Prerequisites
Complete Onboarding to create an account, fund it, and download an API key bundle. With TM_KEY_FILE set in your environment, you're ready to continue.
Step 4 ("Place the order") sends a real market order that spends real funds. Stop after step 3 (the quote) on the first run to confirm everything works.
The fastest way to unblock is the True Markets Discord — community and team hang out there for integration help. For account or network access issues, email [email protected].
1. Sign your request and get a JWT
Sign {key_id}.{timestamp} with ES256, then POST to /v1/auth/api-key/token to receive a short-lived access_token.
Signing is language-specific cryptography. See the Auth Service API reference.
- Node.js
- Python
import { readFileSync } from "node:fs";
import { createPrivateKey, sign } from "node:crypto";
async function getToken() {
const { key_id, private_key: jwk } = JSON.parse(
readFileSync(process.env.TM_KEY_FILE, "utf8"),
);
const key = createPrivateKey({ key: jwk, format: "jwk" });
const timestamp = Math.floor(Date.now() / 1000);
const signature = sign(
"SHA256",
Buffer.from(`${key_id}.${timestamp}`),
{ key, dsaEncoding: "ieee-p1363" },
).toString("base64url");
const { access_token } = await (await fetch(
"https://api.truemarkets.co/v1/auth/api-key/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key_id, timestamp, signature }),
},
)).json();
return access_token;
}
const token = await getToken();
import base64, json, os, time
from pathlib import Path
import requests
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
def get_token() -> str:
bundle = json.loads(Path(os.environ["TM_KEY_FILE"]).expanduser().read_text())
key_id, jwk = bundle["key_id"], bundle["private_key"]
d = int.from_bytes(base64.urlsafe_b64decode(jwk["d"] + "=="), "big")
key = ec.derive_private_key(d, ec.SECP256R1())
ts = int(time.time())
der = key.sign(f"{key_id}.{ts}".encode(), ec.ECDSA(hashes.SHA256()))
r, s = decode_dss_signature(der)
sig = base64.urlsafe_b64encode(
r.to_bytes(32, "big") + s.to_bytes(32, "big")
).rstrip(b"=").decode()
return requests.post(
"https://api.truemarkets.co/v1/auth/api-key/token",
json={"key_id": key_id, "timestamp": ts, "signature": sig},
).json()["access_token"]
token = get_token()
The token is short-lived. For a production app, see Refresh access token. The remaining steps assume TOKEN is set in your environment.
2. Get tradable assets
Fetch the catalog of assets available across CeFi and DeFi venues. This endpoint is public, so no token is required.
Limit orders are supported only on the CeFi assets returned by this endpoint. Pass venue=cefi to filter to that subset, venue=defi for the DeFi catalog, or omit venue to get both.
- cURL
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
curl "https://api.truemarkets.co/v1/conductor/assets?venue=cefi"
const assets = await (await fetch(
"https://api.truemarkets.co/v1/conductor/assets?venue=cefi",
)).json();
import requests
assets = requests.get(
"https://api.truemarkets.co/v1/conductor/assets",
params={"venue": "cefi"},
).json()
HttpResponse<String> response = HttpClient.newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("https://api.truemarkets.co/v1/conductor/assets?venue=cefi"))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString());
using System.Net.Http;
using System.Net.Http.Json;
var http = new HttpClient();
var assets = await http.GetFromJsonAsync<object>(
"https://api.truemarkets.co/v1/conductor/assets?venue=cefi");
<?php
$ch = curl_init('https://api.truemarkets.co/v1/conductor/assets?venue=cefi');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$assets = json_decode(curl_exec($ch), true);
package main
import (
"io"
"net/http"
)
res, _ := http.Get("https://api.truemarkets.co/v1/conductor/assets?venue=cefi")
defer res.Body.Close()
assets, _ := io.ReadAll(res.Body)
require 'json'; require 'net/http'; require 'uri'
uri = URI('https://api.truemarkets.co/v1/conductor/assets')
uri.query = URI.encode_www_form(venue: 'cefi')
res = Net::HTTP.get_response(uri)
assets = JSON.parse(res.body)
library(httr); library(jsonlite)
assets <- GET("https://api.truemarkets.co/v1/conductor/assets",
query = list(venue = "cefi"))
content(assets)
3. Get a trade quote
Fetch a quote without committing. qty_unit: "quote" means "$5 of BTC"; use "base" to specify a BTC amount instead.
- cURL
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
curl -X POST https://api.truemarkets.co/v1/conductor/quotes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy"
}'
const quote = await (await fetch("https://api.truemarkets.co/v1/conductor/quotes", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
base_asset: "BTC",
quote_asset: "USDC",
qty: "5",
qty_unit: "quote",
side: "buy",
}),
})).json();
import os, requests
quote = requests.post(
"https://api.truemarkets.co/v1/conductor/quotes",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy",
},
).json()
String body = """
{
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy"
}""";
HttpResponse<String> response = HttpClient.newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("https://api.truemarkets.co/v1/conductor/quotes"))
.header("Authorization", "Bearer " + System.getenv("TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build(),
HttpResponse.BodyHandlers.ofString());
using System.Net.Http;
using System.Net.Http.Json;
var token = Environment.GetEnvironmentVariable("TOKEN");
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var quote = await http.PostAsJsonAsync(
"https://api.truemarkets.co/v1/conductor/quotes",
new {
base_asset = "BTC",
quote_asset = "USDC",
qty = "5",
qty_unit = "quote",
side = "buy",
});
<?php
$ch = curl_init('https://api.truemarkets.co/v1/conductor/quotes');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'base_asset' => 'BTC',
'quote_asset' => 'USDC',
'qty' => '5',
'qty_unit' => 'quote',
'side' => 'buy',
]),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TOKEN'),
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
]);
$quote = json_decode(curl_exec($ch), true);
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
body, _ := json.Marshal(map[string]string{
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy",
})
req, _ := http.NewRequest("POST",
"https://api.truemarkets.co/v1/conductor/quotes",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
quote, _ := io.ReadAll(res.Body)
require 'json'; require 'net/http'; require 'uri'
uri = URI('https://api.truemarkets.co/v1/conductor/quotes')
req = Net::HTTP::Post.new(uri,
'Authorization' => "Bearer #{ENV['TOKEN']}",
'Content-Type' => 'application/json')
req.body = JSON.generate(
base_asset: 'BTC', quote_asset: 'USDC',
qty: '5', qty_unit: 'quote', side: 'buy')
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
quote = JSON.parse(res.body)
library(httr); library(jsonlite)
quote <- POST("https://api.truemarkets.co/v1/conductor/quotes",
add_headers(Authorization = paste("Bearer", Sys.getenv("TOKEN"))),
body = toJSON(list(
base_asset = "BTC",
quote_asset = "USDC",
qty = "5",
qty_unit = "quote",
side = "buy"
), auto_unbox = TRUE),
content_type_json())
content(quote)
4. Place the order
Same shape as the quote, plus type: "market". Returns an order_id to track status.
Steps 1–3 only read. The request below moves real funds.
- cURL
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
# capture the order_id from the response with jq
ORDER_ID=$(curl -s -X POST https://api.truemarkets.co/v1/conductor/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy",
"type": "market"
}' | jq -r '.order_id')
const order = await (await fetch("https://api.truemarkets.co/v1/conductor/orders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
base_asset: "BTC",
quote_asset: "USDC",
qty: "5",
qty_unit: "quote",
side: "buy",
type: "market",
}),
})).json();
const orderId = order.order_id;
order = requests.post(
"https://api.truemarkets.co/v1/conductor/orders",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
json={
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy",
"type": "market",
},
).json()
order_id = order["order_id"]
String body = """
{
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy",
"type": "market"
}""";
HttpResponse<String> response = HttpClient.newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("https://api.truemarkets.co/v1/conductor/orders"))
.header("Authorization", "Bearer " + System.getenv("TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build(),
HttpResponse.BodyHandlers.ofString());
// parse the JSON body with your preferred library, e.g. org.json:
String orderId = new org.json.JSONObject(response.body()).getString("order_id");
var response = await http.PostAsJsonAsync(
"https://api.truemarkets.co/v1/conductor/orders",
new {
base_asset = "BTC",
quote_asset = "USDC",
qty = "5",
qty_unit = "quote",
side = "buy",
type = "market",
});
var order = await response.Content.ReadFromJsonAsync<Dictionary<string, object>>();
var orderId = order["order_id"];
<?php
$ch = curl_init('https://api.truemarkets.co/v1/conductor/orders');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'base_asset' => 'BTC',
'quote_asset' => 'USDC',
'qty' => '5',
'qty_unit' => 'quote',
'side' => 'buy',
'type' => 'market',
]),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('TOKEN'),
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
]);
$order = json_decode(curl_exec($ch), true);
$orderId = $order['order_id'];
body, _ := json.Marshal(map[string]string{
"base_asset": "BTC",
"quote_asset": "USDC",
"qty": "5",
"qty_unit": "quote",
"side": "buy",
"type": "market",
})
req, _ := http.NewRequest("POST",
"https://api.truemarkets.co/v1/conductor/orders",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TOKEN"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var order struct {
OrderID string `json:"order_id"`
}
json.NewDecoder(res.Body).Decode(&order)
orderId := order.OrderID
uri = URI('https://api.truemarkets.co/v1/conductor/orders')
req = Net::HTTP::Post.new(uri,
'Authorization' => "Bearer #{ENV['TOKEN']}",
'Content-Type' => 'application/json')
req.body = JSON.generate(
base_asset: 'BTC', quote_asset: 'USDC',
qty: '5', qty_unit: 'quote', side: 'buy', type: 'market')
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
order = JSON.parse(res.body)
order_id = order['order_id']
order <- POST("https://api.truemarkets.co/v1/conductor/orders",
add_headers(Authorization = paste("Bearer", Sys.getenv("TOKEN"))),
body = toJSON(list(
base_asset = "BTC",
quote_asset = "USDC",
qty = "5",
qty_unit = "quote",
side = "buy",
type = "market"
), auto_unbox = TRUE),
content_type_json())
order_id <- content(order)$order_id
Each snippet above captures the order_id from the response into a variable
(order_id / orderId / $ORDER_ID) so the next step can track the order.
5. Verify the trade
Track the order with the order_id captured in step 4.
- cURL
- Node.js
- Python
- Java
- C#
- PHP
- Go
- Ruby
- R
curl https://api.truemarkets.co/v1/conductor/orders/$ORDER_ID/status \
-H "Authorization: Bearer $TOKEN"
const status = await (await fetch(
`https://api.truemarkets.co/v1/conductor/orders/${orderId}/status`,
{ headers: { Authorization: `Bearer ${process.env.TOKEN}` } },
)).json();
status = requests.get(
f"https://api.truemarkets.co/v1/conductor/orders/{order_id}/status",
headers={"Authorization": f"Bearer {os.environ['TOKEN']}"},
).json()
HttpResponse<String> response = HttpClient.newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create(
"https://api.truemarkets.co/v1/conductor/orders/"
+ orderId + "/status"))
.header("Authorization", "Bearer " + System.getenv("TOKEN"))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString());
var status = await http.GetFromJsonAsync<object>(
$"https://api.truemarkets.co/v1/conductor/orders/{orderId}/status");
<?php
$ch = curl_init("https://api.truemarkets.co/v1/conductor/orders/$orderId/status");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('TOKEN')],
CURLOPT_RETURNTRANSFER => true,
]);
$status = json_decode(curl_exec($ch), true);
url := "https://api.truemarkets.co/v1/conductor/orders/" + orderId + "/status"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TOKEN"))
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
status, _ := io.ReadAll(res.Body)
uri = URI("https://api.truemarkets.co/v1/conductor/orders/#{order_id}/status")
req = Net::HTTP::Get.new(uri,
'Authorization' => "Bearer #{ENV['TOKEN']}")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
status = JSON.parse(res.body)
status <- GET(
paste0("https://api.truemarkets.co/v1/conductor/orders/",
order_id, "/status"),
add_headers(Authorization = paste("Bearer", Sys.getenv("TOKEN"))))
content(status)
Next steps
- Retail API reference — every endpoint, schema, and error code.
- Auth Service API — refresh tokens, additional auth methods (passkey, magic link, SSO).
- True Markets 101 — when to pick the Institutional API over the Retail API.
- Join the Discord — chat with the team and other integrators.