Quick Start
Make your first authenticated API call to the CoinMENA Partner API in about 5 minutes.
This guide walks you through your first API call to CoinMENA — from connectivity check to a fully signed, authenticated request. Estimated time: ~5 minutes.
By the end of this guide, you will:
- Confirm your connection to the CoinMENA Partner API
- Understand the three headers required for authenticated requests
- Make your first signed request and receive a valid response
Examples target the production environmentAll examples in this guide use the production base URL:
https://external-api.coinmena.com. To run them against the sandbox instead, contact [email protected] for sandbox credentials and replace the base URL in the code samples.
Prerequisites
Before you start, make sure you have:
| Requirement | Details |
|---|---|
| Environment | Production: https://external-api.coinmena.comSandbox: contact [email protected] for credentials |
| Partner ID | Provided by CoinMENA after your partner account is created |
| Ed25519 private key | Generated on your side — your public key must be shared with CoinMENA before making signed requests |
Placeholders in the examples belowReplace
your_partner_idwith your actual Partner ID, and<your_generated_signature>with the Base64 signature generated by the Python or Node.js script in Step 2.
Step 1: Your First Public API Call
Confirm connectivity — no authentication required.
Public endpoints do not require signing. Let's start by fetching the list of supported assets to confirm you can reach the API.
curl https://external-api.coinmena.com/v1/public/assetsResponse — 200 OK
{
"items": [
{
"id": "BTC",
"type": "crypto",
"name_en": "Bitcoin",
"decimal_places": 8
},
{
"id": "USD",
"type": "fiat",
"name_en": "US Dollar",
"decimal_places": 2
}
]
}If you see a valid response with an items array, you are connected to the API and ready for authenticated requests.
Step 2: Your First Authenticated API Call
Sign a request and call a partner endpoint.
All authenticated requests must be signed using your Ed25519 private key. An invalid, missing, or expired signature results in a 401 Unauthorized response.
The three required headers
Every signed request must include:
| Header | Value |
|---|---|
X-Partner-ID | Your partner identifier |
X-Timestamp | Current Unix timestamp in milliseconds |
X-Signature | Base64-encoded Ed25519 signature of the canonical string |
The examples below show how to generate these headers and call GET /v1/partner/asset-pairs — a good choice for your first authenticated call because it is read-only, requires no prior setup, and confirms that your partner account is active and your signature is valid.
Language examples
Pick the language that matches your stack. All three examples do exactly the same thing.
cURL
curl -X GET "https://external-api.coinmena.com/v1/partner/asset-pairs" \
-H "X-Partner-ID: your_partner_id" \
-H "X-Timestamp: 1737654321000" \
-H "X-Signature: <your_generated_signature>"
The cURL example assumes the signature has already been generated. Use the Python or Node.js examples below to compute it programmatically, then paste the output into the cURL command.
Python
import time
import hashlib
import base64
import requests
from cryptography.hazmat.primitives.serialization import load_pem_private_key
with open("private_key.pem", "rb") as f:
private_key = load_pem_private_key(f.read(), password=None)
# Build the canonical string: {TIMESTAMP}{METHOD}{PATH}{SHA256_BODY_HASH}
timestamp = str(int(time.time() * 1000))
method = "GET"
path = "/v1/partner/asset-pairs"
body = ""
body_hash = hashlib.sha256(body.encode("utf-8")).hexdigest()
canonical = f"{timestamp}{method}{path}{body_hash}"
# Sign and Base64-encode the canonical string
signature_bytes = private_key.sign(canonical.encode("utf-8"))
signature = base64.b64encode(signature_bytes).decode()
headers = {
"X-Partner-ID": "your_partner_id",
"X-Timestamp": timestamp,
"X-Signature": signature,
"Accept": "application/json"
}
# Send the signed request
response = requests.get(
"https://external-api.coinmena.com/v1/partner/asset-pairs",
headers=headers
)
print(response.json())Node.js (v18+)
import crypto from "crypto";
import fs from "fs";
const privateKey = crypto.createPrivateKey(fs.readFileSync("private_key.pem", "utf8"));
// Build the canonical string: {TIMESTAMP}{METHOD}{PATH}{SHA256_BODY_HASH}
const timestamp = Date.now().toString();
const method = "GET";
const path = "/v1/partner/asset-pairs";
const body = "";
const bodyHash = crypto.createHash("sha256").update(Buffer.from(body, "utf8")).digest("hex");
const canonical = `${timestamp}${method}${path}${bodyHash}`;
// Sign and Base64-encode the canonical string
const signatureBuffer = crypto.sign(null, Buffer.from(canonical, "utf8"), privateKey);
const signature = signatureBuffer.toString("base64");
// Send the signed request using the built-in fetch API (Node.js 18+)
const response = await fetch("https://external-api.coinmena.com/v1/partner/asset-pairs", {
method: "GET",
headers: {
"X-Partner-ID": "your_partner_id",
"X-Timestamp": timestamp,
"X-Signature": signature,
"Accept": "application/json"
}
});
const data = await response.json();
console.log(data);What success looks like
A successful response returns a 200 OK with a list of asset pairs:
Response — 200 OK
{
"items": [
{
"id": "BTC-USD",
"base_id": "BTC",
"quote_id": "USD",
"buy": "67550.75",
"sell": "67450.25",
"tradable": true
}
]
}If you see items in the response, your signature validated successfully — authentication is working.
Got a401 Unauthorized?A
401means your signature, timestamp, or headers were rejected by the server. See the Authentication guide's troubleshooting section for a systematic debugging checklist.
Ready to Go Deeper?
The examples above show the minimum needed to get started. For the full signing rules, canonical string breakdown, edge cases, and a detailed troubleshooting checklist — see the Authentication guide.
Updated 2 days ago
| Next Step | Description |
|---|---|
| Authentication | Full breakdown of the signing algorithm, rules, and troubleshooting |
| IP Whitelisting | Add a second security layer to your integration |
| Error Codes | Reference for every error the API returns |
