E Eklas Buyer APITelegram Shop · REST v1
v1 · 1.0

REST API · Version 1

Buy from the shop, straight from your code.

Browse products, place orders, and get your delivered content back in a single call — paid from the same wallet balance you top up in the bot. Plain HTTPS, JSON in and out, one API key. If you can send an HTTP request, you can use this.

All systems normal Base URL https://api-esb.eklas.dev/v1 Auth Bearer key Currency USDT

Quickstart

Your first order in three calls

Grab your key from the bot (🔌 Developer API menu → Reveal key), then: check what's in stock, confirm your balance, and buy. Every request carries your key in the Authorization header.

terminal
# 1 · list what's in stock
curl https://api-esb.eklas.dev/v1/products?in_stock=true \
  -H "Authorization: Bearer tgb_live_9f2c4a…"

# 2 · check your wallet
curl https://api-esb.eklas.dev/v1/balance \
  -H "Authorization: Bearer tgb_live_9f2c4a…"

# 3 · buy product #55, two units — the codes come back in the response
curl -X POST https://api-esb.eklas.dev/v1/orders \
  -H "Authorization: Bearer tgb_live_9f2c4a…" \
  -H "Content-Type: application/json" \
  -d '{"product_id": 55, "quantity": 2, "client_order_id": "my-0001"}'
buy.py
import requests

API  = "https://api-esb.eklas.dev/v1"
AUTH = {"Authorization": "Bearer tgb_live_9f2c4a…"}

# browse
products = requests.get(f"{API}/products", params={"in_stock": True}, headers=AUTH).json()

# buy — response already contains the delivered items
order = requests.post(f"{API}/orders", headers=AUTH, json={
    "product_id": 55,
    "quantity": 2,
    "client_order_id": "my-0001",   # retry-safe, never double-charges
}).json()

print(order["data"]["status"], order["data"]["items"])
buy.mjs
const API  = "https://api-esb.eklas.dev/v1";
const auth = { Authorization: "Bearer tgb_live_9f2c4a…" };

const order = await fetch(`${API}/orders`, {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ product_id: 55, quantity: 2, client_order_id: "my-0001" }),
}).then(r => r.json());

console.log(order.data.status, order.data.items);

Authentication

Get your key

Open the bot, go to the 🔌 Developer API menu, and tap Reveal key. Your key is tied to your own account and wallet — send it as a Bearer token on every request:

header
Authorization: Bearer tgb_live_9f2c4a1e7b6d…

Your key starts with tgb_live_ and is shown to you once, so copy it somewhere safe. From that same menu you can:

  • Copy your key — tap to copy it.
  • Rotate it — get a fresh key; the old one stops working the instant the new one appears.

Want your key locked to specific server IPs, a higher rate limit, or a daily spending cap? Message support and we'll set it up.

🔒

Keep it server-side. Your key can spend your wallet balance. Never ship it in a browser, mobile app, or public repo. If it leaks, rotate immediately — the old key dies the moment the new one is issued.

Conventions

Responses & errors

Every response is JSON with a top-level success boolean. Successful calls carry a data object (plus meta for paginated lists); failures carry a structured error.

success · 200
{
  "success": true,
  "data": { /* the resource */ },
  "meta": { "page": 1, "limit": 50, "total": 128 }
}
error · 402
{
  "success": false,
  "error": {
    "code": "insufficient_balance",
    "message": "Your wallet balance is too low for this order.",
    "http_status": 402
  }
}

Always branch on error.code, not on the human message — codes are stable, messages may be reworded. The full list lives in Error codes.

Catalogue

List products

Returns the products available to you, with your account's pricing already applied. Supports search, category filter, stock filter, and pagination.

GET /v1/products 🔑 key required

Query parameters

NameTypeDescription
searchoptionalstringMatch against product name and description.
category_idoptionalintegerOnly products in this category.
typeoptionalstringcode, telegram_premium, telegram_stars, seat
in_stockoptionalbooleanHide sold-out items when true.
pagedefault 1integerPage number.
limitdefault 50integerItems per page, max 100.
200 · response
{
  "success": true,
  "data": [
    {
      "id": 55,
      "name": "Netflix 1 Month — Private",
      "description": "Full HD, private profile, 30 days.",
      "type": "code",
      "price": 4.50,
      "currency": "USDT",
      "in_stock": true,
      "stock": 20,
      "unlimited_stock": false,
      "delivery": "instant",
      "max_quantity": 10,
      "requires": [],
      "category": { "id": 3, "name": "Streaming" }
    }
  ],
  "meta": { "page": 1, "limit": 50, "total": 128 }
}
💡

delivery tells you how the product arrives: instant — the order call returns the content directly (digital codes, supplier-fulfilled, and Telegram Premium/Stars all resolve server-side before responding). manual — a human delivers it, so the order comes back pending_manual and you fetch it once ready.

Catalogue

Get a product

Full detail for a single product, including any extra fields the buyer must supply at checkout (via requires).

GET /v1/products/{id} 🔑 key required
200 · response
{
  "success": true,
  "data": {
    "id": 72,
    "name": "Telegram Premium — 3 Months",
    "type": "telegram_premium",
    "price": 11.00,
    "currency": "USDT",
    "in_stock": true,
    "unlimited_stock": true,
    "delivery": "instant",
    "max_quantity": 1,
    "requires": ["telegram_username"]     // pass in customer_payload when ordering
  }
}

Catalogue

List categories

The category tree, handy for building filters in your own UI.

GET /v1/categories 🔑 key required
200 · response
{
  "success": true,
  "data": [
    { "id": 3, "name": "Streaming", "product_count": 18 },
    { "id": 5, "name": "Telegram",  "product_count": 7 }
  ]
}

Orders

How delivery works

Placing an order charges your wallet and returns the finished product in the same response. You never deal with a "processing" state — for digital codes, supplier-backed items, and Telegram Premium/Stars, the API completes fulfillment on the server before it answers, so the delivered content is right there in items. There are just two outcomes to handle:

completed

The default. The wallet was charged and the goods are delivered — read them from items (and content) on the order. Nothing more to do.

delivery: instant

pending_manual

Only for products marked manual — a person delivers them by hand. Fetch the order later with GET /v1/orders/{id}; items fills in once staff complete it.

delivery: manual
💸

If delivery can't be completed, you aren't charged. When an instant order can't be fulfilled (e.g. the upstream ran out), it comes back as an error with code delivery_failed and your wallet is automatically refunded — the response includes your restored balance.

🔁

Idempotency. Send a unique client_order_id with every order. If a network blip makes you retry, the API returns the same order instead of charging you twice. Reuse the id to safely recover a lost response.

Orders

Place an order

Charges your wallet, fulfils the order, and returns it with the delivered items. For instant products the call blocks briefly while delivery completes, then returns completed.

POST /v1/orders 🔑 key required

Body parameters (JSON)

NameTypeDescription
product_idrequiredintegerThe product to buy.
quantitydefault 1integerUnits to buy, up to the product's max_quantity.
client_order_idrecommendedstringYour own unique id for this purchase. Makes retries safe.
customer_payloadconditionalobjectRequired when the product lists requires (e.g. {"telegram_username": "@ali"} for Premium/Stars, or {"emails": ["[email protected]"]} for seats).
request
curl -X POST https://api-esb.eklas.dev/v1/orders \
  -H "Authorization: Bearer tgb_live_9f2c4a…" \
  -H "Content-Type: application/json" \
  -d '{
        "product_id": 55,
        "quantity": 2,
        "client_order_id": "my-0001"
      }'
200 · delivered
{
  "success": true,
  "data": {
    "id": 9812,
    "status": "completed",
    "product_id": 55,
    "product_name": "Netflix 1 Month — Private",
    "quantity": 2,
    "total_price": 9.00,
    "currency": "USDT",
    "client_order_id": "my-0001",
    "items": ["NFLX-AAAA-1111", "NFLX-BBBB-2222"],
    "content": "NFLX-AAAA-1111\nNFLX-BBBB-2222",
    "balance": 33.50,
    "created_at": "2026-07-18T14:03:22Z"
  }
}
402 · not enough balance
{
  "success": false,
  "error": {
    "code": "insufficient_balance",
    "message": "Your wallet balance is too low for this order.",
    "http_status": 402,
    "details": { "balance": 3.00 }
  }
}

Orders

Get an order

Fetch a single order by its id or by your client_order_id — a receipt for instant orders, and the way to collect a pending_manual delivery once staff complete it.

GET /v1/orders/{id_or_client_order_id} 🔑 key required
200 · response
{
  "success": true,
  "data": {
    "id": 9813,
    "status": "completed",
    "product_name": "Telegram Premium — 3 Months",
    "quantity": 1,
    "total_price": 11.00,
    "currency": "USDT",
    "items": ["Activated for @ali_dev"],
    "content": "Activated for @ali_dev",
    "created_at": "2026-07-18T14:05:10Z"
  }
}

Orders

List your orders

Your order history, newest first, with optional status filtering and pagination.

GET /v1/orders 🔑 key required

Query parameters

NameTypeDescription
statusoptionalstringFilter: completed, pending_manual, failed, cancelled.
pagedefault 1integerPage number.
limitdefault 20integerItems per page, max 100.
200 · response
{
  "success": true,
  "data": [
    { "id": 9813, "status": "completed", "product_name": "Telegram Premium — 3 Months", "total_price": 11.00, "created_at": "2026-07-18T14:05:10Z" },
    { "id": 9812, "status": "completed", "product_name": "Netflix 1 Month — Private",   "total_price": 9.00,  "created_at": "2026-07-18T14:03:22Z" }
  ],
  "meta": { "page": 1, "limit": 20, "total": 42 }
}

Account

Your account

Profile and wallet snapshot for the key holder.

GET /v1/me 🔑 key required
200 · response
{
  "success": true,
  "data": {
    "user_id": 6210034,
    "username": "eklas",
    "balance": 33.50,
    "currency": "USDT",
    "total_spent": 210.00
  }
}

Account

Check balance

A lightweight call for when you only need the wallet figure — e.g. a pre-flight check before a big order.

GET /v1/balance 🔑 key required
200 · response
{
  "success": true,
  "data": { "balance": 33.50, "currency": "USDT" }
}

Account

Ping

Unauthenticated health check — use it to confirm the API is reachable before wiring up auth.

GET /v1/ping 🌐 no auth
200 · response
{ "success": true, "data": { "service": "eklas-buyer-api", "version": "1.0" } }

Reference

Error codes

HTTP status plus a stable error.code. Branch on the code.

200
ok
Success.
400
bad_request
Malformed JSON or query.
401
missing_key · invalid_key
Auth missing or wrong.
402
insufficient_balance
Wallet too low for the order.
403
banned · api_paused · purchases_disabled · forbidden_product · ip_not_allowed
Access denied for this key.
404
not_found
Product or order doesn't exist.
409
out_of_stock
Requested quantity unavailable.
422
validation_error
Missing customer_payload field or qty over limit.
429
rate_limited
Slow down, or an idempotent order is still in flight.
502
delivery_failed
Couldn't deliver — wallet auto-refunded.
503
api_disabled · upstream_unavailable
API off, or supplier unreachable.

Reference

Rate limits

Your key has a request budget, split into two buckets:

BucketLimitApplies to
read120 / minAll GET endpoints.
write40 / minPlacing orders (POST).

If you go over, you get a 429 with a Retry-After header telling you how many seconds to wait — pause for that long, then retry. Need higher limits? Just ask support.

Good to know

Keep your key safe

🔑 Treat it like a password

Your key can spend your wallet balance. Keep it on your server — never in a browser, mobile app, or public repo.

🔁 Rotate if it leaks

Grab a fresh key from the bot's Developer API menu. The old one stops working immediately.

🔒 Always use HTTPS

Call the API over https://. Plain-HTTP requests are redirected.

🧾 Retries can't double-charge

Send a unique client_order_id on each order and a retry safely returns the same order.

🌐 Lock it to your IPs

Ask support to restrict your key to your server's IP addresses for extra safety.

💵 Set a spending cap

Want a daily limit on how much the key can spend? Support can add one for you.

FAQ

Common questions

How do I get a key?

Open the bot → 🔌 Developer APIReveal key. It's yours alone.

How do I add funds?

Top up your wallet inside the bot (Deposit). The API spends that same balance.

What currency are prices in?

USDT — shown on every product and order as currency.

What if delivery fails?

You're refunded automatically and the call returns delivery_failed — you're never charged for something you didn't get.

Is it safe to retry an order?

Yes. Reuse the same client_order_id and you'll get the same order back, never a second charge.

Do orders arrive instantly?

Digital products come back in the response. Hand-delivered items show pending_manual — fetch the order again once it's ready.