tgpay cryptoAPI
crypto-payapireferencetokens

Merchant API reference

5 min readUpdated Aug 22, 2026

The conventions every method shares, plus the read-only catalog methods. Method-by-method pages: invoices and refunds, transfers and checks, subscriptions, webhooks.

The API is Crypto Bot-compatible: an existing Crypto Bot integration works after changing only the base URL and the token. Everything beyond that contract is marked extension below.

A machine-readable OpenAPI 3.1 spec of the whole API — every method, object, error name, and webhook — is published alongside these pages: crypto-pay-openapi.yaml · crypto-pay-openapi.json. Feed it to code generators, API clients, or your AI tools.

Base URL and authentication

All methods live at https://crypto.tgpaybot.com/pay/api/<methodName>.

Authenticate every request with the token in the TgCryptoPay-API-Token header (Crypto-Pay-API-Token is accepted as a compatibility alias; if both are sent, the canonical one wins). A missing, invalid, or revoked token — or a token of a deleted app — returns 401 unauthorized.

The token has the form <app_id>:<secret> and is shown once, at creation or rotation; the server stores only its hash. See Getting started as a developer.

Tokens and scopes

Two kinds of credentials authenticate identically:

  • The main token — full access to every method, and the only key that signs webhooks.
  • Restricted tokens (extension) — up to 10 live per app, created in More → Merchant API under Restricted tokens, each with a label and a subset of scopes. Revoking one is instant and doesn’t touch the others; rotating the main token doesn’t touch them either.
ScopeMethods it unlocks
(any valid token)getMe, getCurrencies, getExchangeRates
readgetBalance, getStats, getInvoices, getChecks, getTransfers, getSubscriptionPlans, getSubscriptions
invoicescreateInvoice, deleteInvoice
refundsrefundInvoice
payoutstransfer, transferBatch
checkscreateCheck, deleteCheck
subscriptionscreateSubscriptionPlan, archiveSubscriptionPlan, cancelSubscription

Scopes are additive and independent — invoices does not imply read, so a server that only creates invoices can hold a token that can’t read anything. Calling a method the token doesn’t cover returns 403 scope_required. getMe reports the current token’s scopes (null for the main token) and token_name, so you can always check what you’re holding.

Requests and responses

  • Read methods are GET with query-string parameters. Money-moving methods are POST only — parameters as a JSON body, form-urlencoded, or query params (the body wins on conflict); multipart/form-data is rejected.
  • Every response is JSON with the same envelope: success {"ok": true, "result": …}, error {"ok": false, "error": {"code": <HTTP status>, "name": "<error_name>"}}. Branch on error.name — it’s the stable machine-readable string.
  • One edge case: a type-invalid query value on a GET method (e.g. offset=abc) returns HTTP 422 with a {"detail": …} body outside the envelope. Send well-typed query values.

Amounts

  • Crypto amounts are decimal strings in whole-coin units ("10.5") — never JSON numbers. Responses also carry amount_minor (extension): the integer minor-unit value as a string, because wei-scale integers overflow a JavaScript Number.
  • Per-asset decimals come from getCurrencies — drive your amount math from there instead of hardcoding.
  • Fiat amounts have at most 2 decimal places.
  • Rates are fixed-point strings, never numbers.

Pagination

List methods (getInvoices, getChecks, getTransfers, getSubscriptions) take offset (default 0) and count (default 100, max 1000; getSubscriptions max 500) and return {"items": […]}, newest first. ID filters (invoice_ids, check_ids, transfer_ids) are comma-separated integer lists. Timestamps everywhere are ISO 8601 strings.

Idempotency: spend_id

Methods that move money take a caller-generated spend_id key (1–64 characters): required on transfer and each transferBatch item, optional on createCheck and refundInvoice (extension — use it anyway). Retrying with the same key and the same parameters replays the original result instead of moving funds twice, so a timed-out request is always safe to retry. The same key with different parameters returns 409 idempotency_conflict; a retry while the original is still executing returns 409 idempotency_in_progress.

Rate limits

Per app, shared across all its tokens; exceeding returns 429 rate_limited:

MethodLimit
createInvoice, createCheck60 per minute
refundInvoice, transfer30 per minute
transferBatch10 per minute

Read methods are not rate-limited. During platform maintenance, write methods return 503 maintenance while reads keep working.

Catalog and account methods

getMe

GET /pay/api/getMe — no parameters. Returns the app’s identity: app_id, name, payment_processing_bot_username, webhook_url, webhook_events (the extended webhook types the app opted into), and the token introspection fields scopes / token_name described above.

getBalance

GET /pay/api/getBalance — no parameters. Returns an array with one row per supported asset, even at zero: currency_code, available (spendable balance), onhold (funds locked in your outstanding checks), and amount_minor.

getCurrencies

GET /pay/api/getCurrencies — no parameters. The authoritative list of what the API supports: crypto rows (is_blockchain: true) and the fiat currencies invoices can be priced in (is_fiat: true). Each row carries code, name, decimals, and the is_stablecoin flag.

getExchangeRates

GET /pay/api/getExchangeRates — no parameters. Crypto-to-fiat quotes: source, target, rate (a fixed-point string), and is_validfalse means the whole table is served from a stale cache; treat those rates as indicative only.

getStats

GET /pay/api/getStats — optional start_at / end_at (ISO 8601; the default window is the last 24 hours). Returns volume (USD value of paid invoices in the window), conversion (paid/created, percent), unique_users_count, created_invoice_count, paid_invoice_count, and the effective window bounds. An unparsable date returns 400 invalid_date.

Errors every method can return

HTTPerror.nameWhen
401unauthorizedmissing, invalid, or revoked token
403scope_requiredthe token lacks the method’s scope
400invalid_requestunparsable or invalid parameters (POST)
429rate_limitedrate limit exceeded
503maintenanceplatform maintenance (write methods)
500internal_errorunexpected server error

Method-specific errors are listed on each method’s page.