Skip to main content

near-kit

CI codecov PyPI version Python 3.11+ License: MIT

A Pythonic SDK for NEAR Protocol. Feels like requests.

import near

near.view("counter.near", "get_count")          # one line, no setup
near.balance("alice.near")                       # -> Amount('42.5 NEAR')

Human-readable everywhere — "10 NEAR" and "30 Tgas", never 10000000000000000000000000. Typed errors, exact integer math, sync and async, post-quantum keys, and the full nearcore 2.13 action set (global contracts, gas keys, deterministic accounts). The Python sibling of near-kit for TypeScript.

Install

uv add near-kit        # or: pip install near-kit

Python 3.11+.

The rule of the API

Reads are stateless; if it signs, you need a client.

Module functions cover one-off reads, requests.get()-style. A Near client holds the stateful things: your signer, a nonce cache, and a connection pool.

from near import Near

# Explicit config...
client = Near(network="testnet",
              account_id="alice.testnet",
              private_key="ed25519:...")

# ...or zero-argument discovery: NEAR_NETWORK / NEAR_ACCOUNT_ID /
# NEAR_PRIVATE_KEY env vars, falling back to ~/.near-credentials.
client = Near()

# ...or straight from NEAR CLI credentials:
client = Near.from_file("alice.testnet", network="testnet")

Reads

client.view("counter.testnet", "get_count")
client.balance()                      # your balance -> Amount
client.balance("bob.testnet")         # anyone's
client.account("bob.testnet")         # AccountView (balance, storage, code hash)
client.account_exists("bob.testnet")
client.access_keys("bob.testnet")
client.transaction_status(tx_hash)
client.rpc("block", {"finality": "final"})   # escape hatch: any RPC method

Writes

client.send("bob.testnet", "5 NEAR")
client.call("counter.testnet", "increment", {"by": 2},
            deposit="0.1 NEAR", gas="30 Tgas")

Multi-action transactions are just data — a list of actions, one receiver, executed atomically:

from near import create_account, transfer, deploy_contract, function_call, add_full_access_key

client.send_transaction(
    "sub.alice.testnet",
    actions=[
        create_account(),
        transfer("5 NEAR"),
        add_full_access_key(key.public_key),
        deploy_contract(wasm_bytes),
        function_call("init", {"owner": "alice.testnet"}),
    ],
)

Tokens

NEP-141 fungible tokens are first-class: amounts are human strings in the token's own decimals, and register=True batches the receiver's storage_deposit into the same atomic transaction when they aren't registered yet.

client.ft_transfer("usdt.tether-token.near", "bob.near", "5.25 USDT", register=True)

balance = client.ft_balance("usdt.tether-token.near")   # TokenAmount, exact int
print(balance)                                          # 5.25 USDT
balance.display                                         # Decimal('5.25')

NFTs (NEP-171) get the same treatment: nft_transfer attaches the required 1 yoctoNEAR, nft_token / nft_tokens_for_owner cover lookups.

Async

AsyncNear is the same surface, awaited — for FastAPI services, relayers, and MCP servers:

from near import AsyncNear

async with AsyncNear() as client:
    await client.call("counter.testnet", "increment")
    results = await asyncio.gather(*(client.send(acc, "0.1 NEAR") for acc in accounts))

Concurrent transactions from one account just work — the client manages nonces and retries collisions automatically.

Amounts are exact

Amount is an int subclass denominated in yoctoNEAR, so arithmetic and comparisons are exact; it parses and prints human strings. Bare numbers are rejected at API boundaries — unit confusion is a bug that should not compile.

from near import Amount

bal = client.balance()          # Amount(5250000000000000000000000)
str(bal)                        # '5.25 NEAR'
bal.as_near                     # Decimal('5.25')
bal > Amount("1 NEAR")          # True
client.send("bob.near", 5)      # UnitParseError: write "5 NEAR" or "5 yocto"

Typed errors

from near import ContractPanicError, AccountNotFoundError

try:
    client.call("app.near", "buy", deposit="1 NEAR")
except ContractPanicError as e:
    print(e.panic)     # the contract's panic message
    print(e.logs)      # contract logs leading up to it

Every error derives from NearError with a stable .code and a .retryable flag.

Testing your app: the sandbox is a database

Run NEAR like you'd run postgres in CI:

docker run -d -p 3030:3030 nearprotocol/sandbox:2.13.4

The sandbox root account's key is deterministic (derived from the image's test seed), so nothing needs to be copied out of the container:

from near import Near
from near.testing import sandbox_signer

near = Near(rpc_url="http://localhost:3030", signer=sandbox_signer())
near.send("anything.sandbox", ...)   # root account, fully funded

GitHub Actions:

services:
  near-sandbox:
    image: nearprotocol/sandbox:2.13.4
    ports: ["3030:3030"]

Or let pytest do all of it — installing near-kit registers a plugin. Its near_sandbox fixture reuses NEAR_SANDBOX_URL or localhost:3030 when reachable, otherwise starts (and later removes) a Docker sandbox on a free port; sandbox_near is a Near client signing as the root account:

from near import add_full_access_key, create_account, generate_key, transfer
from near.testing import fast_forward


def test_my_app(sandbox_near):
    key = generate_key()
    sandbox_near.send_transaction(  # root creates + funds a fresh account
        "alice.sandbox",
        actions=[create_account(), transfer("10 NEAR"), add_full_access_key(key.public_key)],
    )
    fast_forward(sandbox_near, 100)  # time travel: 100 blocks, no waiting

Meta-transactions (NEP-366)

User signs, relayer pays gas:

# User side — no gas spent:
signed = client.sign_delegate("app.near", actions=[function_call("claim", {})])
payload = encode_signed_delegate(signed)          # base64, POST it to your relayer

# Relayer side:
relayer.send_delegate(payload)

nearcore 2.13's DelegateV2 (NEP-611, the gas-key flavour of delegate actions) is deliberately not modeled while the feature is reworked upstream.

Global contracts

Publish WASM once; any account then deploys it by reference instead of paying to store its own copy:

from near import publish_contract, use_global_contract

# Publisher: identified by your account (re-publish to update every user)
# or by code hash (immutable).
client.send_transaction(me, actions=[publish_contract(wasm_bytes)])
client.send_transaction(me, actions=[publish_contract(wasm_bytes, identified_by="hash")])

# Anyone: run the published code on their own account.
client.send_transaction("app.alice.near",
                        actions=[use_global_contract(account_id="publisher.near")])

client.global_contract(account_id="publisher.near")   # ContractCode(code=..., hash=...)
client.global_contract_exists(code_hash="5FzD...")
client.contract_code("app.alice.near")                # the code an account runs

Gas keys

A gas key is an access key with its own prepaid balance for gas and several independent nonce lanes, so one key can keep many transactions in flight (nearcore 2.13):

from near import add_gas_key, transfer_to_gas_key, withdraw_from_gas_key

client.send_transaction(me, actions=[add_gas_key(gas_pk, num_nonces=8),
                                     transfer_to_gas_key(gas_pk, "5 NEAR")])

# Sign with the gas key and pick a lane; gas comes out of the key's balance,
# deposits still come out of the account.
as_gas_key = client.with_signer(KeyPairSigner(me, gas_key))
as_gas_key.send_transaction("app.near", [function_call("ping")], gas_key_index=3)

client.gas_key_nonces(me, gas_pk)                 # one nonce per lane
client.access_key(me, gas_pk).gas_key_balance     # what is left for gas
client.send_transaction(me, actions=[withdraw_from_gas_key(gas_pk, "1 NEAR")])

Any key can also ask for strict_nonce=True, which makes the node accept only "current nonce + 1" — for pipelines that must stay strictly sequential. Concurrent strict sends on one key (or lane) take turns inside the client, so all of them land; ordinary sends keep overlapping freely.

Deterministic accounts (NEP-616)

An account whose ID is a function of its initial state — a global contract plus seed storage — so the address is known before it exists and anyone can bring it to life:

from near import deterministic_state_init, derive_deterministic_account_id

action = deterministic_state_init(account_id="publisher.near",
                                  data={b"owner": b"alice.near"}, deposit="1 NEAR")
account_id = derive_deterministic_account_id(action.state_init)   # "0s" + 40 hex chars
client.send_transaction(account_id, actions=[action])

The seed map is encoded canonically (keys sorted bytewise, as nearcore's BTreeMap) and the node re-derives the ID, so a mismatch is rejected instead of creating a stray account. nearcore master's UniversalStateInit (unreleased) is not modeled yet.

Message signing (NEP-413)

Off-chain auth ("login with NEAR"):

signed = client.sign_message("Login to MyApp", recipient="myapp.com")

# Server side:
from near import verify_message
verify_message(signed, "Login to MyApp", "myapp.com", nonce)

Post-quantum keys

ML-DSA-65 (FIPS 204) keys work everywhere ed25519 keys do:

from near import MlDsa65KeyPair

pq = MlDsa65KeyPair.generate()
client.send_transaction(account_id, actions=[add_full_access_key(pq.public_key)])

Custom signers (KMS, HSM)

Anything with account_id, public_key, and sign(message) -> bytes is a signer:

class KmsSigner:
    account_id = "treasury.near"
    public_key = PublicKey.parse("ed25519:...")

    def sign(self, message: bytes) -> bytes:
        return kms.sign(key_id=..., message=message)

client = Near(network="mainnet", signer=KmsSigner())

What's under the hood

Borsh serialization via pyborsh (Pydantic-native, byte-verified against Rust), ed25519/ML-DSA via pyca/cryptography, NEP-616 Keccak-256 via pycryptodome, HTTP via httpx. Every transaction byte this library produces is verified end-to-end against a real nearcore node in CI.

License

MIT

Metadata

Release files for near-kit 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for near-kit 1.3.0
File Size Uploaded
near_kit-1.3.0.tar.gz 332.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for near-kit 1.3.0
File Interpreter ABI Platform
near_kit-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 389.9 kB

Release files / near_kit-1.3.0.tar.gz

Download URL near_kit-1.3.0.tar.gz
Size 332.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6f2756bbf39c5c04a5cb0b744b3c6574f41350463a5fc90eaa00f8ad4ab679db
BLAKE2b-256 checksum
How to use checksums
8bb87ab902cb2424140471f53bfc6102357a8507345853d381f868477a4aedd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / near_kit-1.3.0-py3-none-any.whl

Download URL near_kit-1.3.0-py3-none-any.whl
Size 57.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b44d0fa46abef5ea9e48d6e1c18e2689336e9fde88f407dbef08332fc34ae8e3
BLAKE2b-256 checksum
How to use checksums
52b52d48699b1214e13ffd5abe3d8baba66b1a5474ac590126e36bea432a1089
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.3.0 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page