faportalsmp
Asynchronous Python library for the Portals Gift Marketplace API.
A maintained fork of aportalsmp — trade Telegram gifts (NFTs), place and manage
offers, run giveaways, read your account, and deposit & withdraw GRAM.
pip install faportalsmp
Imported as
aportalsmp:import aportalsmp as portals
Quick start
import asyncio
import aportalsmp as portals
AUTH = "tma query_id=...&user=...&auth_date=...&hash=..." # see Authentication
async def main():
floors = await portals.giftsFloors(authData=AUTH)
print("Plush Pepe floor:", floors.floor("plushpepe"))
listings = await portals.search(gift_name="Plush Pepe", sort="price_asc", limit=5, authData=AUTH)
for gift in listings:
print(gift.name, gift.model, gift.price)
balances = await portals.myBalances(authData=AUTH)
print("Balance:", balances.balance, "GRAM")
asyncio.run(main())
Authentication
Every request needs authData — a Telegram Mini App initData string prefixed
with tma . Generate it from a Telegram user session with update_auth():
from aportalsmp import update_auth
# with api_id / api_hash (creates a .session file)
authData = await update_auth(api_id=12345, api_hash="abc...", session_name="account")
# or from an existing Pyrogram/Kurigram session string
authData = await update_auth(session_string="BQ...")
Wallet — GRAM deposits & withdrawals
GRAM is the native coin (moved on the TON network). Amounts and balances are in GRAM; addresses and the on-chain transfer use the TON network.
Balance, limits & history
bal = await portals.myBalances(authData=AUTH) # .balance, .frozen_funds
limits = await portals.walletLimits(authData=AUTH) # .min_deposit, .min_withdraw, .max_withdraw, .daily_limit
hist = await portals.walletHistory(offset=0, limit=20, authData=AUTH)
Deposit GRAM
A deposit is a native GRAM transfer to the Portals deposit wallet carrying a text comment equal to the deposit id — that's how the marketplace credits your balance.
Intent — send it yourself (from any wallet/bot):
info = await portals.deposit(authData=AUTH)
print(info.address) # where to send GRAM
print(info.comment) # text comment to attach (the deposit id)
print(info.amount) # expected amount, if provided
Auto-send — the library signs & broadcasts for you (needs a wallet mnemonic):
info = await portals.deposit(
amount=0.5,
mnemonic="word1 word2 ... word24", # used only to sign, never transmitted
wallet_version="v4r2", # v4r2 (default), v3r2, v3r1, v5r1
authData=AUTH,
)
print(info.sent_tx) # {'status': 'sent', 'amount': 0.5, 'to': ..., 'comment': ...}
statuses = await portals.depositStatus(ids=info.id, authData=AUTH)
Withdraw GRAM
withdrawal_id = await portals.withdrawGram(amount=1.0, wallet="UQ...external_address", authData=AUTH)
statuses = await portals.withdrawStatus(ids=withdrawal_id, authData=AUTH)
withdrawPortalsandwithdrawTonare kept as aliases ofwithdrawGram.
Gifts
| Function | Purpose |
|---|---|
giftsFloors(authData) |
Floor prices for every collection (by short name) |
filterFloors(gift_name, authData) |
Model / backdrop / symbol floors for a collection |
collections(limit, authData) |
Collections with floor, supply, volume |
search(...) |
Search listings with filters & sorting |
marketActivity(...) |
Recent buys / listings / offers / price updates |
myPortalsGifts(offset, limit, listed, authData) |
Your owned gifts |
myActivity(offset, limit, authData) |
Your activity |
buy(nft_id, price, authData) |
Buy a listing |
sale(nft_id, price, authData) / bulkList(nfts, authData) |
List gift(s) for sale |
changePrice(nft_id, price, authData) |
Re-price a listing |
transferGifts(nft_ids, username, anonymous, authData) |
Transfer gifts to a user |
withdrawGifts(nft_ids, authData) |
Withdraw gifts out of the marketplace |
getGiveaways / giveawayInfo / joinGiveaway |
Giveaways |
gifts = await portals.search(
gift_name="Plush Pepe",
model="Sad Cat",
backdrop=["Onyx Black", "Midnight Blue"],
sort="price_asc", min_price=0, max_price=5000, limit=20,
authData=AUTH,
)
if gifts:
result = await portals.buy(nft_id=gifts[0].id, price=gifts[0].price, authData=AUTH)
Gift names resolve leniently — "plush pepe", "Durov's Cap" and "Durov’s Cap" all work.
Your gifts & inventory
See everything you own, then unlist, quick-sell, transfer or withdraw it.
# full inventory — everything you own (listed or not)
mine = await portals.inventory(offset=0, limit=50, authData=AUTH)
for g in mine:
print(g.name, g.model, g.status)
groups = await portals.inventoryCollections(authData=AUTH) # grouped by collection, with counts
value = await portals.inventoryValue(authData=AUTH) # estimated inventory value (GRAM)
# only your marketplace listings
listed = await portals.myPortalsGifts(listed=True, authData=AUTH)
# take a gift off sale
await portals.unlist(nft_id=mine[0].id, authData=AUTH) # or bulkUnlist(nft_ids=[...])
# instant-sell into the best collection offer
await portals.quickSalePreview(nft_ids=[mine[0].id], authData=AUTH) # preview payout
await portals.quickSale(nft_ids=[mine[0].id], authData=AUTH)
# withdraw the gift out of Portals (to your Telegram account)
await portals.withdrawGifts(nft_ids=[mine[0].id], authData=AUTH)
# or transfer it to another Portals user
await portals.transferGifts(nft_ids=[mine[0].id], username="someone", authData=AUTH)
# check gifts are still available before buying
await portals.checkAvailability(nft_ids=[some_id], authData=AUTH)
| Function | Purpose |
|---|---|
inventory(offset, limit, authData) |
Every gift you own (listed or not) |
inventoryCollections(authData) |
Inventory grouped by collection, with counts |
inventoryValue(authData) |
Estimated total inventory value (GRAM) |
myPortalsGifts(offset, limit, listed, authData) |
Your gifts by marketplace listing status |
unlist(nft_id, authData) / bulkUnlist(nft_ids, authData) |
Remove gift(s) from sale |
quickSalePreview / quickSale(nft_ids, authData) |
Preview & instant-sell into the top offer |
checkAvailability(nft_ids, authData) |
Check gifts are still available |
withdrawGifts(nft_ids, authData) |
Withdraw gifts out of Portals (to Telegram) |
transferGifts(nft_ids, username, anonymous, authData) |
Transfer gifts to another user |
Offers
await portals.makeOffer(nft_id="...", offer_price=10, expiration_days=7, authData=AUTH)
await portals.editOffer(offer_id="...", new_price=12, authData=AUTH)
await portals.cancelOffer(offer_id="...", authData=AUTH)
await portals.collectionOffer(gift_name="Plush Pepe", amount=100, max_nfts=3, authData=AUTH)
top = await portals.topOffer(gift_name="Plush Pepe", authData=AUTH)
received = await portals.myReceivedOffers(authData=AUTH)
placed = await portals.myPlacedOffers(authData=AUTH)
Advanced trading
Granular floors, market config, portfolio history and premarket orders (these return raw API dicts):
await portals.backdropFloors(gift_name="Plush Pepe", authData=AUTH)
await portals.modelBackgroundFloors(gift_name="Plush Pepe", authData=AUTH)
await portals.attributeFloors(payload={...}, authData=AUTH)
await portals.marketConfig(authData=AUTH)
await portals.inventoryCostHistory(authData=AUTH)
# premarket (collateral-based; POST bodies are marketplace-defined)
await portals.premarketCollaterals(authData=AUTH)
await portals.premarketCollections(authData=AUTH)
await portals.premarketPreviewGift(payload={...}, authData=AUTH)
await portals.premarketAddGift(payload={...}, authData=AUTH)
Referrals & cashback
# referrals
await portals.referralInfo(authData=AUTH)
await portals.referralFriends(offset=0, limit=20, authData=AUTH)
await portals.referralLevels(authData=AUTH)
await portals.applyReferral(code="FRIEND_CODE", authData=AUTH)
await portals.claimReferral(authData=AUTH)
# cashback
await portals.cashbackInfo(authData=AUTH)
await portals.cashbackLevels(authData=AUTH)
await portals.claimCashback(authData=AUTH)
Keeping the gift list fresh
Collection IDs live in aportalsmp/utils/collections_ids.py and are generated
from the public /collections endpoint. When new gifts drop, regenerate them:
python scripts/update_collections.py
Data objects
Every response is wrapped in a lightweight object with typed properties and a
.toDict() escape hatch:
PortalsGift, CollectionOffer, GiftOffer, Points, Stats, Balances,
DepositInfo, DepositStatus, WithdrawStatus, WalletLimits,
WalletHistory, Filters, GiftsFloors, Collections, Activity,
MyActivity, SaleResult, Giveaway, GiveawayRequirements, …
Errors
All errors subclass Exception and live in aportalsmp:
authDataError, offerError, accountError, requestError,
connectionError, floorsError, giftsError, tradingError.
from aportalsmp import requestError
try:
await portals.buy(nft_id="...", price=1.0, authData=AUTH)
except requestError as e:
print("API error:", e)
Disclaimer
Unofficial library — not affiliated with Portals. Trading and GRAM transfers move real assets; test with small amounts first and keep your mnemonics safe. Use at your own risk.
Metadata
Release files for faportalsmp 2.2.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 | |
|---|---|---|---|
| faportalsmp-2.2.0.tar.gz | 36.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| faportalsmp-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.7 kB
Release files / faportalsmp-2.2.0.tar.gz
| Download URL | faportalsmp-2.2.0.tar.gz |
|---|---|
| Size | 36.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f58c7e6cdf7c8493d6139fb7d73bb37bd542ccaa3bbc37e7c416e0faad4623d6
|
|
BLAKE2b-256 checksum How to use checksums |
bfd8dc9f161c1cb9acd74d0b544df3c82dc335d32b77a6b073b5c6dff797d3c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / faportalsmp-2.2.0-py3-none-any.whl
| Download URL | faportalsmp-2.2.0-py3-none-any.whl |
|---|---|
| Size | 33.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c7e512bccbc52f41ae1026c23d88797f6bce54b7579ee437c3ae24095aa308a9
|
|
BLAKE2b-256 checksum How to use checksums |
aacd82e721635ed379eb6d1c5a9e218c251e1c421672b61f82d66594ba15167c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|