Skip to main content

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)

withdrawPortals and withdrawTon are kept as aliases of withdrawGram.


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)

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

faportalsmp-2.1.0.tar.gz (34.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

faportalsmp-2.1.0-py3-none-any.whl (29.7 kB view details)

Uploaded Python 3

File details

Details for the file faportalsmp-2.1.0.tar.gz.

File metadata

  • Download URL: faportalsmp-2.1.0.tar.gz
  • Upload date:
  • Size: 34.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for faportalsmp-2.1.0.tar.gz
Algorithm Hash digest
SHA256 ba27aee684a1a713617135065c2a2c0e9ca668edbcfd93714d0912154b4640bd
MD5 c3e2b91961617216a921a56ab019218e
BLAKE2b-256 c1c3b47312028d4c48c11d985534491fc3b1bf01aae8057caaea79a3294eaae9

See more details on using hashes here.

File details

Details for the file faportalsmp-2.1.0-py3-none-any.whl.

File metadata

  • Download URL: faportalsmp-2.1.0-py3-none-any.whl
  • Upload date:
  • Size: 29.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for faportalsmp-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 108cb5a1b77da3e99a28c306626a6bb08980444a29d88fd00997cac4b375243b
MD5 813ec702a9af54b3aec4c3acd308f3b4
BLAKE2b-256 1cc78b259e0e3ccab69bc0bf832ba6c9d68e39e5af8237fd606eb0fb6c03da20

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page