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.

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.

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.