faportalsmp · documentation

faportalsmp

An asynchronous Python library for the Portals Gift Marketplace — a maintained fork of aportalsmp. Trade Telegram gifts, place offers, run giveaways, read your account, and deposit & withdraw GRAM.

$pip install faportalsmp

import aportalsmp as portals

What this fork adds over aportalsmp

Same library, brought in line with the current marketplace and extended. Nothing to relearn — existing code keeps working.

Areaaportalsmpfaportalsmp
WalletWithdraw onlyGRAM deposit (intent + auto-send) & withdraw, status, limits, history
Gift listStaticRegenerated from the live API + generator script; swapped IDs fixed
Name lookupExact / casedCase-, apostrophe- and hyphen-insensitive
Python3.10+ in practiceRuns on 3.8+

Installing

Requires Python 3.8+. Everything — including GRAM deposit auto-send — is included in the single install.

pip install faportalsmp

Then import the package as aportalsmp:

import aportalsmp as portals

Limits

Practical limits enforced by the API and the library:

  • search / lists — up to 100 items per request; paginate with offset (increment by limit).
  • offer expirationexpiration_days must be 7 or 0 (no expiration).
  • offer price — edits require a value >= 0.5 GRAM.
  • wallet — deposit/withdraw minimums, maximums and the daily limit are returned by walletLimits().

Authentication

Every call takes an authData string — a Telegram Mini App initData prefixed with tma . Generate it from a user session with update_auth().

async def update_auth(api_id, api_hash, session_string, session_path, session_name)→ str

Opens the Portals Mini App as your Telegram user and returns the tma … authData string. Provide either api_id + api_hash, or an existing session_string.

from aportalsmp import update_auth

authData = await update_auth(api_id=12345, api_hash="abc...", session_name="account")
# or
authData = await update_auth(session_string="BQ...")
🔑
Pass the returned tma … string as authData= to every function below.

Working with gifts

Read floors and listings, then buy, list, re-price, transfer or withdraw gifts.

async def buy(nft_id, price, authData)→ dict

Buy a listing at price (GRAM). Returns the purchase result JSON (purchased / failed with reasons).

if gifts:
    result = await portals.buy(nft_id=gifts[0].id, price=gifts[0].price, authData=authData)
async def sale(nft_id, price, authData) · bulkList(nfts, authData)→ SaleResult

List a single gift (sale) or many at once (bulkList, a list of {"nft_id", "price"}) for sale in GRAM.

Your gifts & inventory

See everything you own, then unlist, quick-sell, transfer or withdraw it to Telegram.

# view — full inventory (everything you own)
mine = await portals.inventory(offset=0, limit=50, authData=authData)
for g in mine:
    print(g.name, g.model, g.status)

# or only your marketplace listings
listed = await portals.myPortalsGifts(listed=True, authData=authData)

# take a listing off sale
await portals.unlist(nft_id=mine[0].id, authData=authData)

# instant-sell into the best collection offer
plan = await portals.quickSalePreview(nft_ids=[mine[0].id], authData=authData)
await portals.quickSale(nft_ids=[mine[0].id], authData=authData)

# withdraw the gift out of Portals (to your Telegram account)
await portals.withdrawGifts(nft_ids=[mine[0].id], authData=authData)

# or transfer it to another user inside Portals
await portals.transferGifts(nft_ids=[mine[0].id], username="someone", authData=authData)
FunctionPurpose
inventory(offset, limit, authData)Every gift you own (listed or not)
inventoryCollections(authData)Your inventory grouped by collection, with counts
inventoryValue(authData)Estimated total value of your inventory (GRAM)
myPortalsGifts(offset, limit, listed, authData)Your gifts filtered by marketplace listing status
unlist(nft_id, authData) · bulkUnlist(nft_ids, authData)Remove gift(s) from sale
quickSalePreview / quickSale(nft_ids, authData)Preview & execute an instant sale into the top offer
checkAvailability(nft_ids, authData)Check gifts are still available (e.g. before buying)
withdrawGifts(nft_ids, authData)Withdraw gifts out of Portals (to Telegram)
transferGifts(nft_ids, username, anonymous, authData)Transfer gifts to another user inside Portals

The rest of the gifts module:

FunctionReturnsPurpose
giftsFloors(authData)GiftsFloorsFloor price of every collection (short names)
filterFloors(gift_name, authData)FiltersModel / backdrop / symbol floors for a collection
collections(limit, authData)CollectionsCollections with floor, supply, volume
marketActivity(...)list[Activity]Recent buys / listings / offers / price updates
myPortalsGifts(offset, limit, listed, authData)list[PortalsGift]Gifts you own
myActivity(offset, limit, authData)list[MyActivity]Your activity
changePrice(nft_id, price, authData)NoneRe-price a listing
transferGifts(nft_ids, username, anonymous, authData)NoneTransfer gifts to a user
withdrawGifts(nft_ids, authData)NoneWithdraw gifts out of the marketplace
getGiveaways / giveawayInfo / joinGiveawayGiveaway…Browse and join giveaways
🧠
Gift names resolve leniently — "plush pepe", "Durov's Cap" and "Durov’s Cap" all match. Refresh the collection list any time with python scripts/update_collections.py.

Working with offers

Per-gift offers and collection-wide offers.

# single-gift offers
await portals.makeOffer(nft_id="...", offer_price=10, expiration_days=7, authData=authData)
await portals.editOffer(offer_id="...", new_price=12, authData=authData)
await portals.cancelOffer(offer_id="...", authData=authData)

# collection-wide offers
await portals.collectionOffer(gift_name="Plush Pepe", amount=100, max_nfts=3, authData=authData)
top = await portals.topOffer(gift_name="Plush Pepe", authData=authData)
editCollectionOffercancelCollectionOfferallCollectionOffers myCollectionOffersmyReceivedOffersmyPlacedOffers

Working with your account

Points, stats and balances. Balances are denominated in GRAM.

FunctionReturnsFields
myPoints(authData)Pointstotal_points, purchase/sell/referral/bonus points & counts
myStats(authData)Statstotal_bought, total_sold, total_volume
myBalances(authData)Balancesbalance, frozen_funds (GRAM)

Wallet · GRAM

💎
GRAM is the native coin, moved on the TON network. Amounts and balances are in GRAM; wallet addresses, TON Connect and the on-chain transfer are TON-network.

Deposit GRAM

A deposit is a native GRAM transfer to the Portals deposit wallet carrying a text comment equal to the deposit id — that is how the marketplace credits your balance.

post async def deposit(amount, mnemonic, address, wallet_version, authData)→ DepositInfo

Intent — pass only authData to mint a deposit and get where/how to send GRAM. Auto-send — also pass a mnemonic (+ amount) and the library signs & broadcasts the transfer via pytoniq.

ParameterDescription
amount floatGRAM to deposit (required for auto-send)
mnemonic str | list24-word wallet mnemonic; enables auto-send. Used only to sign, never transmitted
wallet_version strv4r2 (default), v3r2, v3r1, v5r1
address strOverride the destination (normally from the API)
# intent — send GRAM yourself
info = await portals.deposit(authData=authData)
info.address   # where to send GRAM
info.comment   # text comment to attach (the deposit id)

# auto-send — library signs & broadcasts
info = await portals.deposit(
    amount=0.5, mnemonic="word1 ... word24", wallet_version="v4r2", authData=authData,
)
info.sent_tx   # {'status': 'sent', 'amount': 0.5, 'to': ..., 'comment': ...}
get async def depositStatus(ids, authData)→ list[DepositStatus]

Poll one or more deposits by id. Omit ids for recent deposits.

Withdraw GRAM

post async def withdrawGram(amount, wallet, authData)→ str

Withdraw GRAM to an external TON address. Returns the withdrawal id.

wid = await portals.withdrawGram(amount=1.0, wallet="UQ...address", authData=authData)
statuses = await portals.withdrawStatus(ids=wid, authData=authData)

# withdrawPortals and withdrawTon are aliases of withdrawGram

get async def walletLimits(authData) · walletHistory(offset, limit, authData) · withdrawStatus(ids, authData)

walletLimits → min/max amounts, daily limit & fees. walletHistory → deposits, withdrawals and trades. withdrawStatus → poll withdrawals by id.

Advanced trading

Granular floors, market config, portfolio history and premarket orders. These return raw API dicts.

FunctionPurpose
backdropFloors(gift_name, authData)Per-backdrop floor prices for a collection
modelBackgroundFloors(gift_name, authData)Per-model / per-background floor prices
attributeFloors(payload, authData)Floors for specific attribute combinations
marketConfig(authData)Marketplace config (fees, feature flags)
inventoryCostHistory(authData)Historical value of your inventory over time

Premarket orders

Collateral-based premarket orders. The POST bodies are marketplace-defined — pass them as payload.

FunctionPurpose
premarketCollaterals(authData)Available collateral options
premarketCollections(authData)Collections that support premarket
premarketPreviewGift(payload, authData)Preview adding a gift to a premarket order
premarketAddGift(payload, authData)Add a gift to a premarket order

Referrals & cashback

# referrals
info    = await portals.referralInfo(authData=authData)
friends = await portals.referralFriends(offset=0, limit=20, authData=authData)
await portals.applyReferral(code="FRIEND_CODE", authData=authData)
await portals.claimReferral(authData=authData)

# cashback
cb = await portals.cashbackInfo(authData=authData)
await portals.claimCashback(authData=authData)
referralInforeferralFriendsreferralLevels applyReferralclaimReferralcashbackInfo cashbackLevelsclaimCashback

Objects

Responses are wrapped in lightweight objects with typed properties and a .toDict() escape hatch to the raw API dict.

PortalsGiftDepositInfoDepositStatusWithdrawStatus WalletLimitsWalletHistoryBalancesPoints StatsFiltersGiftsFloorsCollections CollectionItemActivityMyActivitySaleResult CollectionOfferGiftOfferGiveawayGiveawayRequirements
gift = (await portals.search(gift_name="Plush Pepe", limit=1, authData=authData))[0]
gift.name, gift.model, gift.backdrop, gift.symbol, gift.price
gift.toDict()   # raw API dict

Exceptions

All errors subclass Exception and are importable from aportalsmp.

authDataErrorrequestErrorconnectionErroraccountError tradingErrorofferErrorfloorsErrorgiftsError
from aportalsmp import requestError

try:
    await portals.buy(nft_id="...", price=1.0, authData=authData)
except requestError as e:
    print("API error:", e)

Contacts & donations

Source & issues: github.com/thebrainair/faportalsmp.

⚠️
Unofficial library, not affiliated with Portals. Trading and GRAM transfers move real assets — test with small amounts and keep your mnemonics safe.