Authentication

How to authenticate your requests

API Key Management

⚠️ API access is available only for business accounts.

API keys are managed in the API section of the YouHodler web application.

Operations

The following operations are available for API keys:

  • Create API Key — issue a new API key
  • Revoke API Key — disable an existing API key
  • Reactivate API Key — re-enable a previously revoked API key (a new key will be issued)

Secret

When an API key is successfully created, you will also receive a secret.

The secret is required for request signing and authentication. Detailed information about request signing is provided in the section below.

IP Whitelist (Optional)

You can optionally restrict API key usage by configuring an IP whitelist.

Both IPv4 and IPv6 addresses are supported.

Only requests originating from the whitelisted IP addresses will be accepted by the API.


API Key Authentication

Include your API key in the x-apikey HTTP header with every request:

curl -X GET "https://api.youhodler.com/v1/balance" \
  -H "x-apikey: YOUR_API_KEY"

Signing POST Requests

All POST requests must be signed with an Ed25519 signature. The signature is computed over the raw request body and sent in the x-signature header.

When you create API credentials, YouHodler returns an Ed25519 private key (PKCS#8 DER format, base64-encoded). Keep it secret — the server only stores the corresponding public key and cannot recover your private key.

Required Fields

Every POST request body must include:

FieldTypeDescription
timestampnumberCurrent time in milliseconds (Unix epoch)
recvWindownumber(optional) Maximum allowed delay in ms between timestamp and server time. Default: 5000, max: 60000

How to Sign

  1. Build the JSON request body including timestamp (and optionally recvWindow).
  2. Sign the raw JSON string (bytes) using your Ed25519 private key.
  3. Base64-encode the 64-byte Ed25519 signature.
  4. Send the result in the x-signature header.

Examples

Java

import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

String privateKeyBase64 = "YOUR_PRIVATE_KEY_BASE64"; // PKCS#8 DER, base64
String apiKey = "YOUR_API_KEY";

long timestamp = System.currentTimeMillis();
String body = "{\"fromTicker\":\"btc\",\"toTicker\":\"usd\",\"fromAmount\":\"0.1\",\"timestamp\":" + timestamp + "}";

// Load Ed25519 private key
byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("Ed25519");
PrivateKey privateKey = kf.generatePrivate(keySpec);

// Sign the body
Signature signer = Signature.getInstance("Ed25519");
signer.initSign(privateKey);
signer.update(body.getBytes(StandardCharsets.UTF_8));
String signatureBase64 = Base64.getEncoder().encodeToString(signer.sign());

// Send the request
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.youhodler.com/v1/convert/getQuote"))
    .header("x-apikey", apiKey)
    .header("x-signature", signatureBase64)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());

Note: Ed25519 support requires Java 15+.

Node.js

const crypto = require("node:crypto");

const privateKeyBase64 = "YOUR_PRIVATE_KEY_BASE64"; // PKCS#8 DER, base64
const apiKey = "YOUR_API_KEY";

const body = JSON.stringify({
  fromTicker: "btc",
  toTicker: "usd",
  fromAmount: "0.1",
  timestamp: Date.now(),
});

const privateKey = crypto.createPrivateKey({
  key: Buffer.from(privateKeyBase64, "base64"),
  format: "der",
  type: "pkcs8",
});

const signature = crypto.sign(null, Buffer.from(body, "utf8"), privateKey);
const signatureBase64 = signature.toString("base64");

// Use signatureBase64 as the x-signature header value

Python

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import load_der_private_key
import base64, json, time, requests

private_key_b64 = "YOUR_PRIVATE_KEY_BASE64"  # PKCS#8 DER, base64

private_key = load_der_private_key(base64.b64decode(private_key_b64), password=None)

body = json.dumps({
    "fromTicker": "btc",
    "toTicker": "usd",
    "fromAmount": "0.1",
    "timestamp": int(time.time() * 1000),
})

signature = private_key.sign(body.encode("utf-8"))
signature_b64 = base64.b64encode(signature).decode()

resp = requests.post(
    "https://api.youhodler.com/v1/convert/getQuote",
    headers={
        "x-apikey": "YOUR_API_KEY",
        "x-signature": signature_b64,
        "Content-Type": "application/json",
    },
    data=body,
)

Signature Errors

HTTP StatusError CodeError LabelDescription
4019008MISSING_SIGNATUREThe x-signature header is missing on a POST request
4019009INVALID_SIGNATUREThe signature does not match
4011001INVALID_TIMESTAMPThe timestamp is missing or the request has expired

Authentication Errors

If authentication fails, the API returns one of the following errors:

HTTP StatusError CodeError LabelDescription
4019006MISSING_API_KEYThe x-apikey header was not provided
4019007INVALID_API_KEYThe API key is invalid or has been revoked
4019001UNAUTHORIZEDGeneral authorization failure
4039012INVALID_IPRequest IP is not in the allowlist
4039013KYC_NOT_VERIFIEDKYC verification not completed
4039014API_NOT_AVAILABLEAPI access is not available for this account

Example error response:

{
  "errorCode": 9006,
  "errorLabel": "MISSING_API_KEY",
  "errorDescription": "Missing X-APIKEY in headers"
}

Security Best Practices

  • Never expose your API key or private key in client-side code or public repositories.
  • Keep the Ed25519 private key secure — it cannot be recovered from the server.
  • Rotate your API credentials periodically through your account manager.
  • Use IP allowlisting to restrict which IPs can use your API key.
  • Always include a timestamp in POST requests and keep recvWindow as small as practical to limit replay attacks.