Introduction
The CoinDrop API lets you add a game top-up service to your site or Telegram bot. All requests are made over HTTPS and every response is JSON.
PropertyValue
FormatJSON (application/json)
AuthX-API-Key header
Base URLhttps://coindonate.uz/api/v1
Versionv1 (stable)
CurrencyUSD (primary), UZS shown for reference
Authentication
Every request must include your X-API-Key header.
Where to get your API key:
CoinDrop dashboard → Profile → Security & API Key → Generate key

Format: cd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
curl https://coindonate.uz/api/v1/balance \
  -H "X-API-Key: cd_your_api_key_here"
import requests

headers = {"X-API-Key": "cd_your_api_key_here"}
r = requests.get("https://coindonate.uz/api/v1/balance", headers=headers)
print(r.json())
▶ Try it live
Test every endpoint right here with your own API key. Read requests run live; order requests run in TEST mode (validated & priced, but nothing is ordered and no balance is charged). Each card shows the request in cURL, Python, PHP and Node.js.
🔑 Your API key
If you're logged in, click Load my key to auto-fill it. Otherwise paste your key (Dashboard → Profile → API Key). It stays in your browser only.
Ordering by game type
What you send in POST /orders depends on the game. The games endpoint tells you exactly which fields a game needs via id_type, amount_based, requires_server and has_validator. Use those flags to build the right form — no hardcoding.
Game typeFlagsYou sendplayer_id is…
Standard
PUBG, Standoff 2, Blood Strike
id_type: numeric product_id + player_id Numeric player ID
With server / zone
Mobile Legends, Genshin
requires_server: true product_id + player_id + server_id Numeric ID (+ Zone ID)
Telegram Stars amount_based: true
id_type: text
amount (or product_id: amt_N) + player_id Telegram @username
Telegram Premium id_type: text product_id + player_id Telegram @username
Steam amount_based: true
id_type: text
amount (USD) + player_id Steam login
Vouchers / gift cards
Roblox, Discord, Xbox…
separate endpoint product_id + quantity (no player) — (a code is returned)
1 · Standard game (PUBG)
POST /api/v1/orders
{
  "game_key": "pubg-mobile-buykos",
  "product_id": "12345",         # from /games/{key}/products
  "player_id": "52425715863"
}
2 · Game with server / zone (Mobile Legends)

When requires_server: true, add server_id (the server_label tells you what it's called, e.g. "Zone ID"). You can also verify the ID first with validate (this game has has_validator: true).

POST /api/v1/orders
{
  "game_key": "mobile-legends-global",
  "product_id": "88",
  "player_id": "123456789",
  "server_id": "2345"
}
3 · Telegram Stars (amount-based)

Two ways: a preset product_id: "amt_50", or a custom amount (any number, min 50). Send the Telegram username as player_id.

# Custom amount — 250 Stars to @durov
POST /api/v1/orders
{ "game_key": "telegram-stars", "amount": 250, "player_id": "durov" }
✓ Response
{
  "success": true,
  "order_id": 512,
  "status": "delivered",
  "game_key": "telegram-stars",
  "product_name": "250",
  "player_id": "durov",
  "amount_usd": 3.94,
  "amount_uzs": 48500,
  "message": "Delivered successfully"
}
4 · Telegram Premium

Premium uses ready packages (3 / 6 / 12 months) — pick a product_id from products, and send the Telegram username as player_id.

POST /api/v1/orders
{ "game_key": "telegram-premium", "product_id": "prem_3m", "player_id": "durov" }
5 · Steam wallet (amount-based, USD)

Steam's amount is a direct USD wallet value (min $0.30). Send the Steam login as player_id. Steam has no validator.

POST /api/v1/orders
{ "game_key": "steam", "amount": 10, "player_id": "mysteamlogin" }
6 · Voucher / gift card (returns a code)

Vouchers use their own endpoint and return the code(s) immediately — no player ID needed. See Buy a code.

POST /api/v1/promocodes/games/roblox-us/order
{ "product_id": "80", "quantity": 1 }
→ { "codes": ["ABCD-1234-EFGH"], "pin_code": "ABCD-1234-EFGH" }
Rule of thumb. Call /games once, read each game's flags, and render the form: numeric vs text ID, whether to ask for a server, and whether to offer a custom amount. The products response repeats these under meta.
Pricing model
Every product returns two USD price tiers (each also in UZS). Understanding them prevents costly mistakes.
price_usd
You pay this
Deducted from your CoinDrop balance for each order. This is your wholesale price on CoinDrop.
retail_usd
Suggested resale
price_usd + your own markup (set in your profile). What you'd charge your customers.
Your rate is fixed server-side. price_usd always reflects exactly what will be deducted from your balance — you never need to compute it yourself. Subscribe to a reseller tariff to lower your price_usd.
Rate limits
Requests are rate-limited per API key. Sending too many requests temporarily blocks the key.
ScopeLimitIf exceeded
All requests120 / minuteBlocked for 5 minutes
Orders (POST)30 / minuteBlocked for 10 minutes

When blocked, the API returns 429 with the remaining wait time. Space out your requests and cache the games/products lists on your side (they change rarely).

{
  "detail": "Rate limit exceeded (120/60s). Blocked for 300s."
}
API can be turned off. If your API access is disabled (in your profile, or by an admin), every request returns 403 — API access is disabled and no orders are accepted until it is re-enabled. Check account.api_enabled on the Account endpoint.
Webhooks
Instead of polling order status, pass a notification_url when creating an order. When the order reaches a final state, we POST a JSON payload to your URL.
FieldDescription
eventorder.updated or promocode.order
order_idYour CoinDrop order id
statusdelivered or failed
external_refThe value you sent when creating the order
POST → your notification_url
{
  "event": "order.updated",
  "order_id": 142,
  "status": "delivered",
  "game_key": "pubg-mobile",
  "product_name": "60 UC",
  "player_id": "52425715863",
  "amount_usd": 0.9208,
  "amount_uzs": 11300.0,
  "external_ref": "bot_user_42"
}

Most orders finish within ~30s and the webhook fires right away. If an order is still processing, the webhook is sent later, once it settles. Always use an https:// URL and respond with 200. Treat the webhook as advisory — confirm with Order status if needed.

Errors
CodeMeaningCause
200OKSuccess
401UnauthorizedMissing or invalid API key
403ForbiddenAPI disabled, account blocked, IP not allowed, or game unavailable
404Not FoundResource not found
422UnprocessableInsufficient balance or invalid input
429Too Many RequestsRate limit exceeded
503Service UnavailableUpstream provider temporarily down
{
  "detail": "Error description here"
}
Account & tariffs
GET /api/v1/account Profile, balance & tariffs
Requires API key

Your account overview: balance (USD + UZS), API status, discounts, and which tariffs (subscriptions) are currently active and until when.

curl https://coindonate.uz/api/v1/account \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "account": {
    "id": 6,
    "name": "My Game Shop",
    "email": "[email protected]",
    "api_enabled": true,
    "balance_usd": 39.12,
    "balance_uzs": 481200,
    "retail_markup_pct": 15.0,
    "personal_discount_pct": 0.0,
    "total_discount_pct": 3.0,
    "has_validator": true
  },
  "tariffs": [
    {
      "plan": "Reseller",
      "kind": "reseller",
      "period": "1m",
      "status": "active",
      "validator_access": false,
      "discount_pct": 3.0,
      "expires_at": "2026-08-04T10:00:00"
    }
  ],
  "usd_to_uzs": 12300.0
}
FieldMeaning
api_enabledIf false, no requests are accepted (403).
total_discount_pctTariff + personal discount off the CoinDrop margin.
has_validatorWhether player-validation is unlocked (Validator tariff).
tariffs[]Active subscriptions with their expiry.
Balance
GET /api/v1/balance Current balance
Requires API key
curl https://coindonate.uz/api/v1/balance \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "balance_usd": 39.12,
  "balance_uzs": 481200,
  "name": "My Game Shop"
}
Games
GET /api/v1/games All available games
Requires API key
curl https://coindonate.uz/api/v1/games \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "games": [
    {
      "key": "pubg-mobile",
      "name": "PUBG Mobile",
      "image_url": "https://coindonate.uz/...",
      "category": "mobile",
      "popular": true,
      "id_type": "numeric",
      "id_label": "Player ID",
      "amount_based": false,
      "requires_server": false,
      "server_label": "",
      "has_validator": false
    },
    {
      "key": "mobile-legends",
      "name": "Mobile Legends",
      "id_type": "numeric",
      "id_label": "Player ID",
      "amount_based": false,
      "requires_server": true,
      "server_label": "Server ID",
      "has_validator": true
    },
    {
      "key": "telegram-stars",
      "name": "Telegram Stars",
      "id_type": "text",
      "id_label": "Telegram username",
      "amount_based": true,
      "requires_server": false,
      "has_validator": true
    }
  ]
}
FieldTells you…
id_typenumeric (player ID) or text (Telegram username / Steam login).
id_labelWhat to show the user as the ID field label.
amount_basedIf true, products use amt_<n> ids (Stars, Steam) — the user picks a quantity.
requires_serverWhether a server_id is needed (MLBB, Genshin…).
has_validatorWhether you can call the validate endpoint for this game.

Use these fields to render the right input form per game — no hardcoding needed.

🎁 Telegram Gifts
Send standard Telegram star-gifts (💝 🧸 🎁 🌹 …) to any user. Gifts are sent from your own connected Telegram account — connect it once in your CoinDrop dashboard → Gift akkaunt (one-time $1). The connected account must have a public @username.

How billing works: your account keeps a stars pool. When a gift is ordered, if the pool has enough stars the gift is sent free (already paid). If not, CoinDrop auto-buys a stars package (minimum 50) at your price, charges your USD balance, and the leftover stays in your pool for the next gifts. You only ever pay for stars — never separately for the gift.
GET /api/v1/gifts Available gifts & prices
Requires API key + connected account
curl https://coindonate.uz/api/v1/gifts \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "gifts": [
    {
      "gift_id": 5170145012310081615,
      "emoji": "💝",
      "custom_emoji_id": "5465263910414195580",
      "stars": 15,
      "limited": false,
      "price_usd": 0.2364,
      "price_uzs": 2900.0,
      "min_purchase_stars": 50
    }
  ]
}
FieldMeaning
gift_idGift ID — pass this to /gifts/send.
custom_emoji_idPremium (custom) emoji ID — render the animated gift emoji in your bot via <tg-emoji emoji-id="…">.
starsStar cost of this gift.
price_usdValue of the gift's stars at your price. Actual charge may be 0 if your pool already has enough stars.
min_purchase_starsIf a top-up is needed, this is the minimum stars bought (≥ 50).
POST /api/v1/gifts/send Send a gift to a user
Requires API key + connected account

Sends the gift from your connected account. If your stars pool is short, CoinDrop auto-buys the needed package first and charges your balance. Use dry_run:true to preview the exact charge without sending.

curl -X POST https://coindonate.uz/api/v1/gifts/send \
  -H "X-API-Key: cd_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "gift_id": 5170145012310081615,
    "player_id": "devsardor",
    "external_ref": "your-order-id-123",
    "notification_url": "https://your-site.com/webhook"
  }'
ParamRequiredDescription
gift_idyesID from /api/v1/gifts.
player_idyesRecipient's Telegram @username (without @) or numeric ID.
anonymousnotrue = hide the sender's name (gift shows as anonymous).
commentnoText message attached to the gift (max 255 chars).
external_refnoYour own order id — echoed back in responses & webhook.
notification_urlnoWebhook URL — POSTed when the order finishes.
dry_runnotrue = validate & return the exact charge, but do not send.
✓ 200 Response
{
  "success": true,
  "order_id": 82,
  "status": "delivered",
  "game_key": "gift",
  "product_name": "🎁 Gift (25⭐)",
  "recipient": "devsardor",
  "gift_stars": 25,
  "stars_bought": 50,        # 0 = sent free from pool
  "charged_usd": 0.788,      # 0 when sent from pool
  "charged_uzs": 9600.0,
  "message": "Gift delivered"
}
🔎 dry_run preview
{
  "success": true, "dry_run": true, "status": "validated",
  "gift_stars": 25, "pool_stars": 20,
  "will_buy_stars": 50, "would_charge_usd": 0.788, "would_charge_uzs": 9600.0,
  "balance_usd": 3746.5
}
✗ Errors
400  no connected account (connect one in dashboard → Gift akkaunt)
400  connected account has no @username (required to buy stars)
404  gift not found / sold out
422  insufficient balance

Gift orders also appear in List orders with game_key: "gift".

Products
GET /api/v1/games/{game_key}/products Packages & prices
Requires API key
ParameterTypeDescription
game_keystringGame ID: pubg-mobile, free-fire, mobile-legends…
curl https://coindonate.uz/api/v1/games/pubg-mobile/products \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "products": [
    {
      "id": "12345",
      "name": "60 UC",
      "amount": null,
      "price_usd": 0.9208,
      "price_uzs": 11300,
      "retail_usd": 1.0589,
      "retail_uzs": 13000,
      "image": "/api/product-img/xxx.webp"
    }
  ],
  "meta": {
    "id_type": "numeric",
    "id_label": "Player ID",
    "amount_based": false,
    "requires_server": false,
    "server_label": "",
    "has_validator": false
  }
}

Each product carries two price tiers, both in USD and UZS: price_usd/price_uzs (you pay) and retail_usd/retail_uzs (suggested resale). See Pricing model. The meta block repeats the game-type info.

Amount-based games (Telegram Stars, Steam). When amount_based: true, product ids look like amt_50, amt_100 … and each includes an amount field. Order them the same way — just send the amt_<n> id and the username/login as player_id.
✓ Telegram Stars example
{
  "success": true,
  "products": [
    { "id": "amt_50",  "name": "50 Stars",  "amount": 50,
      "price_usd": 0.788, "price_uzs": 9700,  "retail_usd": 0.9062, "retail_uzs": 11100 },
    { "id": "amt_100", "name": "100 Stars", "amount": 100,
      "price_usd": 1.576, "price_uzs": 19400, "retail_usd": 1.812, "retail_uzs": 22300 }
  ],
  "meta": { "id_type": "text", "id_label": "Telegram username", "amount_based": true,
            "requires_server": false, "has_validator": true }
}
Validate player
POST /api/v1/games/{game_key}/validate Check player ID / nickname
Requires API key

Verify a player ID before ordering (returns the player nickname when supported). Prevents wrong-ID top-ups.

FieldTypeRequiredDescription
player_idstringPlayer / user ID
server_idstringoptionalServer / zone (MLBB, Genshin…)
curl -X POST https://coindonate.uz/api/v1/games/mobile-legends/validate \
  -H "X-API-Key: cd_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "player_id": "123456789", "server_id": "2345" }'

Requires an active Validator tariff (has_validator: true). Not available for Steam.

✓ 200 — valid ID
{
  "success": true,
  "valid": true,
  "player_id": "123456789",
  "username": "PlayerNick",
  "premium": false
}
✗ 200 — wrong ID
{
  "success": false,
  "valid": false,
  "message": "Player not found"
}
Create order
POST /api/v1/orders New order
Requires API key

When an order is placed, price_usd is deducted from your CoinDrop balance. Orders are fulfilled automatically (up to ~30s). If fulfilment fails, your balance is refunded.

FieldTypeRequiredDescription
game_keystringpubg-mobile, free-fire, mobile-legends…
product_idstringid from the products endpoint. Optional for amount-based games if you send amount.
player_idstringPlayer ID / Telegram username / Steam login
amountnumberoptionalAmount-based only (Stars/Steam): custom quantity instead of a preset. Stars min 50, Steam min $0.30.
server_idstringoptionalServer / region (MLBB, Genshin…)
player_namestringoptionalPlayer nickname
external_refstringoptionalYour internal ID (bot user ID, etc.)
notification_urlstringoptionalHTTPS webhook — POSTed when the order settles. See Webhooks.
cURL
Python
JavaScript
curl -X POST https://coindonate.uz/api/v1/orders \
  -H "X-API-Key: cd_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "game_key": "pubg-mobile",
    "product_id": "12345",
    "player_id": "52425715863",
    "external_ref": "telegram_user_999"
  }'
import requests

r = requests.post(
    "https://coindonate.uz/api/v1/orders",
    headers={"X-API-Key": "cd_your_key"},
    json={
        "game_key": "pubg-mobile",
        "product_id": "12345",
        "player_id": "52425715863",
        "external_ref": "user_999",
    },
)
data = r.json()
if data["success"]:
    print("Delivered!", data["order_id"])
else:
    print("Status:", data["message"])
const res = await fetch('https://coindonate.uz/api/v1/orders', {
  method: 'POST',
  headers: {
    'X-API-Key': 'cd_your_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    game_key: 'pubg-mobile',
    product_id: '12345',
    player_id: '52425715863',
    external_ref: 'user_999',
  }),
});
const data = await res.json();
console.log(data);
Telegram Stars / Steam (amount-based). Two ways: use a preset amt_<n> product id, or send a custom amount (any quantity — Stars min 50, Steam min $0.30). Send the Telegram username / Steam login as player_id. Telegram Premium uses normal product ids like any other game.
# Preset:
curl -X POST https://coindonate.uz/api/v1/orders \
  -H "X-API-Key: cd_your_key" -H "Content-Type: application/json" \
  -d '{ "game_key": "telegram-stars", "product_id": "amt_50", "player_id": "durov" }'

# Custom amount (e.g. 177 Stars) + webhook:
curl -X POST https://coindonate.uz/api/v1/orders \
  -H "X-API-Key: cd_your_key" -H "Content-Type: application/json" \
  -d '{
    "game_key": "telegram-stars",
    "amount": 177,
    "player_id": "durov",
    "external_ref": "bot_user_42",
    "notification_url": "https://your-server.com/webhooks/coindrop"
  }'
✓ 200 — Delivered
{
  "success": true,
  "order_id": 142,
  "status": "delivered",
  "game_key": "pubg-mobile",
  "product_name": "60 UC",
  "player_id": "52425715863",
  "amount_usd": 0.9208,
  "amount_uzs": 11300.0,
  "message": "Delivered successfully"
}
✗ 422 — Insufficient balance
{
  "detail": "Insufficient balance"
}
Order status
GET /api/v1/orders/{order_id} Single order status
Requires API key
curl https://coindonate.uz/api/v1/orders/142 \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "order": {
    "id": 142,
    "game_key": "pubg-mobile",
    "product_name": "60 UC",
    "player_id": "52425715863",
    "server_id": "",
    "amount_usd": 0.9208,
    "amount_uzs": 11300.0,
    "status": "delivered",
    "external_ref": "bot_user_42",
    "created_at": "2026-07-02T10:30:00",
    "updated_at": "2026-07-02T10:30:22"
  }
}

Status values: processing · delivered · failed

List orders
GET /api/v1/orders Recent orders
Requires API key
Query paramDefaultDescription
page1Page number
per_page50Items per page (max 100)
statusdelivered | failed | processing
date_fromFrom date (YYYY-MM-DD)
date_toTo date (YYYY-MM-DD)
curl "https://coindonate.uz/api/v1/orders?page=1&per_page=20&status=delivered&date_from=2026-07-01" \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "orders": [
    {
      "id": 142,
      "game_key": "pubg-mobile",
      "product_name": "60 UC",
      "player_id": "52425715863",
      "quantity": 1,
      "amount_usd": 0.9208,
      "amount_uzs": 11300.0,
      "status": "delivered",
      "external_ref": "bot_user_42",
      "created_at": "2026-07-02T10:30:00"
    }
  ],
  "meta": { "page": 1, "per_page": 20, "total": 137, "total_pages": 7 }
}
Voucher games
Gift-card & voucher catalog (Roblox, Discord Nitro, Xbox, iTunes, Free Fire codes…). These deliver a code instantly instead of topping up an account.
GET /api/v1/promocodes/games Voucher games list
Requires API key
curl https://coindonate.uz/api/v1/promocodes/games \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "games": [
    { "key": "roblox-us", "name": "ROBLOX US CARD" },
    { "key": "discord-nitro", "name": "Discord Nitro" },
    { "key": "xbox-usa", "name": "XBOX Giftcards (USA)" }
  ]
}
Voucher products
GET /api/v1/promocodes/games/{game}/products Prices & stock
Requires API key

Each product shows is_active (live stock). When false, it is out of stock — don't order it. Prices carry the same USD + UZS tiers as game products.

curl https://coindonate.uz/api/v1/promocodes/games/roblox-us/products \
  -H "X-API-Key: cd_your_key"
✓ 200 Response
{
  "success": true,
  "products": [
    {
      "id": "80",
      "name": "Roblox 10$ US",
      "price_usd": 9.717,
      "price_uzs": 119500,
      "retail_usd": 11.1745,
      "retail_uzs": 137400,
      "currency": "USD",
      "is_active": true,
      "stock": 178
    }
  ]
}
Buy a code
POST /api/v1/promocodes/games/{game}/order Buy voucher / gift-card code
Requires API key

Deducts price_usd × quantity from your balance and returns the delivered codes immediately. On failure your balance is refunded. Out-of-stock items return 422 before charging.

FieldTypeRequiredDescription
product_idstringid from the voucher products endpoint
quantityintegeroptionalNumber of codes (default 1, max 99)
external_refstringoptionalYour internal ID
notification_urlstringoptionalHTTPS webhook
curl -X POST https://coindonate.uz/api/v1/promocodes/games/roblox-us/order \
  -H "X-API-Key: cd_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "product_id": "80", "quantity": 2 }'
✓ 200 — Delivered
{
  "success": true,
  "order_id": 143,
  "status": "delivered",
  "product_name": "Roblox 10$ US",
  "quantity": 2,
  "unit_price_usd": 9.717,
  "amount_usd": 19.434,
  "amount_uzs": 239000,
  "codes": ["ABCD-1234-EFGH", "WXYZ-5678-IJKL"],
  "pin_code": "ABCD-1234-EFGH",
  "message": "Delivered"
}
✗ 422 — Out of stock (not charged)
{
  "detail": "This item is currently out of stock"
}