PayAny Energy
My Wallets
API reference
OverviewAuthenticationPrice and termsOrdersOrder statusAccountAddresses and quick rentWebhooksErrorsExamples

Authentication

Every key works in exactly one mode, chosen when you create it. A request made in the other mode is refused with 45210. Webhooks are always HMAC-signed with the key's secret, whichever mode the key uses.

Bearer#

Send the access id and the secret, joined by a dot:

Authorization: Bearer <access_id>.<secret>
Shell
curl https://energy.payany.com/v1/account \
  -H "Authorization: Bearer ek_0123456789abcdef0123.Zq3…"

Simple, but the secret travels with every request. Fine for a quick integration; prefer HMAC in production.

HMAC#

The secret never leaves your server. Send four headers:

HeaderValue
X-Access-Idyour access_id
X-Timestampcurrent Unix time in seconds (must be within ±300 s of ours)
X-Noncea fresh random string, 8–64 chars of A-Z a-z 0-9 _ -, never reused
X-Signaturehex(HMAC_SHA256(secret, string_to_sign))

The string to sign is five parts joined by a newline (\n):

string_to_sign = X-Timestamp + "\n" + X-Nonce + "\n" + METHOD + "\n" + path_with_query + "\n" + raw_body
  • METHOD upper-case (GET, POST, PATCH, DELETE).
  • path_with_query exactly as you send it, without the host: /v1/price?energy=65000&term=1h. When there is no query string, it is just the path (/v1/orders).
  • raw_body the exact bytes of the body — an empty string for GET. Serialise your JSON once, sign those bytes, send those bytes. Any JSON serialiser works because the signature is over bytes.
  • The signature is hex and compared case-insensitively.
  • A nonce can be used once per key within 10 minutes (45205 otherwise). It is only consumed after the signature matched.

Test vectors#

Secret test-secret, timestamp 1760000000. Use these two vectors to unit-test your signer.

GET with a query string — nonce n0nce-123:

GET /v1/price?energy=65000&term=1h

string_to_sign = "1760000000\nn0nce-123\nGET\n/v1/price?energy=65000&term=1h\n"
X-Signature    = afd6a1001db2471c8fabd9e5b86be6154ffc2c5843b9918e6378a030887e20d9

POST with a body — nonce n0nce-124:

POST /v1/orders

body           = {"external_id":"inv-1001","energy_address":"TLsV52sRDL79HXGGm9yzwKibb6BeruhUzy","energy":65000,"term":"1h"}
string_to_sign = "1760000000\nn0nce-124\nPOST\n/v1/orders\n" + body
X-Signature    = 7244351749440bda013f07fb44fd9e04109fb1ffac1288a75bae5c65eb3566cd

Note the trailing \n after the path even when the body is empty.

Signing in a few lines#

Python
import hashlib, hmac, secrets, time

ts, nonce = str(int(time.time())), secrets.token_hex(16)
msg = f"{ts}\n{nonce}\nGET\n/v1/account\n".encode() + b""
sig = hmac.new(SECRET.encode(), msg, hashlib.sha256).hexdigest()
Shell
TS=$(date +%s); NONCE=$(openssl rand -hex 16)
SIG=$(printf '%s\n%s\nGET\n/v1/account\n' "$TS" "$NONCE" \
  | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl https://energy.payany.com/v1/account \
  -H "X-Access-Id: $ACCESS_ID" -H "X-Timestamp: $TS" \
  -H "X-Nonce: $NONCE" -H "X-Signature: $SIG"

Complete clients: Examples.

Debugging a signature#

POST /v1/auth/echo with your usual headers (any body) never fails on a bad signature — it always answers HTTP 200 and tells you what it saw:

FieldMeaning
modeHmac, Bearer or null — what the request looked like
access_id, key_found, key_auth_mode, key_activethe key the request named
method, path_with_query, body_length, body_sha256what we received
timestamp, server_time, timestamp_okthe clock check
nonce, nonce_format_okthe nonce check
string_to_signthe exact string we signed
signature_matchedHMAC: the signature matched · Bearer: the secret matched
client_ip, ip_allowedyour IP as we see it, and whether the key's allow-list admits it

Compare its string_to_sign with yours byte by byte. The expected signature is never returned. Echo does not consume the nonce and does not count against the key's rate limit (it is limited to 60 calls a minute per IP).

Checks, in order#

  1. Credentials present (45201) → the key exists and the secret / signature is right (45202, 45203, 45204, 45205, all HTTP 401) → the key is active and not expired (45208, 403) → your account is not frozen (46003, 403).
  2. IP allow-list — when the key has one, calls from any other IP get 45206 (403). Entries are exact IPv4/IPv6 addresses or CIDR ranges. An empty list allows every IP.
  3. Rate limit — 600 calls per minute per key by default (45209, HTTP 429). Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. Need more? Ask support.
  4. Permissions — Read for every GET, Order for everything that creates or changes something (45207, 403).
PreviousOverviewNextPrice and terms
PayAny Energy

TRON energy for USDT transfers — a PayAny product.

Scan to download the app
Product
  • Buy Energy
  • My Wallets
  • Pricing
  • Find an Order
  • Telegram Bot ↗
Developers
  • API Docs
  • API Keys
Company
  • FAQ
  • Terms of Service
  • Privacy Policy
  • Delete your account
  • PayAny.com ↗
© 2026 PayAny. All rights reserved.Energy is delegated on the TRON network; delivery times depend on network confirmation.