diff --git a/docs.json b/docs.json index f067a5821..9f8340eb9 100644 --- a/docs.json +++ b/docs.json @@ -89,7 +89,8 @@ "pages": [ "ecosystem/api/toncenter/v2/overview", "ecosystem/api/toncenter/v2-authentication", - "ecosystem/api/toncenter/v2-errors" + "ecosystem/api/toncenter/v2-errors", + "ecosystem/api/toncenter/v2-pagination" ], "openapi": { "source": "ecosystem/api/toncenter/v2.json", diff --git a/ecosystem/api/toncenter/v2-pagination.mdx b/ecosystem/api/toncenter/v2-pagination.mdx new file mode 100644 index 000000000..2e5c478ad --- /dev/null +++ b/ecosystem/api/toncenter/v2-pagination.mdx @@ -0,0 +1,685 @@ +--- +title: "Pagination" +sidebarTitle: "Pagination" +--- + +import { Aside } from '/snippets/aside.jsx'; + +The v2 API uses cursor-based pagination to retrieve large result sets in batches. Instead of specifying a page number, the identifier of the last record received is passed to the API, which returns the next batch starting from that point. This ensures consistent results even when new data is being added. + +Three endpoints support pagination: + +| Endpoint | Cursor parameters | End of results signal | +| -------------------------------------------------------------------------------------------------------- | ------------------------- | ------------------------------------------- | +| [`getTransactions`](/ecosystem/api/toncenter/v2/accounts/list-account-transactions) | `lt` + `hash` | Response returns fewer results than `limit` | +| [`getBlockTransactions`](/ecosystem/api/toncenter/v2/blocks/list-block-transactions) | `after_lt` + `after_hash` | `incomplete` field is `false` | +| [`getBlockTransactionsExt`](/ecosystem/api/toncenter/v2/blocks/list-block-transactions-extended-details) | `after_lt` + `after_hash` | `incomplete` field is `false` | + +All other v2 endpoints return single objects or fixed results and do not support pagination. + + + +## getTransactions + +Returns the transaction history of a given account. Results are ordered by logical time in descending order (newest first). + +Pagination uses `lt` and `hash` as a cursor pair. Both must be provided together. These values come from the last transaction in the previous response. + +### Pagination parameters + +All pagination parameters are optional. They are omitted for the first request and included on subsequent pages. + +| Parameter | Type | Description | +| --------- | ------- | ------------------------------------------------------------------------------ | +| `lt` | integer | Logical time of the transaction to start from. Must be sent with `hash`. | +| `hash` | string | Hash of the transaction to start from (base64 or hex). Must be sent with `lt`. | +| `to_lt` | integer | Logical time to stop at (returns transactions from `lt` down to `to_lt`). | +| `limit` | integer | Maximum number of transactions to return. | + + + +### Paginating through transactions + + + + Send a request with only `address` and `limit`. No cursor parameters are needed for the first page. + + ```bash + curl "https://toncenter.com/api/v2/getTransactions?address=EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2&limit=5" + ``` + + Response (abbreviated): + + ```json + { + "ok": true, + "result": [ + { + "transaction_id": { "lt": "66822814000007", "hash": "CRBa2CODN76LJVnhlJQj4YK72XyKZr1wxoE3geVvjzM=" }, + "utime": 1770840795 + }, + { + "transaction_id": { "lt": "66822814000003", "hash": "xG97UIir0GIv8rUtbhjgwzYKjZCNY/tepayx0wpba6Y=" }, + "utime": 1770840795 + }, + { + "transaction_id": { "lt": "66807630000007", "hash": "UgUsErhclkJ+6rfeYl64aBGlAnLBfzQs+0hKtsquFA0=" }, + "utime": 1770804654 + }, + { + "transaction_id": { "lt": "66807630000003", "hash": "TKpxufc9Fq4UG9zFA0EvsiMTM/CAEb/Mbh6WsinnMnQ=" }, + "utime": 1770804654 + }, + { + "transaction_id": { "lt": "66784070000010", "hash": "jYTMpoesWRQ9gs9sbiYcRu+raNOU1Jd3bqCC9V/ntFU=" }, + "utime": 1770748952 + } + ] + } + ``` + + + + Take the `lt` and `hash` from the last transaction in the response. In this case: + + - `lt`: `66784070000010` + - `hash`: `jYTMpoesWRQ9gs9sbiYcRu+raNOU1Jd3bqCC9V/ntFU=` + + + + Pass the extracted `lt` and `hash` as query parameters to get the next batch. + + ```bash + curl "https://toncenter.com/api/v2/getTransactions?address=EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2&limit=5<=66784070000010&hash=jYTMpoesWRQ9gs9sbiYcRu%2BraNOU1Jd3bqCC9V%2FntFU%3D" + ``` + + Response (abbreviated): + + ```json + { + "ok": true, + "result": [ + { + "transaction_id": { "lt": "66784070000010", "hash": "jYTMpoesWRQ9gs9sbiYcRu+raNOU1Jd3bqCC9V/ntFU=" }, + "utime": 1770748952 + }, + { + "transaction_id": { "lt": "66776372000007", "hash": "rurBAd24NwH8DzD3TzEW6LpXyNh5RPw7S65288Wi/Lg=" }, + "utime": 1770730669 + }, + { + "transaction_id": { "lt": "66776372000003", "hash": "FadeYZd6F5jZbnl+dVocps+ylGqf3IXoL2B3WC6B+dM=" }, + "utime": 1770730669 + }, + { + "transaction_id": { "lt": "66773132000007", "hash": "GgHyA2ob2m06MyeA0qFHUB8J4s3xN882ETlmngV5//0=" }, + "utime": 1770722937 + }, + { + "transaction_id": { "lt": "66773132000003", "hash": "QGFGNpXQJnB6YTEsTztMAShGiwkah7bL5i6n/O+EhDY=" }, + "utime": 1770722937 + } + ] + } + ``` + + + + + + Continue extracting the cursor from the last transaction and fetching the next page. When the response returns fewer transactions than the `limit`, all results have been retrieved. + + + + + ```javascript + const address = "EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2"; + + async function main() { + let allTransactions = []; + let lt = undefined; + let hash = undefined; + let page = 0; + + while (true) { + page++; + const params = new URLSearchParams({ address, limit: "5" }); + if (lt && hash) { + params.set("lt", lt); + params.set("hash", hash); + } + + const res = await fetch( + `https://toncenter.com/api/v2/getTransactions?${params}` + ); + const { result } = await res.json(); + + console.log( + lt + ? `\n--- Page ${page} (cursor: lt=${lt}) ---` + : `\n--- Page ${page} (no cursor) ---` + ); + + for (let i = 0; i < result.length; i++) { + const tx = result[i]; + if (i === 0 && lt && hash) { + console.log( + ` [skipped] lt: ${tx.transaction_id.lt} hash: ${tx.transaction_id.hash} (duplicate)` + ); + } else { + console.log( + ` lt: ${tx.transaction_id.lt} hash: ${tx.transaction_id.hash}` + ); + } + } + + if (lt && hash) { + result.shift(); + } + + allTransactions.push(...result); + + if (result.length < 5) break; + + const last = result[result.length - 1]; + lt = last.transaction_id.lt; + hash = last.transaction_id.hash; + } + + console.log(`\nTotal unique transactions: ${allTransactions.length}`); + } + + main(); + ``` + + Output: + + ``` + --- Page 1 (no cursor) --- + lt: 67064337000004 hash: KkpVTX9RwiZcug8KQuOFUF8+eNoxuHVuIFpGQqufCWU= + lt: 67011337000003 hash: b5fhFby+j8gg936W+XEsAEhboQW0zPcOHOHgyqXkTwI= + lt: 66986300000006 hash: lT/wWTiJIdEF8A2Rox9CRdQRzlUgnIDGeUfEHQ8jGZQ= + lt: 66927779000007 hash: 07QaeBRRA62+RJPgetgSraYLH5i9G5QhC2dUvKAsAiI= + lt: 66927779000003 hash: 8d7sSor1NqbGNdX1JEGl1d4rX3lb7CeC3DZjhe7V7z4= + + --- Page 2 (cursor: lt=66927779000003) --- + [skipped] lt: 66927779000003 hash: 8d7sSor1NqbGNdX1JEGl1d4rX3lb7CeC3DZjhe7V7z4= (duplicate) + lt: 66926353000007 hash: szM1I5/MU1uJGgeScS7uxNF6V/FsLkokCpul88ZEau8= + lt: 66905979000007 hash: UpvmmLyIskTyb+ht0AER1lI5kKhVclz8Gr8k1qTtBUs= + lt: 66905979000003 hash: RBXO0z9nKYhpdzxszf3zv/7K9ec3uMI7wF/wAsKnu08= + lt: 66905973000007 hash: PD6rHCJB1O+CVAoT7zAf2W4Ux5NHD++BstZR3Qcq2w0= + + Total unique transactions: 9 + ``` + + +### Fetching a specific range + +Use `to_lt` to stop pagination at a specific logical time instead of iterating through the entire history: + +```bash +curl "https://toncenter.com/api/v2/getTransactions?address=EQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2&limit=20<=66822814000007&to_lt=66784070000010" +``` + +This returns transactions between the two logical times, which is useful when syncing only recent activity. + +## getBlockTransactions + +Returns a list of short transaction identifiers for all transactions in a given block. + +Pagination uses `after_lt` and `after_hash` as a cursor pair. Both must be provided together. The response includes an `incomplete` field that indicates whether more transactions remain in the block. + +### Pagination parameters + +All pagination parameters are optional. They are omitted for the first request and included on subsequent pages. + +| Parameter | Type | Description | +| ------------ | ------- | ---------------------------------------------------------------------------- | +| `after_lt` | integer | Return transactions after this logical time. Must be sent with `after_hash`. | +| `after_hash` | string | Return transactions after this hash. Must be sent with `after_lt`. | +| `count` | integer | Maximum number of transactions to return per request. | + + + +### Paginating through block transactions + + + + Send a request with the block identifiers (`workchain`, `shard`, `seqno`) and `count`. No cursor parameters are needed for the first page. + + ```bash + curl "https://toncenter.com/api/v2/getBlockTransactions?workchain=-1&shard=-9223372036854775808&seqno=57442406&count=3" + ``` + + Response: + + ```json + { + "ok": true, + "result": { + "@type": "blocks.transactions", + "id": { + "@type": "ton.blockIdExt", + "workchain": -1, + "shard": "-9223372036854775808", + "seqno": 57442406, + "root_hash": "pHQ5p6kF4hrkedOr5btZ4RvoTSES5X0Sfi5nBY88UOM=", + "file_hash": "A7rLPEIXoyZXiJLsxS+wgJgFicQg05vcircegwE5YC8=" + }, + "req_count": 3, + "incomplete": true, + "transactions": [ + { + "@type": "blocks.shortTxId", + "account": "-1:3333333333333333333333333333333333333333333333333333333333333333", + "lt": "67098924000001", + "hash": "4HIgukW2DrYLGAm8fviPr83e/pNuY3Ys4kLwt+93248=" + }, + { + "@type": "blocks.shortTxId", + "account": "-1:3333333333333333333333333333333333333333333333333333333333333333", + "lt": "67098924000002", + "hash": "dveSZ4S5kWt+gi32AEdDW5X5ie3U36gQW+Gls1ZjOVg=" + }, + { + "@type": "blocks.shortTxId", + "account": "-1:3333333333333333333333333333333333333333333333333333333333333333", + "lt": "67098924000005", + "hash": "hYMkXXLa8SUI7BumkwtWnwge+nH9KB5B/0ICBqdyaKQ=" + } + ] + } + } + ``` + + The `incomplete` field is `true`, meaning there are more transactions in this block. + + + + Take the `lt` and `hash` from the last transaction in the response. In this case: + + - `lt`: `67098924000005` + - `hash`: `hYMkXXLa8SUI7BumkwtWnwge+nH9KB5B/0ICBqdyaKQ=` + + + + Pass the extracted values as `after_lt` and `after_hash` to get the next batch. + + ```bash + curl "https://toncenter.com/api/v2/getBlockTransactions?workchain=-1&shard=-9223372036854775808&seqno=57442406&count=3&after_lt=67098924000005&after_hash=hYMkXXLa8SUI7BumkwtWnwge%2BnH9KB5B%2F0ICBqdyaKQ%3D" + ``` + + Response: + + ```json + { + "ok": true, + "result": { + "@type": "blocks.transactions", + "id": { + "@type": "ton.blockIdExt", + "workchain": -1, + "shard": "-9223372036854775808", + "seqno": 57442406, + "root_hash": "pHQ5p6kF4hrkedOr5btZ4RvoTSES5X0Sfi5nBY88UOM=", + "file_hash": "A7rLPEIXoyZXiJLsxS+wgJgFicQg05vcircegwE5YC8=" + }, + "req_count": 3, + "incomplete": false, + "transactions": [ + { + "@type": "blocks.shortTxId", + "account": "-1:feecb1590c59de3f75d2d1a81b95227aa929b943c19e404fbc8eea50dd2bd8f0", + "lt": "67098924000001", + "hash": "3ZnfwA3+/49AJDJrRzs4TgfgnCWkhpFSUL6IZ+MsdGM=" + } + ] + } + } + ``` + + The `incomplete` field is now `false`, meaning all transactions in this block have been retrieved. Total: 3 + 1 = 4 transactions. + + + + + + + ```javascript + async function main() { + const workchain = -1; + const shard = "-9223372036854775808"; + const seqno = 57442406; + + console.log(`Block: workchain=${workchain} shard=${shard} seqno=${seqno}\n`); + + let allTransactions = []; + let afterLt = undefined; + let afterHash = undefined; + let page = 0; + + while (true) { + page++; + const params = new URLSearchParams({ + workchain: String(workchain), + shard: shard, + seqno: String(seqno), + count: "3", + }); + if (afterLt && afterHash) { + params.set("after_lt", afterLt); + params.set("after_hash", afterHash); + } + + const res = await fetch( + `https://toncenter.com/api/v2/getBlockTransactions?${params}` + ); + const { result } = await res.json(); + const txs = result.transactions || []; + + console.log( + afterLt + ? `--- Page ${page} (cursor: after_lt=${afterLt}) ---` + : `--- Page ${page} (no cursor) ---` + ); + + for (const tx of txs) { + console.log( + ` account: ${tx.account} lt: ${tx.lt} hash: ${tx.hash}` + ); + } + + allTransactions.push(...txs); + + if (!result.incomplete) break; + + const last = txs[txs.length - 1]; + afterLt = last.lt; + afterHash = last.hash; + } + + console.log(`\nTotal transactions in block: ${allTransactions.length}`); + } + + main(); + ``` + + Output: + + ``` + Block: workchain=-1 shard=-9223372036854775808 seqno=57442406 + + --- Page 1 (no cursor) --- + account: -1:3333333333333333333333333333333333333333333333333333333333333333 lt: 67098924000001 hash: 4HIgukW2DrYLGAm8fviPr83e/pNuY3Ys4kLwt+93248= + account: -1:3333333333333333333333333333333333333333333333333333333333333333 lt: 67098924000002 hash: dveSZ4S5kWt+gi32AEdDW5X5ie3U36gQW+Gls1ZjOVg= + account: -1:3333333333333333333333333333333333333333333333333333333333333333 lt: 67098924000005 hash: hYMkXXLa8SUI7BumkwtWnwge+nH9KB5B/0ICBqdyaKQ= + + --- Page 2 (cursor: after_lt=67098924000005) --- + account: -1:feecb1590c59de3f75d2d1a81b95227aa929b943c19e404fbc8eea50dd2bd8f0 lt: 67098924000001 hash: 3ZnfwA3+/49AJDJrRzs4TgfgnCWkhpFSUL6IZ+MsdGM= + + Total transactions in block: 4 + ``` + + +## getBlockTransactionsExt + +Returns full transaction details for all transactions in a given block. Pagination works the same way as [`getBlockTransactions`](#getblocktransactions), using `after_lt` and `after_hash` as cursors with the `incomplete` field as the end of results signal. The only difference is that this endpoint returns complete transaction objects instead of short identifiers. + +### Pagination parameters + +All pagination parameters are optional. They are omitted for the first request and included on subsequent pages. + +| Parameter | Type | Description | +| ------------ | ------- | ---------------------------------------------------------------------------- | +| `after_lt` | integer | Return transactions after this logical time. Must be sent with `after_hash`. | +| `after_hash` | string | Return transactions after this hash. Must be sent with `after_lt`. | +| `count` | integer | Maximum number of transactions to return per request. | + + + +### Paginating through extended block transactions + + + + ```bash + curl "https://toncenter.com/api/v2/getBlockTransactionsExt?workchain=-1&shard=-9223372036854775808&seqno=57442406&count=3" + ``` + + Response (abbreviated): + + ```json + { + "ok": true, + "result": { + "@type": "blocks.transactionsExt", + "id": { + "@type": "ton.blockIdExt", + "workchain": -1, + "shard": "-9223372036854775808", + "seqno": 57442406, + "root_hash": "pHQ5p6kF4hrkedOr5btZ4RvoTSES5X0Sfi5nBY88UOM=", + "file_hash": "A7rLPEIXoyZXiJLsxS+wgJgFicQg05vcircegwE5YC8=" + }, + "req_count": 3, + "incomplete": true, + "transactions": [ + { + "@type": "raw.transactionExt", + "address": { "account_address": "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF" }, + "utime": 1771491922, + "transaction_id": { "lt": "67098924000001", "hash": "4HIgukW2DrYLGAm8fviPr83e/pNuY3Ys4kLwt+93248=" }, + "fee": "0", + "in_msg": null, + "out_msgs": [] + }, + { + "@type": "raw.transactionExt", + "address": { "account_address": "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF" }, + "utime": 1771491922, + "transaction_id": { "lt": "67098924000002", "hash": "dveSZ4S5kWt+gi32AEdDW5X5ie3U36gQW+Gls1ZjOVg=" }, + "fee": "0", + "in_msg": { "source": "Ef8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAU", "value": "3094985186" }, + "out_msgs": [] + }, + { + "@type": "raw.transactionExt", + "address": { "account_address": "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF" }, + "utime": 1771491922, + "transaction_id": { "lt": "67098924000005", "hash": "hYMkXXLa8SUI7BumkwtWnwge+nH9KB5B/0ICBqdyaKQ=" }, + "fee": "0", + "in_msg": { "source": "Ef9bgW4Vqf9_dfCfj5JzEcWVcJLG0wFUyn_PRoYJDzw1i4In", "value": "821183560232225" }, + "out_msgs": [{ "destination": "Ef9bgW4Vqf9_dfCfj5JzEcWVcJLG0wFUyn_PRoYJDzw1i4In", "value": "1000000000" }] + } + ] + } + } + ``` + + + + Take the `lt` and `hash` from the last transaction's `transaction_id`. In this case: + + - `lt`: `67098924000005` + - `hash`: `hYMkXXLa8SUI7BumkwtWnwge+nH9KB5B/0ICBqdyaKQ=` + + + + ```bash + curl "https://toncenter.com/api/v2/getBlockTransactionsExt?workchain=-1&shard=-9223372036854775808&seqno=57442406&count=3&after_lt=67098924000005&after_hash=hYMkXXLa8SUI7BumkwtWnwge%2BnH9KB5B%2F0ICBqdyaKQ%3D" + ``` + + Response (abbreviated): + + ```json + { + "ok": true, + "result": { + "@type": "blocks.transactionsExt", + "id": { + "@type": "ton.blockIdExt", + "workchain": -1, + "shard": "-9223372036854775808", + "seqno": 57442406, + "root_hash": "pHQ5p6kF4hrkedOr5btZ4RvoTSES5X0Sfi5nBY88UOM=", + "file_hash": "A7rLPEIXoyZXiJLsxS+wgJgFicQg05vcircegwE5YC8=" + }, + "req_count": 3, + "incomplete": false, + "transactions": [ + { + "@type": "raw.transactionExt", + "address": { "account_address": "Ef_-7LFZDFneP3XS0agblSJ6qSm5Q8GeQE-8jupQ3SvY8Bh7" }, + "utime": 1771491922, + "transaction_id": { "lt": "67098924000001", "hash": "3ZnfwA3+/49AJDJrRzs4TgfgnCWkhpFSUL6IZ+MsdGM=" }, + "fee": "81240283", + "in_msg": { "source": "", "value": "0" }, + "out_msgs": [{ "destination": "Ef9bgW4Vqf9_dfCfj5JzEcWVcJLG0wFUyn_PRoYJDzw1i4In", "value": "1300000000" }] + } + ] + } + } + ``` + + The `incomplete` field is `false`. All transactions in this block have been retrieved. + + + + + + + ```javascript + async function main() { + const workchain = -1; + const shard = "-9223372036854775808"; + const seqno = 57442406; + + console.log(`Block: workchain=${workchain} shard=${shard} seqno=${seqno}\n`); + + let allTransactions = []; + let afterLt = undefined; + let afterHash = undefined; + let page = 0; + + while (true) { + page++; + const params = new URLSearchParams({ + workchain: String(workchain), + shard: shard, + seqno: String(seqno), + count: "3", + }); + if (afterLt && afterHash) { + params.set("after_lt", afterLt); + params.set("after_hash", afterHash); + } + + const res = await fetch( + `https://toncenter.com/api/v2/getBlockTransactionsExt?${params}` + ); + const { result } = await res.json(); + const txs = result.transactions || []; + + console.log( + afterLt + ? `--- Page ${page} (cursor: after_lt=${afterLt}) ---` + : `--- Page ${page} (no cursor) ---` + ); + + for (const tx of txs) { + console.log( + ` account: ${tx.address.account_address} lt: ${tx.transaction_id.lt} hash: ${tx.transaction_id.hash} fee: ${tx.fee}` + ); + } + + allTransactions.push(...txs); + + if (!result.incomplete) break; + + const last = txs[txs.length - 1]; + afterLt = last.transaction_id.lt; + afterHash = last.transaction_id.hash; + } + + console.log(`\nTotal transactions in block: ${allTransactions.length}`); + } + + main(); + ``` + + Output: + + ``` + Block: workchain=-1 shard=-9223372036854775808 seqno=57442406 + + --- Page 1 (no cursor) --- + account: Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF lt: 67098924000001 hash: 4HIgukW2DrYLGAm8fviPr83e/pNuY3Ys4kLwt+93248= fee: 0 + account: Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF lt: 67098924000002 hash: dveSZ4S5kWt+gi32AEdDW5X5ie3U36gQW+Gls1ZjOVg= fee: 0 + account: Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF lt: 67098924000005 hash: hYMkXXLa8SUI7BumkwtWnwge+nH9KB5B/0ICBqdyaKQ= fee: 0 + + --- Page 2 (cursor: after_lt=67098924000005) --- + account: Ef_-7LFZDFneP3XS0agblSJ6qSm5Q8GeQE-8jupQ3SvY8Bh7 lt: 67098924000001 hash: 3ZnfwA3+/49AJDJrRzs4TgfgnCWkhpFSUL6IZ+MsdGM= fee: 81240283 + + Total transactions in block: 4 + ``` +