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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file faportalsmp-2.2.0.tar.gz.
File metadata
- Download URL: faportalsmp-2.2.0.tar.gz
- Upload date:
- Size: 36.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f58c7e6cdf7c8493d6139fb7d73bb37bd542ccaa3bbc37e7c416e0faad4623d6
|
|
| MD5 |
60185044a937861ae7bb16cce905743c
|
|
| BLAKE2b-256 |
bfd8dc9f161c1cb9acd74d0b544df3c82dc335d32b77a6b073b5c6dff797d3c5
|
File details
Details for the file faportalsmp-2.2.0-py3-none-any.whl.
File metadata
- Download URL: faportalsmp-2.2.0-py3-none-any.whl
- Upload date:
- Size: 33.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7e512bccbc52f41ae1026c23d88797f6bce54b7579ee437c3ae24095aa308a9
|
|
| MD5 |
bafc0acbe68ef1baf82988747b995d81
|
|
| BLAKE2b-256 |
aacd82e721635ed379eb6d1c5a9e218c251e1c421672b61f82d66594ba15167c
|