> For the complete documentation index, see [llms.txt](https://docs.getblock.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.getblock.io/api-reference/polygon-grpc.md).

# Polygon gRPC

Polygon gRPC is the **binary Protocol Buffers interface** exposed by Bor v2.8.3+ for high-performance consensus-layer coordination with Heimdall v2. It is not a replacement for Bor's Ethereum JSON-RPC — the `eth_*` execution surface remains the primary developer interface for smart contracts, transactions, and account state.

What Bor gRPC provides is a **strongly typed, HTTP/2 Multiplexed transport for the specific set of calls that** Heimdall v2 makes against Bor to build checkpoints, propose milestones, verify block producers, and correlate spans. Because it uses Protocol Buffers instead of JSON-RPC, milestone proposition — the single hottest path in the Heimdall→Bor interaction — runs approximately 4.4× faster than the equivalent HTTP JSON-RPC round trip (per Polygon Labs' devnet benchmarks).

The service is defined at [`https://github.com/0xPolygon/polyproto/tree/main/bor`](https://github.com/0xPolygon/polyproto/tree/main/bor) and exposes a **single gRPC service — `bor.BorApi` — with 11 unary methods**. There are no streaming endpoints and no bidirectional calls; every method is a request/response pair. Bor v2.8.3 introduces the server implementation (disabled by default, loopback bind on activation, PR #2194); Heimdall v2 v0.9.0 introduces the client side (opt-in via `bor_grpc_flag = "true"` in `app.toml`). HTTP JSON-RPC remains the default transport for backward compatibility.

### Key Features

* **Single Service, 11 Methods**: `bor.BorApi` — no service sprawl, no versioned aliases, no deprecated legacy names. Every method has a purpose in the Heimdall↔Bor coordination path
* **Protocol Buffers Over HTTP/2**: strongly-typed schemas via the polyproto `.proto` files; binary encoding produces \~3-5× smaller payloads than JSON-RPC and enables HTTP/2 multiplexing
* **Batched Block Info in One Round Trip**: `GetBlockInfoInBatch` returns headers + total difficulty + author addresses for `[startBlockNumber, endBlockNumber]` — replacing what used to require N separate JSON-RPC calls per block with a single gRPC call
* **Native Bor System Receipts**: `BorBlockReceipt` exposes the sprint-boundary state-sync transaction receipts that Bor generates internally — a Bor-native concept with no direct Ethereum equivalent, essential for bridge indexers tracking Ethereum → Polygon state sync flow
* **Consensus-Grade Root Hashes**: `GetRootHash(startBlock, endBlock)` computes the Merkle root over a Bor block range in the exact form Heimdall uses to build checkpoint submissions to Ethereum L1
* **Milestone Voting Support**: `GetVoteOnHash` accepts a milestone ID and hash, and returns whether the local Bor node accepts the milestone proposal — the primary voting input for validator milestone acknowledgment
* **Bearer Token Authentication**: gRPC metadata carries an authorization token. Loopback deployments can leave it empty (Bor's `[grpc]` section defaults to loopback bind); public exposure requires the token to be set
* **Zero Impact on Ethereum JSON-RPC**: gRPC and JSON-RPC coexist on the same Bor node. Enabling gRPC doesn't change `eth_*` method behavior. Applications using Bor for standard EVM reads/writes need not migrate

{% hint style="info" %}
***TECHNICAL DISCLAIMER: AUTHORITATIVE BOR GRPC SPECIFICATION***

*GetBlock's Polygon Bor gRPC API reference documentation is provided exclusively for informational purposes and to optimize the developer experience. The canonical and normative specifications for the `bor.BorApi` service — including exact protobuf message schemas, field ordering, and the shared `common.H160` / `common.H256` type definitions — are maintained by:*

* ***Polygon**:* [*github.com/0xPolygon/polyproto*](https://github.com/0xPolygon/polyproto) *— the `.proto` files are the source of truth. `bor/bor.proto` defines this service; `common/common.proto` defines shared `H160` (20-byte address) and `H256` (32-byte hash) types*
* ***Bor**:* [*github.com/0xPolygon/bor*](https://github.com/0xPolygon/bor) *— the reference server implementation (see PR #2194 and the* [*`internal/cli/server/api_service.go`*](https://github.com/0xPolygon/bor/blob/master/internal/cli/server/api_service.go) *file for the Go-side implementation)*
* ***Heimdall v2**:* [*github.com/0xPolygon/heimdall-v2*](https://github.com/0xPolygon/heimdall-v2) *— the reference client, and the primary consumer for which this API was designed*
  {% endhint %}

### Network Information

| Property                 | Value                                                                         |
| ------------------------ | ----------------------------------------------------------------------------- |
| Service                  | `bor.BorApi`                                                                  |
| Proto Package Path       | `bor` (defined in `polyproto/bor/bor.proto`)                                  |
| Total Methods            | 11 (all unary — no streaming)                                                 |
| Common Types             | `common.H160` (20-byte address), `common.H256` (32-byte hash)                 |
| Protocol                 | Protocol Buffers over HTTP/2 (TLS on public endpoints)                        |
| Authentication           | Bearer token in gRPC metadata (`authorization: Bearer <TOKEN>`)               |
| Default Self-Hosted Port | 3131 (loopback bind by default in Bor v2.8.3+)                                |
| Server Version Required  | Bor v2.8.3 or newer                                                           |
| Reference Client Version | Heimdall v2 v0.9.0 or newer                                                   |
| Bor Config Section       | `[grpc]` in `config.toml` — `addr = "127.0.0.1:3131"`, `token = ""`           |
| Server Reflection        | Depends on Bor build (verify with `grpcurl list` against production endpoint) |
| Bor Chain ID (Mainnet)   | 137                                                                           |

### Base URL

Polygon Bor gRPC is exposed on GetBlock's standard TLS endpoint pattern — the same pattern used by TRON gRPC and Solana Yellowstone gRPC. Select the region closest to your consumer when creating the endpoint in the GetBlock dashboard.

{% tabs %}
{% tab title="Frankfurt, Germany" %}

```bash
https://go.getblock.io/
```

{% endtab %}

{% tab title="New York, USA" %}

```bash
https://shared.us-east-1.getblock.io/
```

{% endtab %}

{% tab title="Singapore, Singapore" %}

```bash
https://shared.ap-southeast-1.getblock.io/
```

{% endtab %}
{% endtabs %}

### Supported Networks

| Network                       | gRPC | Frankfurt | New York | Singapore |
| ----------------------------- | ---- | --------- | -------- | --------- |
| Polygon PoS Mainnet (Bor 137) | ✅    | ✅         | ✅        | ✅         |

### Quickstart

In this section, you will learn how to fetch the **latest Bor block header** — the most recently produced block on Polygon PoS — using either:

* **`grpcurl`** (CLI — the universal gRPC probe tool)
* **JavaScript** (Node.js with `@grpc/grpc-js`)
* **Python** (with `grpcio`)

`HeaderByNumber` is a good first call because it uses the same "block number or tag" convention (`"latest"`, `"earliest"`, `"pending"`, `"finalized"`, `"safe"`, or hex `"0x..."`) as standard Ethereum JSON-RPC, so its input semantics are immediately familiar. Its response validates two things at once: your gRPC connection works, and the response schema matches the `polyproto/bor/bor.proto` `Header` message.

{% tabs %}
{% tab title="grpcurl (CLI)" %}
{% stepper %}
{% step %}

#### Install grpcurl

`grpcurl` is the universal gRPC exploration tool. It's like `curl` for gRPC — no `.proto` files required if the server supports gRPC reflection; otherwise pass `.proto` files via `-proto`.

{% code overflow="wrap" %}

```bash
# macOS
brew install grpcurl

# Linux (from GitHub releases)
curl -LO https://github.com/fullstorydev/grpcurl/releases/latest/download/grpcurl_linux_x86_64.tar.gz
tar xzf grpcurl_linux_x86_64.tar.gz
sudo mv grpcurl /usr/local/bin/

# Windows (via Scoop)
scoop install grpcurl
```

{% endcode %}
{% endstep %}

{% step %}

### Vendor the polyproto files&#x20;

GetBlock's endpoint has gRPC server reflection enabled, `grpcurl list` which shows every method automatically, and you can skip this step if you prefer to clone the proto files directly:

```bash
git clone https://github.com/0xPolygon/polyproto.git
```

Then pass them to `grpcurl` via `-import-path` and `-proto`.
{% endstep %}

{% step %}

### Call `HeaderByNumber` with `"latest"`

With reflection enabled:

{% code overflow="wrap" %}

```bash
grpcurl -H 'x-access-token: <ACCESS_TOKEN>' \
        -d '{"number": "latest"}' \
        shared.ap-southeast-1.getblock.io:443\
        bor.BorApi/HeaderByNumber
```

{% endcode %}

Without reflection (using local proto files):

{% code overflow="wrap" %}

```bash
grpcurl -import-path ./polyproto \
        -proto bor/bor.proto \
        -H 'x-access-token: <ACCESS_TOKEN>' \
        -d '{"number": "latest"}' \
        shared.us-east-1.getblock.io:443\
        bor.BorApi/HeaderByNumber
```

{% endcode %}

Replace `<ACCESS-TOKEN>` with your actual GetBlock access token.
{% endstep %}

{% step %}

### Expected output

{% code title="" overflow="wrap" %}

```bash
{
  "header": {
    "number": "90383503",
    "parentHash": {
      "Hi": {
        "Hi": "8001495684790200550",
        "Lo": "12820516500619814269"
      },
      "Lo": {
        "Hi": "12820709937774159835",
        "Lo": "10590621424795036054"
      }
    },
    "time": "1784280252",
    "uncleHash": {
      "Hi": {
        "Hi": "2147176784914242938",
        "Lo": "12359484209441330202"
      },
      "Lo": {
        "Hi": "15209294876342121491",
        "Lo": "17339213695834952519"
      }
    },
    "coinbase": {
      "Hi": {}
    },
    "stateRoot": {
      "Hi": {
        "Hi": "3861539587187450034",
        "Lo": "7546199042364005968"
      },
      "Lo": {
        "Hi": "249015285448705997",
        "Lo": "16479773740607020354"
      }
    },
    "txRoot": {
      "Hi": {
        "Hi": "12980661894323543903",
        "Lo": "1577448976556285933"
      },
      "Lo": {
        "Hi": "6785559408085401529",
        "Lo": "5091134292871561698"
      }
    },
    "receiptRoot": {
      "Hi": {
        "Hi": "3131572995761028724",
        "Lo": "12231827564059625448"
      },
      "Lo": {
        "Hi": "1608246602846981995",
        "Lo": "16695883862857860181"
      }
    },
    "bloom": "fmHEfsIdurzMvT0gtg/6wWE2T+b/BamCoo2TbD50YLzGRdbN78RexMGd0vdoyyV1c+i49gz7uG/jfpiFozZ/968WpRO+bfKKKakoX5CinbIkprwIC/9MFdHjNJuYZ+uPx+6A8n8ZnC5rT+vXpcSNK+4Pu9NreF5X+hrXmEPIraGb6QtRQcx7Vee6rfYU4p+RRhuP7QbVHl9wX9lwhx2yGLKLV7DWemu0BpsutPRUsx9qb1XxOa0uSV+pcSJPr9nd4Nq393/GrsvSYc/KWcwRbWTED+PlD6eVEXe4vwB+ctBn+m/fPFgGGRU3BXMvMJHRvN1SRThgO3AUCg0n0hnrDw==",
    "difficulty": "AQ==",
    "gasLimit": "160000000",
    "gasUsed": "36671898",
    "extraData": "14MBEAiDYm9yiGdvMS4yNi4zhWxpbnV4AAAAAAAAAAD49YD47MDBgMDAwoADwQTAwQXAwMEHwQrBC8EMwQ3BDsICDsEQwRHBEsIPE8EUwRXBFsEXwRjBGcEawRvAwMDBHMEgwSHBIsESwiMkwSXBJsEnwSjBKcEqwQHBLMDBHsEvwTDBMcEywTPBNMDAwSvBOMDBOcEtwTvBO8E+wT/CPUDBQcFCwUPBRMDBPMIeR8FIwkk1wUrBS8FMwU3BTsFPwVDBUcFRwVPBVMJSVcFWwVfBWMFZwVrBW8FcwV3BXsFfwWDBYcFiwWPBZMDAwTrAwMFqwMFFwW3AwQHAwMFuwXPCZXTAwWXCOnXBcMDBesF7hAas/ABAsnoC4VUmV488IhxyqA3HCZhw8uGdCrBauiFzkd8EPpduPmuBL8FR8h5mc3E6IqK8F/QOfQ37WwP7sOk3sFOE8gE=",
    "mixDigest": {
      "Hi": {},
      "Lo": {}
    },
    "nonce": "AAAAAAAAAAA=",
    "baseFee": "OcXBxBQ="
  }
}
```

{% endcode %}

This contain `GetHeaderByNumberResponse` message containing a `Header` object with the current block number, parent hash, timestamp, gas limit, and all other standard Ethereum header fields. The `number` field will be the height of the block Bor considers `latest` at the moment of the call.
{% endstep %}
{% endstepper %}
{% endtab %}

{% tab title="JavaScript (Node.js)" %}
{% stepper %}
{% step %}

### Setup project

```bash
mkdir polygon-bor-quickstart
cd polygon-bor-quickstart
npm init --yes
```

{% endstep %}

{% step %}

### Install gRPC dependencies

```bash
npm install @grpc/grpc-js @grpc/proto-loader
```

{% endstep %}

{% step %}

### Vendor the polyproto files

{% code overflow="wrap" %}

```bash
git clone https://github.com/0xPolygon/polyproto.git protos
```

{% endcode %}

The Bor proto imports `common/common.proto` — cloning the whole repo makes sure both are present.
{% endstep %}

{% step %}

### Set ES module type

Add `"type": "module"` to your `package.json`.
{% endstep %}

{% step %}

### Add code

{% code title="index.js" overflow="wrap" %}

```javascript
import * as grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';

const PROTO_PATH = './protos/bor/bor.proto';
const ENDPOINT = 'shared.ap-southeast-1.getblock.io:443';
const ACCESS_TOKEN = '<ACCESS-TOKEN>';

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
    includeDirs: ['./protos'],
});
const proto = grpc.loadPackageDefinition(packageDefinition);
const BorApiClient = proto.bor.BorApi;

const client = new BorApiClient(ENDPOINT, grpc.credentials.createSsl());
const metadata = new grpc.Metadata();
metadata.add('x-access-token', `${ACCESS_TOKEN}`);

client.HeaderByNumber({ number: 'latest' }, metadata, (err, response) => {
    if (err) {
        console.error('gRPC error:', err.code, err.message);
        return;
    }
    console.log(JSON.stringify(response, null, 2));
});
```

{% endcode %}

Replace `<ACCESS-TOKEN>` with your actual GetBlock access token.
{% endstep %}

{% step %}

### Run the script

```bash
node index.js
```

The response contains the latest Bor block's header.

{% code title="latest Bor block's header." overflow="wrap" %}

```bash
{
  "header": {
    "number": "90383984",
    "parentHash": {
      "Hi": {
        "Hi": "3219579091769836205",
        "Lo": "3715250197314926109"
      },
      "Lo": {
        "Hi": "2674255279228011676",
        "Lo": "3095247116736800573"
      }
    },
    "time": "1784280973",
    "uncleHash": {
      "Hi": {
        "Hi": "2147176784914242938",
        "Lo": "12359484209441330202"
      },
      "Lo": {
        "Hi": "15209294876342121491",
        "Lo": "17339213695834952519"
      }
    },
    "coinbase": {
      "Hi": {
        "Hi": "0",
        "Lo": "0"
      },
      "Lo": 0
    },
    "stateRoot": {
      "Hi": {
        "Hi": "9833850037642172310",
        "Lo": "14374670924963563757"
      },
      "Lo": {
        "Hi": "15326292854892058618",
        "Lo": "3327665482045070470"
      }
    },
    "txRoot": {
      "Hi": {
        "Hi": "1622856331791778660",
        "Lo": "17858003608247780972"
      },
      "Lo": {
        "Hi": "11571743316523524888",
        "Lo": "18243495908713277801"
      }
    },
    "receiptRoot": {
      "Hi": {
        "Hi": "3315218599047333559",
        "Lo": "8266824338816502615"
      },
      "Lo": {
        "Hi": "17685214896531518585",
        "Lo": "53913688016064401"
      }
    },
    "bloom": {
      "type": "Buffer",
      "data": [
        255,
        255,
        255,
        255,
        247,
        255,
        255,
        255,
        255
      ]
    },
    "difficulty": {
      "type": "Buffer",
      "data": [
        1
      ]
    },
    "gasLimit": "160000000",
    "gasUsed": "52014949",
    "extraData": {
      "type": "Buffer",
      "data": [
       
        35,
        192,
        193,
        36,
        193,
        38,
        193,
        88,
        193,
        89,
        193,
        90,
        193,
        91,
        193,
        0,
        64,
        128,
        174,
        1
      ]
    },
    "mixDigest": {
      "Hi": {
        "Hi": "0",
        "Lo": "0"
      },
      "Lo": {
        "Hi": "0",
        "Lo": "0"
      }
    },
    "nonce": {
      "type": "Buffer",
      "data": [
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0
      ]
    },
    "baseFee": {
      "type": "Buffer",
      "data": [
        57,
        150,
        194,
        243,
        88
      ]
    },
    "withdrawalsHash": null,
    "parentBeaconBlockRoot": null,
    "requestsHash": null
  }
}
```

{% endcode %}
{% endstep %}
{% endstepper %}
{% endtab %}

{% tab title="Python" %}
{% stepper %}
{% step %}

### Setup project

```bash
mkdir polygon-bor-quickstart
cd polygon-bor-quickstart
```

{% endstep %}

{% step %}

### Create and activate a virtual environment

```bash
python3 -m venv venv
source venv/bin/activate

# On Windows, 
use venv\Scripts\activate
```

{% endstep %}

{% step %}

### Install gRPC dependencies

```bash
pip install grpcio grpcio-tools
```

{% endstep %}

{% step %}

### Vendor and compile the proto files

```bash
git clone https://github.com/0xPolygon/polyproto.git protos

python -m grpc_tools.protoc \
    -I protos \
    --python_out=. \
    --grpc_python_out=. \
    protos/bor/bor.proto \
    protos/common/common.proto
```

This generates `bor/bor_pb2.py`, `bor/bor_pb2_grpc.py`, and `common/common_pb2.py`.
{% endstep %}

{% step %}

### Create script

{% code title="main.py" %}

```python
import grpc
from google.protobuf.json_format import MessageToJson

from bor import bor_pb2, bor_pb2_grpc

ENDPOINT = "shared.ap-southeast-1.getblock.io:443"
ACCESS_TOKEN = "<ACCESS_TOKEN>"

credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel(ENDPOINT, credentials)
stub = bor_pb2_grpc.BorApiStub(channel)

metadata = [("x-access-token", f" {ACCESS_TOKEN}")]

request = bor_pb2.GetHeaderByNumberRequest(number="latest")
response = stub.HeaderByNumber(request, metadata=metadata)

print(MessageToJson(response, indent=2))
```

{% endcode %}

Replace `<ACCESS-TOKEN>` with your actual access token from GetBlock.
{% endstep %}

{% step %}

### Run the script

```bash
python main.py
```

{% endstep %}
{% endstepper %}
{% endtab %}
{% endtabs %}

### Available API Methods

`bor.BorApi` exposes 11 unary methods. Organized by function:

#### Chain State Reads (3 methods)

| Method         | Description                                                               |
| -------------- | ------------------------------------------------------------------------- |
| HeaderByNumber | Returns a block header for a given block number or tag                    |
| BlockByNumber  | Returns a block (currently header-only per the schema) for a given number |
| GetAuthor      | Returns the block producer (author) address for a given block number      |

#### Receipt Reads (2 methods)

| Method             | Description                                                                               |
| ------------------ | ----------------------------------------------------------------------------------------- |
| TransactionReceipt | Returns the receipt for a transaction hash                                                |
| BorBlockReceipt    | Returns the Bor system-level block receipt (state-sync transactions at sprint boundaries) |

#### Fork-Choice Data (2 methods)

| Method        | Description                                     |
| ------------- | ----------------------------------------------- |
| GetTdByHash   | Returns total difficulty by block hash          |
| GetTdByNumber | Returns total difficulty by block number or tag |

#### Consensus Operations (2 methods)

| Method        | Description                                                                   |
| ------------- | ----------------------------------------------------------------------------- |
| GetRootHash   | Computes the Merkle root of a Bor block range for checkpoint construction     |
| GetVoteOnHash | Returns whether the local node accepts a milestone proposal for a block range |

#### Span Correlation (2 methods)

| Method                      | Description                                                                                                          |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| GetStartBlockHeimdallSpanID | Maps a Bor start block to its corresponding Heimdall span ID                                                         |
| GetBlockInfoInBatch         | Returns headers + total difficulty + author for a block range in one call — the \~4.4× milestone proposition speedup |

### Support

For technical support and questions:

* **Support Email**: <support@getblock.io>

### See Also

* [polyproto GitHub](https://github.com/0xPolygon/polyproto) — the source of truth for `bor.BorApi` and common types
* [`bor/bor.proto`](https://github.com/0xPolygon/polyproto/blob/main/bor/bor.proto) — the `bor.BorApi` service definition
* [`common/common.proto`](https://github.com/0xPolygon/polyproto/blob/main/common/common.proto) — the `H160` and `H256` message types used throughout
* [Bor v2.8.3 Release Notes](https://github.com/0xPolygon/bor/releases/tag/v2.8.3) — server implementation (PR #2194)
* [Polygon JSON-RPC](/api-reference/polygon-matic.md)  — the standard EVM `eth_*` interface (separate from this gRPC service)
* [grpcurl Documentation](https://github.com/fullstorydev/grpcurl) — universal gRPC exploration tool used in the Quickstart


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.getblock.io/api-reference/polygon-grpc.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
