Authentication
PAX uses Ed25519 public key signatures to authenticate API requests. Your private key never leaves your machine. PAX only stores your public key and verifies signatures server-side.
Create an API key
Section titled “Create an API key”Log in to PAX (or sandbox), navigate to Settings, and click Create Key and choose permissions.
Generate an Ed25519 keypair locally and paste your public key (PEM-encoded) into the form. Your private key is never sent to PAX. After creation, PAX returns a key ID (e.g. pax_live_a3b4c5d6e7f8) that identifies your key in API requests.
This script saves the private key to my-private-key.pem, readable only by you, and prints the public key to paste:
import osfrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKeyfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat, PrivateFormat, NoEncryption
private_key = Ed25519PrivateKey.generate()with open(os.open("my-private-key.pem", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), "wb") as f: f.write(private_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()))print(private_key.public_key().public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode())Signing requests
Section titled “Signing requests”Every authenticated request requires three headers:
| Header | Value |
|---|---|
api-access-key |
Your key ID |
api-timestamp |
Current Unix timestamp in milliseconds |
api-signature |
Base64-encoded Ed25519 signature |
The signature message is the concatenation of four components with no separator:
timestamp + method + path + bodyWhere timestamp matches the header value, method is uppercase (e.g. POST), path is the request path with its query string, percent-encoded exactly as sent (e.g. /v1/order/submit?recvWindow=20000), and body is the request body exactly as sent, byte for byte. Use an empty string as the body for GET and DELETE requests.
Example
Section titled “Example”import time, json, base64, requestsfrom cryptography.hazmat.primitives.serialization import load_pem_private_key
# Load your private key and key ID (from key creation step)private_key = load_pem_private_key(open("my-private-key.pem", "rb").read(), password=None)key_id = "pax_test_a3b4c5d6e7f8"
timestamp = str(int(time.time() * 1000))method = "POST"path = "/v1/order/submit"body = json.dumps({"order": {"side": 2, "market_id": "1", "qty": "10000", "ticks": 50000, "tif": 2}})
message = f"{timestamp}{method}{path}{body}"signature = base64.b64encode(private_key.sign(message.encode())).decode("ascii")
headers = { "api-access-key": key_id, "api-timestamp": timestamp, "api-signature": signature, "Content-Type": "application/json",}response = requests.post(f"https://api-sandbox.pax.markets{path}", data=body, headers=headers)Timestamp validation
Section titled “Timestamp validation”PAX checks api-timestamp so a signed request is only valid briefly. Timestamps must not be more than 1 second in the future or older than the recvWindow (default 5000 ms, maximum 60000 ms). Override the default by adding recvWindow as a query parameter:
POST /v1/order/submit?recvWindow=20000Regulated services provided by 1Money USA, Inc., a licensed money transmitter, NMLS ID 2628653 · Licenses