fragment-api-dev
The focused Python SDK for buying Telegram Stars and Telegram Premium through Fragment.
- GRAM and USDT on TON
- 0% API service fee; product prices and blockchain network fees still apply
- No API key
- Sync and native async clients with explicit status polling
- Zero runtime dependencies for sync; optional HTTPX for async
- Pre-payment recipient checks without wallet credentials
- Client-signed checkout: the customer's seed stays in their wallet
Install
pip install 'fragment-api-dev==1.4.0'
# Native async support:
pip install 'fragment-api-dev[async]==1.4.0'
The only SDK extra is async = ["httpx>=0.27,<1"]. Python 3.10+ is required.
Safe local start
Use the repository's examples/mock_server.py and persistent starters before live integration. The mock needs no API backend, wallet, bot token, or payment. It accepts only a synthetic marker, never a real seed.
from fragment_api import FragmentAPI
api = FragmentAPI(base_url="http://127.0.0.1:8081")
purchase = api.buy_stars(
username="@completed", amount=50, payment_method="gram",
seed="mock-only-not-a-wallet", idempotency_key="demo-order-1",
)
print(api.wait(purchase.purchase_id).status)
Use @failed, @429, @timeout, or @reconciliation_required to exercise recovery paths. For the timeout scenario set the SDK request timeout below the mock's two-second delay.
Native async
import asyncio
from fragment_api import AsyncFragmentAPI
async def main():
async with AsyncFragmentAPI(base_url="http://127.0.0.1:8081") as api:
stars, premium = await asyncio.gather(
api.get_stars_price(50), api.get_premium_price(3),
)
print(stars.prices.gram, premium.prices.usdt)
asyncio.run(main())
Every sync API method has an async counterpart with the same arguments and typed result; await it. Network I/O uses httpx.AsyncClient, not threads or synchronous requests. Reuse one client inside one event loop. The context manager closes its own HTTP client; alternatively call await api.aclose(). An injected http_client=httpx.AsyncClient(...) remains caller-owned and must be closed by the caller.
timeout bounds each async request, including body reads. wait(..., timeout=300, poll_interval=2) and wait_checkout(...) also bound the entire polling operation, including in-flight requests. Cancellation propagates normally and does not create another purchase or retry the POST. Cancellation or timeout does not cancel a server-side purchase.
Errors share the sync hierarchy: ValidationError (400), ConflictError (409), RateLimitError (429), ServiceUnavailableError (503), and FragmentAPIError for other HTTP/transport failures. RequestTimeoutError identifies request timeouts; WaitTimeoutError identifies polling deadlines. Inspect status_code, code, action, and retry_after; numeric Retry-After headers are supported. Known request seeds and signed BOCs are redacted from error responses. Do not log request payloads or secrets.
The SDK does not retry automatically. Persist the merchant order and its explicit idempotency_key before sending a POST. After a lost response, retry only the same immutable request and key. Once purchase_id is known, poll that ID. Never create a new order to recover a timeout or reconciliation_required result. Polling stops on completed, failed, or reconciliation_required; checkout polling also stops on expired.
Client-signed checkout
These methods target /api/v1/checkouts on a server that supports the versioned checkout contract. They never take a seed or API key.
checkout = api.create_checkout(
product="stars", username="@recipient", amount=50, payment_method="gram",
payer_address=connected_wallet_address, idempotency_key=persisted_order_key,
)
# Persist checkout.checkout_id before handing anything to the wallet UI.
if checkout.tonconnect_transaction is not None:
transaction = checkout.tonconnect_transaction.to_dict()
# Pass transaction to your TonConnect wallet UI; the customer reviews/signs it.
# After the wallet returns the signed BOC, your integration calls:
submitted = api.submit_checkout(checkout.checkout_id, boc=wallet_result_boc)
latest = api.get_checkout(checkout.checkout_id)
result = api.wait_checkout(checkout.checkout_id)
Checkout mirrors the public checkout response: checkout_id, status, product, username, amount, payment_method, transaction_hash, error, created_at, updated_at, and optional tonconnect_transaction. Frozen TonConnectTransaction and TonConnectMessage preserve integer-string message amounts. to_dict() restores TonConnect's validUntil and optional stateInit field names. Status responses may omit the transaction. The SDK does not sign, submit to the chain, or implement a wallet UI.
Exact routes: POST /api/v1/checkouts, GET /api/v1/checkouts/{id}, POST /api/v1/checkouts/{id}/submitted with {"boc": "..."}. All four methods, including wait_checkout, also exist on AsyncFragmentAPI.
Optional integration attribution
FragmentAPI(integration_id="936da01f-9abd-4d9d-80c7-02af85c822a8") and AsyncFragmentAPI(integration_id=...) accept an explicit UUID. This sends X-Integration-Id for opt-in integration attribution. It is not authentication, a wallet identifier, or an API key. Omit it to send no attribution header; the SDK never generates one. Never put secrets, wallet details, or source URLs in this field.
Quick start
This example uses a 12-word wallet at account index 0. If you use a 24-word TON mnemonic or a Base64 32-byte private key, remove account_index.
import os
from fragment_api import FragmentAPI
api = FragmentAPI()
check = api.check_stars_availability("@telegram_user", 50, "gram")
if not check.available:
raise SystemExit(f"{check.message} {check.action or ''}")
# Accept customer payment only after the check succeeds.
purchase = api.buy_stars(
username="@telegram_user",
amount=50,
payment_method="gram",
seed=os.environ["TON_WALLET_SEED"],
account_index=0, # Required for a 12-word seed
)
result = api.wait(purchase.purchase_id)
if result.completed:
print("Delivered:", result.transaction_hash)
else:
print("Not completed:", result.status, result.error)
Recipient checks are explicit. The SDK submits the purchase and provides a typed Purchase result. If omitted, an idempotency key is generated per call; persistent integrations must supply their own stable key.
Live purchases require a confirmed paid merchant order and a backend-only runtime wallet secret. Never collect seeds in Telegram, browser forms, URLs, logs, or source files. Use client-signed checkout when the customer should pay from their own wallet.
Use check_premium_availability("@telegram_user", 3, "usdt") for Premium. These checks never need or accept a seed, wallet address, account index, or API key.
Run the check shortly before payment. It is not a reservation or guarantee. If the API returns 429, wait for error.retry_after; on 503, retry later and do not charge yet.
12-word wallets
A 12-word wallet can contain many TON accounts. Tell the API which account pays:
| What you know | Send | What happens |
|---|---|---|
| Seed + wallet address | seed, wallet_address |
The API finds the index |
| Seed + account index | seed, account_index |
The API uses V5R1 at that index |
| Seed + address + index | all three | The API verifies and uses that exact wallet |
A 12-word seed alone is not enough. Send wallet_address or account_index.
wallet = api.resolve_wallet(
seed=os.environ["TON_WALLET_SEED"],
wallet_address=os.environ["TON_WALLET_ADDRESS"],
)
purchase = api.buy_stars(
username="@telegram_user",
amount=50,
payment_method="gram",
seed=os.environ["TON_WALLET_SEED"],
account_index=wallet.account_index,
)
If you already know the index, skip resolve_wallet() and pass account_index=0 (or your actual index). You may also pass wallet_address="UQ..." directly. The API searches indices 0..20 during a purchase and 0..100 through resolve_wallet().
For a 24-word TON mnemonic or Base64 32-byte private key, omit both wallet selectors.
Wallet selection errors
FragmentAPIError exposes code and action so your app can show the correct next step.
| Code | Client action |
|---|---|
WALLET_SELECTION_REQUIRED |
Add wallet_address or account_index |
INVALID_BIP39_SEED |
Check all 12 words and their order |
INVALID_WALLET_ADDRESS |
Send a valid TON friendly or raw address |
INVALID_ACCOUNT_INDEX |
Send an integer from 0 to 100 |
WALLET_ADDRESS_MISMATCH |
Remove the index to search, or correct it |
WALLET_INDEX_NOT_FOUND |
Call the resolver, then use its returned index |
WALLET_RESOLVER_BUSY |
Wait briefly and retry the same resolve request |
Prices
Get the 50-Stars reference price and Premium prices for 3, 6, and 12 months before showing a price to your customer:
catalog = api.get_prices()
print(catalog.stars.amount, catalog.stars.prices.gram, catalog.stars.prices.usdt)
quote = api.get_stars_price(100)
print(quote.prices.gram, quote.prices.usdt)
Use get_premium_price(3), get_premium_price(6), or get_premium_price(12) for Premium. Price values are strings to preserve their exact decimal value. A quote can have stale=True when the latest available quote is being served.
Telegram Premium
purchase = api.buy_premium(
username="@telegram_user",
months=3,
payment_method="usdt",
seed=os.environ["TON_WALLET_SEED"],
account_index=0, # Required for a 12-word seed
)
result = api.wait(purchase.purchase_id)
Premium durations are 3, 6, or 12 months.
Payments
| Value | Payment |
|---|---|
gram |
GRAM on TON |
usdt |
USDT on TON |
USDT purchases require a small GRAM balance for the network fee.
Public API
| Method | Purpose |
|---|---|
buy_stars() |
Buy 50 or more Telegram Stars |
buy_premium() |
Buy Telegram Premium |
create_purchase() |
Create a purchase with explicit product fields |
get_purchase() |
Read the latest purchase state |
get_prices() |
Read the 50-Stars reference and Premium price catalog |
get_price() |
Read a Stars or Premium quote |
get_stars_price() |
Read a Stars quote for 50 to 1,000,000 Stars |
get_premium_price() |
Read a quote for 3, 6, or 12 months of Premium |
resolve_wallet() |
Find the account index for a 12-word wallet |
check_availability() |
Check a recipient with explicit product fields |
check_stars_availability() |
Check a Stars recipient before payment |
check_premium_availability() |
Check a Premium recipient before payment |
wait() |
Poll until the purchase reaches a terminal state |
create_checkout() |
Prepare a client-signed TonConnect checkout |
get_checkout() |
Read the latest checkout state |
submit_checkout() |
Report the signed wallet BOC |
wait_checkout() |
Poll checkout delivery or reconciliation state |
No authentication token or API key is required.
Links
Metadata
Release files for fragment-api-dev 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fragment_api_dev-1.4.0.tar.gz | 27.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fragment_api_dev-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.1 kB
Release files / fragment_api_dev-1.4.0.tar.gz
| Download URL | fragment_api_dev-1.4.0.tar.gz |
|---|---|
| Size | 27.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bd93737d1e813c2acf02651b9114cefd39cbd1d1f1d591f4441339bc80bb9349
|
|
BLAKE2b-256 checksum How to use checksums |
f7b8317461cd629f93239fde21831a4e88a6f0a90eda76f24a057a9a90fcdbb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / fragment_api_dev-1.4.0-py3-none-any.whl
| Download URL | fragment_api_dev-1.4.0-py3-none-any.whl |
|---|---|
| Size | 16.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
71a9778cbf21ace6f3a862f0a7586e0be04ab7ae0b99b687d5ff3853d5c65820
|
|
BLAKE2b-256 checksum How to use checksums |
a9ec286c346e349c9d7c747b1d8cbc69f670e5bbbdaf092ee35720b8466d383a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|